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

MySQL题库SQL文件导入实战:从表结构到性能优化全解析

MySQL题库SQL文件导入实战:从表结构到性能优化全解析 ★ FEATURED ARTICLE
简介面向K12在线教育题库建设者与内容运营者这份MySQL题库资源包集中解决数学、物理、化学公式在录入、存储与网页端显示的常见难题。包内含完整数据库脚本、章节与知识点划分样本以及题目属性设置参考可帮助从业者快速搭建题库结构并理解LaTeX公式呈现逻辑。资源共583个文件以574张png示例图为主另有2份docx说明文档、2份html演示页、2份js渲染脚本、1份sql数据文件和若干辅助文件压缩包仅2.03MB轻量易用目录结构清晰便于按模块查阅。已有1106人浏览学习适合K12教育产品设计、题库运营及技术开发等初级到中级人员。对照文档中的数据结构图和MathJax渲染示例读者能直接获得从数据建模到公式显示的一体化参考方案也可将其中的章节划分、题目属性设置思路复用到自己的题库系统。1. 一个「中小学题库mysql.zip」值不值得花一个下午拆开看「中小学题库mysql.zip」这种压缩包经常出现在网盘、QQ 群和课程配套资源里下载是一回事能真正把题用起来是另一回事。我见过太多次这样的场景一位老师兴冲冲解压双击打开里面的 .sql 文件编辑器直接卡死或者在图形客户端里导入半小时后报错满屏乱码。这个 zip 的核心资产其实只有两样一份建表脚本和一份数据脚本。前者决定了你知道题库怎么组织后者决定了你能不能查出想要的题。但真正让人翻车的往往不是数据本身而是导入姿势、字符集内外不一致、以及建表脚本里那些隐性的顺序依赖。这篇文章会从表结构入手讲清楚怎么安全导入、怎么验证数据能不能用、以及常见的 5 个坑。我默认你装的是 MySQL 8.0 或 5.7标题里带 zip 也只是个载体。看懂下面的步骤你拿到任何类似题库包都能在半小时内跑起来。2. 先看表结构再谈导入这套题库的库表和字段以及在设计上的取舍很多人的习惯是双击 zip、解压、立刻导入结果导入一半报错才回头去翻建表脚本。我一般会反过来先用文本查看器把 .sql 文件的开头几百行读完把表结构弄清楚再动手。因为导入失败的大多数原因在表结构里已经写了答案。2.1 最常见的四张表主表、选项表、科目表、知识点关联网上下载的中小学题库包表结构基本逃不出下面这个套路。先看科目表再看题目主表然后是选项表和知识点关联表。科目表最简单一般只存 id 和科目名CREATE TABLE subject ( id smallint unsigned NOT NULL AUTO_INCREMENT, name varchar(50) NOT NULL COMMENT 科目名称, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT科目表;题目主表是整个库的核心字段通常在 10 个上下。常见设计是id、科目 ID、题型、难度、题干、答案、解析、来源、状态、创建时间。下面这份建表语句我做了微调但思路和网上题库包基本一致CREATE TABLE question ( id bigint unsigned NOT NULL AUTO_INCREMENT, subject_id smallint unsigned NOT NULL COMMENT 科目ID关联 subject.id, type tinyint unsigned NOT NULL COMMENT 题型1单选 2多选 3判断 4填空 5解答, difficulty tinyint unsigned NOT NULL COMMENT 难度等级1到5, knowledge_point varchar(100) NOT NULL COMMENT 知识点编码或路径, stem text NOT NULL COMMENT 题干正文, answer text COMMENT 参考答案, analysis text COMMENT 解析, source varchar(100) DEFAULT COMMENT 题目来源, status tinyint NOT NULL DEFAULT 1 COMMENT 1启用 0停用, created_at datetime DEFAULT CURRENT_TIMESTAMP, updated_at datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_subject_type (subject_id,type), KEY idx_difficulty (difficulty) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT题目主表;这里knowledge_point单独用一个字符串字段是省事设计。更规范的做法是拆出知识点表和题目-知识点关联表因为一题可以对应多个知识点多对多关系用字符串逗号分隔会很难查。不过很多现成题库为了简化就这么干了你要接受它的取舍。选项表只在有选择题时才有意义而且一条题目的选项行数不固定所以必须单独存CREATE TABLE question_option ( id bigint unsigned NOT NULL AUTO_INCREMENT, question_id bigint unsigned NOT NULL COMMENT 关联 question.id, option_order tinyint unsigned NOT NULL COMMENT 选项顺序1代表A, option_text text NOT NULL COMMENT 选项内容, PRIMARY KEY (id), KEY idx_question_id (question_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT题目选项表;还有一类表叫关联表或归档表比如题目-知识点关联。如果你在 zip 里看到它说明这个题库的设计比较规范如果只有主表加一个知识点逗号字段说明作者为了省事做了扁平化。两种情况都能用但后续查「某个知识点下有哪些题」的写法完全不同——前者要用 JOIN后者要用 LIKE。2.2 打开建表脚本时我按什么顺序读表结构拿到 .sql 文件不要从第 1 行读到最后一题那样太慢。我读建表脚本的顺序分四步。第一步看文件头部的注释和 SET 语句。很多脚本开头是SET NAMES utf8mb4;、SET FOREIGN_KEY_CHECKS0;这两行决定了导入后的字符集和外键校验状态。如果脚本里没有导入时就要自己补。第二步用编辑器的搜索功能或者命令行的 grep把所有CREATE TABLE拉出来。这一步能快速看到这个库一共几张表谁先建谁后建。外键依赖的表通常排在被依赖的表后面这个顺序如果乱了直接导入一定会报「表不存在」的错误。第三步挑题目主表看字段注释。注释里一般写着题型编号、难度等级的含义这比去读一万行 INSERT 高效得多。你可以用一条简单命令抽取表结构grep -n CREATE TABLE school_questions.sql sed -n 1,80p school_questions.sql第一条命令定位所有建表语句的行号第二条命令看文件开头 80 行。这样你还没导入数据库就已经知道有哪些表、主表长什么样了。这个动作几乎每次导入都能帮我避免一次翻车尤其是刚拿到的包可能建表语句和插入数据混杂在一起时。第四步是看 INSERT 语句的字段列表。如果 INSERT 里写明了(id, subject_id, type, ...)那说明字段一一对应如果只写VALUES没写字段名那说明数据录入顺序和建表顺序完全一致改表结构时就要小心索引错位会导致整批数据写进错误的列。读完这四步你对这个题库的了解就超过了 90% 直接双击导入的人。而且你可以在导入前先做一个决定是原样导入还是先用脚本把建表语句里的字符集、引擎统一改成你服务器的当前配置。3. 把 SQL 灌进 MySQL 的两种姿势命令行 source 和图形客户端导入表结构看完了接下来才是真正动手的时候。这里唯一的硬性要求是不要用文本编辑器打开整个 .sql 文件去「查看」更不要把它复制粘贴进查询窗口。下面两种方式都可行但我推荐第一条。3.1 命令行导入最大的坑是字符集一条指令把参数说透命令行导入是处理大文件最稳的方式因为不用经过客户端内存中转。假设你的题库文件名是school_questions.sql最小可用的导入命令是这样mysql -uroot -p \ -h127.0.0.1 \ -P3306 \ --default-character-setutf8mb4 \ school_db school_questions.sql命令里每一项都值得说清楚。-h127.0.0.1是连本机 MySQL如果不加这一项很多 MySQL 客户端会默认走本机 socket在某些系统上反而慢。-P3306注意是大写的 P指定端口小写-p才是密码参数这两个别写反写反了要么提示密码错误要么提示端口错误。--default-character-setutf8mb4是导入中文题库的核心参数。MySQL 服务端的默认字符集可能是 latin1 或 utf8mb3如果你不指定客户端发送的语句会被按服务端默认字符集解释中文字段非常容易变成乱码。这个参数的作用是让客户端告诉服务端「我发的 SQL 是用 utf8mb4 编码的」服务端也会用 utf8mb4 去解析。绝大多数线上题库脚本都是 utf8mb4少数老包是 gbk如果是 gbk 就把参数改成gbk先看脚本头部的 SET NAMES 再定。school_db是目标数据库名在你执行命令之前这个库得先建好。用下面的语句建库注意字符集和排序规则也一并指定mysql -uroot -p -e CREATE DATABASE school_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;utf8mb4_general_ci适合题库这种查询为主的场景排序规则更宽松如果你需要精确按 Unicode 规则排序可以用utf8mb4_unicode_ci。但对中文题库来讲一般就够用了。另一种命令行导入方式是先进 MySQL 再执行 sourcemysql -uroot -p -h127.0.0.1 --default-character-setutf8mb4进入交互界面后输入USE school_db; SOURCE /home/user/school_questions.sql;SOURCE 后面的路径是服务器本地路径不是 Windows 盘符路径这一点很多人第一次用会写错。SOURCE 和重定向的最大区别在于SOURCE 会在终端里逐行显示执行结果导入过程肉眼可见但输出会非常长刷屏刷到你想放弃。我一般只在导入中途出问题、需要定位是哪一段报错时才用 SOURCE平时直接用重定向。3.2 图形客户端导入时先关掉外键校验再看错误日志如果你更习惯用图形客户端比如 DBeaver 或者官方自带的 Workbench操作路径基本是连上数据库、选中目标库、选择运行 SQL 脚本文件、然后等结果。这个流程本身没问题但有几个参数会被默认值坑到。第一个是字符集选项。很多图形客户端的「运行 SQL 脚本」对话框里默认编码是系统区域设置的编码Windows 中文系统可能默认 GBK你要手动改成 UTF-8。第二个是导入超时时间。MySQL 的 max_allowed_packet 默认值是 64MB如果脚本里某一行插入语句特别长超过了这个限制导入会直接断开。你可以先执行下面的命令查看当前值SHOW VARIABLES LIKE max_allowed_packet;如果偏小可以在当前会话里临时调大SET GLOBAL max_allowed_packet 134217728;这个参数指单个包的最大字节数128MB 基本能覆盖绝大多数题目。注意它是 GLOBAL 级别新连接才生效图形客户端要重新连一次才能读到新值。第三个是外键校验。如果脚本里的建表顺序不合理或者你导入的目标库已经有部分旧表图形客户端导入时会一个接一个报「外键约束失败」。常见做法是在导入前手动执行一条SET FOREIGN_KEY_CHECKS 0;导入完成后再恢复为 1。有些脚本自己就带了这个语句导入过程中你也看到日志里跑过它那就没必要手动加。但问题在于图形客户端大多数情况是一个语句一个语句执行如果脚本没写遇到外键报错会停在那里等你点确认而不是自动跳过。图形客户端导入还有个隐藏问题大事务。有些脚本用BEGIN和COMMIT包住所有插入语句几十万行数据放在一个事务里一旦中途失败所有数据回滚前面导入的时间全部白费。命令行重定向的方式反而不会这样逐事务保留虽然中途失败会有部分数据但至少能快速重来。4. 十万道题能不能用看三个验证查询抽题、判重、性能摸底库导进去之后第一件事不是急着写高大上的功能而是验证这套题「能不能干活」。我一般做三个验证查询抽一页题、找重复题、看一条慢查询的执行计划。这三个问题覆盖了日常使用频率最高的场景。4.1 抽一套 20 题的卷子先认识 ORDER BY RAND 的代价最常见的需求是按科目、题型、难度随机抽 20 道题。很多新手第一反应是写成这样SELECT id, stem, difficulty FROM question WHERE subject_id 1 AND type 1 AND difficulty BETWEEN 3 AND 4 ORDER BY RAND() LIMIT 20;这个语句在 1 万行以内跑起来问题不大数据量到了 10 万行就开始卡几十万行基本没法用。原因是ORDER BY RAND()会让 MySQL 对满足条件的每一行都生成一个随机数然后排序取前 20。这意味着它要先扫描全表、把所有行放进临时表才能排序。对中小学题库这种想要做随机组卷的场景几乎每个请求都会带这个操作性能不可接受。我一般这样改先查出满足条件的 id 集合在这个小集合里随机再回表取数据。更具体地说可以用子查询缩小随机范围SELECT q.id, q.stem, q.difficulty FROM question q JOIN ( SELECT id FROM question WHERE subject_id 1 AND type 1 AND difficulty BETWEEN 3 AND 4 ORDER BY RAND() LIMIT 20 ) t ON q.id t.id;这个写法仍然会扫描所有符合条件的行但子查询里只拿 id不碰 text 字段临时表的体积小很多排序负担也小。如果表里数据已经有几十万行还是慢那就要靠索引覆盖了。(subject_id, type, difficulty)三列的联合索引是这个查询的关键。有了它WHERE 的等值条件加范围条件都能走索引子查询的扫描范围会被索引直接框住。如果你没建这个索引可以把主表里的idx_subject_type扩展成三列联合索引注意 B 树对范围条件之后的列无法继续使用所以 difficulty 应该放在最后。4.2 用 MD5 找重复题text 字段直接 GROUP BY 的翻车现场题库包里重复题非常普遍尤其是从多个渠道汇总出来的包。判断重复题的标准往往是题干相同。有些人直接用 stem 字段 GROUP BYSELECT stem, COUNT(*) AS cnt FROM question GROUP BY stem HAVING cnt 1 LIMIT 50;这个语句在数据量大时有两个问题。第一GROUP BY 一个 text 字段会消耗大量的排序和临时表空间而且 text 在 GROUP BY 时只能用前缀即使字段内容不同也可能被归到同一组。第二InnoDB 的临时表会从内存转磁盘几十万行这个查询能把磁盘 IO 打满。我一般会给题目主表加一个 MD5 列存题干的哈希值。MD5 在这里不是用来加密而是把不定长文本变成定长 32 位字符串索引和分组都快得多ALTER TABLE question ADD COLUMN stem_md5 char(32) GENERATED ALWAYS AS (MD5(stem)) STORED; ALTER TABLE question ADD INDEX idx_stem_md5 (stem_md5);第一条语句里的GENERATED ALWAYS AS ... STORED是生成列MySQL 在插入和更新时会自动计算 MD5 值不需要你手动维护。之后查重就可以这样SELECT q.id, q.stem_md5, q.stem FROM question q JOIN ( SELECT stem_md5, MIN(id) AS keep_id, COUNT(*) AS cnt FROM question GROUP BY stem_md5 HAVING cnt 1 LIMIT 50 ) d ON q.stem_md5 d.stem_md5 AND q.id ! d.keep_id;这条语句能列出「应该被删除的重复项」保留每组里 id 最小的那一条。需要注意MD5 是 128 位摘要理论上有碰撞可能但对题库查重这种场景碰撞概率低到可以忽略。如果你要更严格可以把 stem 和 answer 两个字段拼起来做 MD5减少不同答案同题干的干扰。还有一类近似重复是「题干里有空格不同、标点全角半角不同」MD5 查不出来。这种只能靠归一化处理后再比比如把全角字符转半角、去掉所有空白、去掉 HTML 标签再算一次normal_stem_md5。不过那是清洗数据的活儿不是 SQL 能直接解决的我通常只在导入前做预处理。4.3 性能摸底EXPLAIN 只看这几列就够题库系统做完能不能放心上线我在验证阶段一定会跑一次 EXPLAIN 看执行计划。以刚才的抽题查询为例EXPLAIN SELECT q.id, q.stem FROM question q JOIN ( SELECT id FROM question WHERE subject_id 1 AND type 1 AND difficulty BETWEEN 3 AND 4 LIMIT 20 ) t ON q.id t.id;执行计划里重点看三列type、key、rows。type列是访问类型常见取值从好到差依次是 const、eq_ref、ref、range、index、ALL。ref和range说明用上了索引ALL说明全表扫描。如果看到ALL需要马上检查 WHERE 条件有没有匹配索引。key列显示实际用到的索引名如果显示 NULL说明这条查询没走索引。rows列是 MySQL 估算的扫描行数这个数字比实际行数大 10 倍以上时一般就是索引失效或者统计信息过旧。我还习惯看一个容易忽略的细节Extra列里如果出现Using temporary或Using filesort说明查询里产生了临时表或文件排序。短时间跑一次没事要是这个查询被频繁调用磁盘 IO 会被拖垮。前面的 GROUP BY 查重就属于这种情况。验证完这三个查询你就知道这个题库库是「能用了」还是「还得修」。绝大多数网上下的题库包导入之后缺的不是数据而是索引。所以性能摸底之后顺手补上联合索引和 MD5 生成列这两步的价值比后面任何花哨的功能都大。5. 导入题库的五个高频坑现象、原因、改法这一章是血泪经验的集合。题库导入翻车的点位其实很集中我把高频问题按「现象→原因→解决」拆开每一条都是可以直接照抄的改法。5.1 打开表全是问号utf8mb4 没有贯穿全程现象导入执行不报错但查询出来的题干、答案全是乱码常见的是问号串也有口字型的方块。原因SQL 文件本身是 utf8mb4 编码但你用系统默认的 gbk 或 latin1 编码导入或者建库时字符集没指定服务端按默认字符集解析了插入语句。整条链路只要有一个环节不是 utf8mb4中文就会坏。解决先查库和表的字符集SELECT default_character_set_name FROM information_schema.SCHEMATA WHERE schema_name school_db; SELECT table_collation FROM information_schema.TABLES WHERE table_schema school_db AND table_name question;如果显示不是 utf8mb4需要把字符集改掉。数据还没导入时重建库最干净mysql -uroot -p -e DROP DATABASE school_db; CREATE DATABASE school_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;已经导入但乱码的表修改字符集后数据不会自动从乱码变回可读因为写入时已经损坏。这种情况别浪费时间想着修复直接换编码重新导入一次。切记导入命令必须带--default-character-setutf8mb4别依赖脚本头部的 SET NAMES因为有些人下载的包里那个 SET NAMES 被注释掉了。5.2 解压出来不是 .sql 而是又一个 zip先看压缩包结构现象解压第一层得到一堆 zip、rar、7z 文件往里翻还有多层目录根目录路径带着中文和空格命令行操作时各种报路径不存在。原因发布者打包时没有整理目录把总压缩包塞进了另一个压缩包或者用中文目录名直接打包。很多图形解压工具默认继承原压缩包目录结构导致脚本路径变得很深。解决建议不要用鼠标双击解压改用命令行解压到指定目录并且先列出压缩内容再决定unzip -l school_questions.zip unzip school_questions.zip -d ./quiz_unpack-l参数只列内容不真正解压你会第一眼看到有没有嵌套压缩包。-d指定解压到当前目录下的 quiz_unpack避免和默认同名目录搅在一起。解压完成后用 find 检查所有 .sql 文件的路径find ./quiz_unpack -name *.sql -exec ls -lh {} \;如果还有嵌套的 zip再按层解一次。这里有个判断标准最终你拿出来给 mysql 导入的是一个 .sql 或多个 .sql 文件不是又一个压缩包。如果发现目录里既有 .sql 又有 .zip优先怀疑 zip 里才是更新版本看看两边的文件大小和日期别导了一份旧数据还蒙在鼓里。5.3 几百 MB 的 SQL 文件用编辑器打开只会卡死现象导入前想预览一下文件内容双击用文本编辑器打开结果编辑器假死几分钟然后提示文件过大或者导入到一半提示 lost connection前面导的一堆数据都回滚了。原因SQL 数据文件动辄几百 MB普通文本编辑器会一次性读入内存内存耗尽自然卡死。导入中断则是连接超时或 max_allowed_packet 限制导致。解决预览大文件别用图形编辑器用命令行分段看head -100 school_questions.sql grep -n CREATE TABLE school_questions.sql | head -50 sed -n 5000,5050p school_questions.sqlhead看文件头部的建库建表语句grep -n提取所有 CREATE TABLE 的位置sed指定行号区间看某一段插入数据。这三个命令处理几百 MB 文件都是秒开也不会把内存吃满。导入中断的解决方式分两步。第一步服务器端调大超时和包大小SET GLOBAL wait_timeout 28800; SET GLOBAL max_allowed_packet 268435456;第二步导入时用 nohup 或把它放到后台避免终端断开导致导入中断nohup mysql -uroot -p -h127.0.0.1 --default-character-setutf8mb4 school_db school_questions.sql import.log 21 导入过程会在后台运行日志写入 import.log你随时可以用tail -f import.log查看进度。这一步能在最大程度上避免「手机锁屏、终端断连、导入中止」的悲剧。5.4 外键约束导致导入到一半中断检查建表顺序和引擎现象导入脚本执行到第几百条时报Cannot add or update a child row: a foreign key constraint fails或者建表阶段提示引用了一张不存在的表。原因脚本里建表顺序不合理子表先于父表创建或者父表数据还没插入子表数据就来了还有一种情况是把表建成了 MyISAM 引擎MyISAM 不支持外键约束导入提示直接报错。解决最简单的兜底是在导入前强制关闭外键校验。命令行的做法是在同一个 mysql 会话先执行SET FOREIGN_KEY_CHECKS 0; USE school_db; SOURCE /path/to/school_questions.sql; SET FOREIGN_KEY_CHECKS 1;如果用的是重定向导入可以把这行写在 SQL 文件的最前面。不过我不建议直接改原文件更稳的方式是生成一个包装脚本echo SET FOREIGN_KEY_CHECKS0; import_wrapper.sql cat school_questions.sql import_wrapper.sql echo SET FOREIGN_KEY_CHECKS1; import_wrapper.sql mysql -uroot -p -h127.0.0.1 --default-character-setutf8mb4 school_db import_wrapper.sql原理是在同一次连接里关闭外键检查后MySQL 不再逐条校验子表和父表的匹配关系导入完再打开保证后续日常操作的外键一致性。关掉期间不会破坏数据只要数据本身是自洽的之后数据库也不会出问题。顺便检查一下表的引擎如果主表是 MyISAM建议转成 InnoDB。数据没导入前可以直接改建表语句里的 ENGINE已经导入的话执行ALTER TABLE question ENGINEInnoDB;InnoDB 支持事务、行级锁和外键比 MyISAM 适合题库这种读写混合且需要在线修改的应用场景。5.5 重复导入把自增主键干翻车TRUNCATE 与业务唯一键现象第一次导入成功第二次重新导入同一份文件时报Duplicate entry 1024 for key PRIMARY或者导入成功但数据翻倍id 乱跳。原因INSERT 语句里显式写了 id 值第二次导入时和已有主键冲突。如果 INSERT 没写 id只写其他字段那自增主键会从上次最大值继续数据看起来正常但和之前导入的内容重复叠加。解决如果想清掉旧数据重新导入用 TRUNCATE 而不是 DELETE。TRUNCATE 会重置自增计数器和所有数据而且速度远快于 DELETETRUNCATE TABLE question; TRUNCATE TABLE question_option;如果不想清库只想追加新题那就不能依赖自增主键去重得给题目加一个业务唯一键比如「来源题库内题号」。关于这个做法我在下一章详细展开这里先记住一个原则只要有「同一份 zip 导两次」的可能主键自增 ID 从来不是判断重复的依据。排查主键撞车还有一种情况值得注意两个 sql 文件都从 1 开始编号但各自对应不同科目如果直接合并导入到一个表第二次会全部撞车。这种多文件题库包要么分表存要么导入前统一重排 id我一般优先选择前者把文件按科目拆开分别导入对应表省去大量改 id 的麻烦。6. 从「能查」到「能用」给题库加增量更新顺便躲开全文索引的坑很多老师拿到题库包导入后查了几次题就觉得完事了。但题库不是一次性静态资源教研组会持续往里加题、改答案、修正解析。这里有两个进阶操作决定了这个库半年后是越来越值钱还是沦为废数据。6.1 增量更新用业务唯一键做主键冲突的后悔药为题目主表增加一个业务唯一键biz_key它代表「这道题在原始题库包里的唯一身份」。如果原始数据里有题号就拼上来源前缀没有题号就用题干 MD5 的前 16 位。ALTER TABLE question ADD COLUMN biz_key varchar(64) DEFAULT ; UPDATE question SET biz_key CONCAT(src01_, id); ALTER TABLE question ADD UNIQUE KEY uk_biz_key (biz_key);之后每次有新的题目数据不再先查有没有重复而是直接插入时触发唯一键处理。MySQL 8.0.19 之后的写法是INSERT INTO question (subject_id, type, difficulty, stem, answer, analysis, biz_key) VALUES (1, 1, 3, 这是一道新题干的题, 答案, 解析, src01_1024) AS new ON DUPLICATE KEY UPDATE stem new.stem, answer new.answer, analysis new.analysis, updated_at NOW();这条语句的意思是如果 biz_key 不存在就正常插入如果已存在就更新题干、答案和解析而不是报错或者插入重复行。我第一次用这套逻辑时也犹豫过后来发现对题库管理来说它就是我常说的后悔药——同一道题被不同人录了多遍最终只会留下一行最新版本。6.2 全文索引对中文题干不友好短文本检索该用的兜底方案有人会建议在 stem 字段上加全文索引来实现题干搜索。MySQL 8.0 的全文索引对中文支持依赖 ngram 分词插件效果中规中矩。对题干这种几十字的短文本全文索引的分词粒度经常导致误匹配搜「三角」能出来「三角形相似」但搜「全等」反而被分词拆碎查不到。我的习惯是题目正文的复杂检索不依赖数据库全文索引。先用结构化字段过滤再在业务层做字符串匹配。具体来说把知识点、题型、难度做成下拉筛选题干搜索用 LIKE 限定在已经缩小的结果集里这样既不用引入额外组件也能跑得动。等到题量超过百万行、业务确实需要语义搜索时再考虑外置搜索服务那个迁移成本不是一开始就该承担的。我养成的习惯是每次拿到题库包先建唯一键再导入先跑查重再上线。经历过一次重复导入导致数据翻倍的教训之后这套流程我再也没跳过。希望这篇笔记能帮你看清这类数据库压缩包的里子和坑照着上面的步骤走一遍一个下午把它变成能正经干活的东西。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站