首页 / 资讯中心 / 文章详情

驾考题库数据工程:SQL与JSON双格式导入及查询优化实战

驾考题库数据工程:SQL与JSON双格式导入及查询优化实战 ★ FEATURED ARTICLE
简介这是一套面向驾考学员与驾培应用开发者的科目一、科目四题库数据资源覆盖客车、小车、摩托车、货车四类车型可满足刷题练习、题库二次开发与教学演示等场景。包内共约2000个文件以1995个webp图片素材为主用于题目配图与选项图示另含2个sql与2个json文件分别提供结构化建表数据与便于程序读取的题目数据并附1个gif动图。压缩包整体约103.05MB。题库规模具体为客车科目一2154题、科目四2126题小车科目一1600题、科目四1300题摩托车科目一446题、科目四383题货车科目一2162题、科目四1206题合计近万道题目。目前已有1014人学习下载。借助sql与json双格式读者可直接导入数据库或解析为应用数据配合图片素材快速搭建刷题系统省去逐题录入与配图整理的繁琐工作。1. 从一份驾考题库说起1542 道题背后的数据工程上周有个做驾培 SaaS 的朋友找我说他们 App 的题库模块要重构运营那边催着上线客车和摩托车的分车型练习但后端表结构还是三年前按小车单车型设计的字段里塞了一堆逗号分隔的选项字符串查询慢、统计难、图片路径还写死在代码里。他问我有没有现成的、结构干净的题库数据可以直接导入。我翻出这份驾照考试科目一科目四题库资源给他看——它把小车、客车、货车、摩托车四类车型的科目一和科目四题目同时整理成了 SQL 表数据和 JSON 格式还附带题目配图素材。客车科目一 2154 题、客车科目四 2126 题、小车科目一 1600 题、小车科目四 1300 题、摩托车科目一 446 题、摩托车科目四 383 题、货车科目一 2162 题、货车科目四 1206 题合计过万道题。这份资源适合谁做驾培 App 的后端、做题库小程序的独立开发者、需要离线刷题工具的技术人以及想拿真实数据练 SQL 和 JSON 处理的学生。它解决的核心问题不是“有没有题”而是“题目数据能不能直接进你的系统跑起来”。2. 拆开数据包SQL 与 JSON 双格式的字段设计2.1 两种格式各自解决什么问题这份资源同时提供questions.sql、chapter.sql和questions.json、chapter.json不是简单重复。SQL 文件面向关系型数据库适合直接导入 MySQL、PostgreSQL 或 SQLite让后端用 JOIN 查询章节和题目JSON 文件面向接口层和前端适合直接喂给 Node.js 服务、小程序云函数或者做静态化部署时生成题目列表。我一般会先用 SQL 建库做数据校验和统计再用 JSON 做接口返回两边字段对齐后交叉验证避免出现“数据库里 1600 题、接口只返回 1598 题”这种玄学问题。从文件命名看questions存题目主体chapter存章节或分类信息。常见做法是chapter表里放车型、科目、章节名称和排序questions表里用外键关联章节同时冗余车型和科目字段方便直接筛选。图片素材以独立文件形式存在文件名类似1542-1694068096616.gif、1544-1694068188609.webp题目表里应该有一个字段存图片文件名或相对路径而不是完整 URL——这点很关键完整 URL 换域名就全废了。2.2 建表与导入把 SQL 跑起来先看导入流程。假设你用 MySQL字符集用utf8mb4因为题目里可能有特殊符号。下面是我常用的导入脚本框架# 创建数据库字符集必须 utf8mb4否则图片文件名或题干特殊字符会乱码 mysql -u root -p -e CREATE DATABASE driving_exam DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; # 导入章节表先导 chapter 再导 questions避免外键约束报错 mysql -u root -p driving_exam chapter.sql # 导入题目表 mysql -u root -p driving_exam questions.sql # 验证各车型题目数量是否与描述一致 mysql -u root -p driving_exam -e SELECT vehicle_type, subject, COUNT(*) AS total FROM questions GROUP BY vehicle_type, subject ORDER BY vehicle_type, subject;这段脚本的逻辑很直白建库时指定utf8mb4是为了兼容 webp 文件名和题干里的生僻字先导chapter再导questions是因为题目表通常有指向章节表的外键最后那条统计查询是必须做的——资源描述里给了每个车型每个科目的题量导入后对不上就说明导入过程有截断或编码问题。参数上vehicle_type和subject这两个字段名是我按常见设计推测的实际字段名以你拿到的 SQL 文件为准导入前先用DESC questions;看一眼表结构。提示如果 SQL 文件里用的是INSERT INTO逐条插入上万条数据导入会慢可以临时把autocommit关掉导入完再开速度能快好几倍。2.3 JSON 结构解析与接口化JSON 格式更适合直接做接口。假设questions.json是一个数组每个元素是一道题结构大概是这样{ id: 1001, chapter_id: 12, vehicle_type: car, subject: 1, question: 驾驶机动车在道路上违反道路交通安全法的行为属于什么行为, options: [ {key: A, text: 违章行为}, {key: B, text: 违法行为}, {key: C, text: 过失行为}, {key: D, text: 违规行为} ], answer: B, image: 1542-1694068096616.gif, explain: 违反道路交通安全法属于违法行为。 }用 Node.js 快速起一个分页接口验证数据可用性const fs require(fs); const express require(express); const app express(); // 读取 JSON注意大文件用 readFileSync 会阻塞生产环境建议流式或预加载 const questions JSON.parse(fs.readFileSync(./questions.json, utf8)); app.get(/api/questions, (req, res) { const { vehicle_type, subject, page 1, size 20 } req.query; // 按车型和科目过滤这两个字段是分车型刷题的核心 let list questions.filter(q (!vehicle_type || q.vehicle_type vehicle_type) (!subject || q.subject Number(subject)) ); const start (page - 1) * size; res.json({ total: list.length, page: Number(page), data: list.slice(start, start Number(size)) }); }); app.listen(3000, () console.log(题库接口已启动));这段代码的关键参数是vehicle_type和subject它们决定了前端切换车型和科目时能不能拿到正确数据。page和size做分页避免一次返回上千题把小程序卡死。图片字段image只存文件名前端拼 CDN 前缀或本地静态目录这样换存储方案时不用改数据库。逻辑说明过滤在前、分页在后保证total是过滤后的总数前端分页组件才能算对页数。3. 多车型题库的查询优化与图片素材管理3.1 分车型分科目查询的索引设计过万道题按车型和科目分组后单组最多两千多题看起来不大但如果前端每次刷题都全表扫描并发一上来数据库就吃力。我一般会在questions表上建联合索引-- 车型科目章节的联合索引覆盖最常见的筛选路径 ALTER TABLE questions ADD INDEX idx_vehicle_subject_chapter (vehicle_type, subject, chapter_id); -- 如果经常按章节顺序取题再加一个排序字段的索引 ALTER TABLE questions ADD INDEX idx_chapter_sort (chapter_id, sort_order);索引字段顺序有讲究vehicle_type和subject区分度最高放前面chapter_id放后面用于章节内排序。这样前端请求“小车科目一第 3 章”时查询能直接走索引不用回表扫全量。参数上如果你的表里车型用的是中文“小车”“客车”索引一样生效但建议统一成英文枚举值避免编码问题导致索引失效。3.2 图片素材的路径映射与加载策略资源里的图片素材文件名是时间戳加随机数的形式比如1542-1694068096616.gif、1544-1694068188609.webp。这种命名方式对机器友好但对人不太友好排查问题时很难一眼看出哪张图对应哪道题。我的做法是在数据库里存文件名同时建一张映射表或直接在题目表加image_alt字段记录图片内容描述方便运营核对。加载策略上webp 格式体积小适合移动端但老版本 iOS 支持有限常见做法是准备 gif 和 webp 两套前端按浏览器能力选择。如果图片总量大不要全部打包进小程序包放 CDN 按需加载数据库里只存相对路径。下面是一个批量检查图片文件是否齐全的脚本import os import json # 读取题目 JSON收集所有引用的图片文件名 with open(questions.json, r, encodingutf-8) as f: questions json.load(f) referenced {q[image] for q in questions if q.get(image)} # 扫描素材目录找出实际存在的文件 existing set(os.listdir(./images)) missing referenced - existing unused existing - referenced print(f题目引用图片数: {len(referenced)}) print(f实际存在图片数: {len(existing)}) print(f缺失图片: {missing if missing else 无}) print(f未被引用图片: {len(unused)} 个)这段脚本解决的是资源落地时最常见的对不上问题题目里引用了某张图但素材包里没有或者素材包里有图但没有任何题目引用。referenced和existing两个集合做差集缺失的必须补未引用的可以清理。参数上./images换成你实际的素材目录路径q.get(image)用 get 避免某些题没有图片字段时报 KeyError。3.3 章节表与题目表的关联查询chapter.sql和chapter.json存的是章节结构通常包含车型、科目、章节名、章节排序。前端做“章节练习”时需要先拉章节列表再按章节拉题目。一个典型的关联查询-- 查询小车科目一的所有章节及每章题目数 SELECT c.id, c.name, c.sort_order, COUNT(q.id) AS question_count FROM chapter c LEFT JOIN questions q ON q.chapter_id c.id WHERE c.vehicle_type car AND c.subject 1 GROUP BY c.id, c.name, c.sort_order ORDER BY c.sort_order;用LEFT JOIN是为了防止某个章节下没有题目时章节本身消失COUNT(q.id)统计题目数前端可以显示“本章 120 题”。参数上vehicle_type和subject的取值要和题目表保持一致如果章节表里车型存的是中文而题目表存的是英文关联就会出问题——这是导入前必须核对的第一件事。4. 避坑与排查导入和对接时最容易翻车的五件事4.1 编码不一致导致题干乱码现象导入 SQL 后题干里的中文变成问号或乱码图片文件名也显示异常。原因数据库、表、连接三处字符集不统一常见的是数据库建成了latin1或gbk而 SQL 文件是utf8mb4。解决建库时强制DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci导入命令加--default-character-setutf8mb4导入后用SHOW VARIABLES LIKE character%;确认。4.2 题目数量对不上描述现象描述里小车科目一 1600 题导入后COUNT(*)只有 1580。原因SQL 文件可能被截断或者导入过程中遇到重复主键被INSERT IGNORE跳过也可能是题目表里有软删除标记但统计时没过滤。解决先看 SQL 文件末尾是否完整再用SELECT COUNT(*) FROM questions WHERE vehicle_typecar AND subject1;精确统计对比描述数字差得多就重新下载或检查导入日志。4.3 图片路径写死导致换环境失效现象本地开发时图片能显示部署到服务器后全部 404。原因题目表里存的是http://localhost:8080/images/xxx.gif这种完整 URL换域名或换端口就失效。解决数据库只存文件名前端或网关层拼前缀如果已经存了完整 URL用UPDATE questions SET image SUBSTRING_INDEX(image, /, -1);批量截取文件名。4.4 JSON 大文件解析内存溢出现象Node.js 服务启动时读取questions.json直接 OOM 崩溃。原因过万道题的 JSON 文件可能几十 MBreadFileSync一次性加载到内存再加上解析后的对象内存占用翻倍。解决改用流式解析库如stream-json或者启动时把 JSON 导入数据库接口从数据库查如果坚持用 JSON至少用require的缓存机制或启动时加载一次常驻内存不要每次请求都读文件。4.5 车型枚举值不统一导致筛选失效现象前端传vehicle_typecar后端返回空列表。原因数据库里存的可能是“小车”“C1”“car”多种写法JSON 和 SQL 两边的枚举值不一致。解决导入前先SELECT DISTINCT vehicle_type FROM questions;看实际值统一映射成一套枚举前端和后端共用同一份常量定义。这个坑最隐蔽因为数据本身没问题只是对不上。5. 进阶用法把题库做成可验证的刷题服务数据导入只是第一步真正要落地成刷题服务还得解决“答案校验”和“错题重练”两个问题。我一般会在题目表基础上加一张用户答题记录表用题目 ID 关联记录用户选的答案和是否正确。这样错题本、正确率统计、章节掌握度都能算出来。下面是一个错题重练的查询思路-- 查询某用户在某车型某科目下的错题按章节分组 SELECT q.id, q.question, q.answer, q.image, c.name AS chapter_name FROM user_answers ua JOIN questions q ON q.id ua.question_id JOIN chapter c ON c.id q.chapter_id WHERE ua.user_id 1001 AND ua.is_correct 0 AND q.vehicle_type car AND q.subject 1 ORDER BY c.sort_order, q.id;参数上user_id和is_correct是核心过滤条件is_correct用 0/1 存比存对错文本更省空间。如果错题量大可以加LIMIT分页或者按章节聚合只返回章节和错题数点进去再拉具体题目。验证数据完整性还有一个笨但有效的办法随机抽 20 道题人工核对题干、选项、答案和图片是否匹配。我吃过亏——有一次导入后发现某道题的答案字段串位了正确答案是 B 但数据库里存的是 C原因是 SQL 文件里选项和答案的列顺序和建表语句不一致。从那以后我每次导入新题库都会先跑一遍抽样校验脚本再跑一遍全量答案分布统计看 A/B/C/D 四个选项的答案数量是否大致均衡如果某个选项占比超过 60%大概率是数据有问题。希望这份题库资源和这些排查思路能帮你少走几个弯路。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站