易助8.0表结构包解析:DDL导入、数据字典与版本Diff
简介:易助8.0 ERP数据库表结构资料面向使用该系统的实施顾问、二次开发人员与运维工程师,重点解决表名含义不清、字段注释缺失以及SQL查询和报表开发时难以快速定位数据表的问题。压缩包共1163个文件、约1.52MB,以1124个XML表结构说明为主体,辅以36个HTML索引页和2个XSL样式文件;XML承载表与字段的中文注释,HTML便于按模块跳转查阅,XSL用于结构化展示与整理。已有792人学习下载,说明其在同类ERP数据字典资料中具有一定参考价值。读者可据此建立较完整的数据库字典,快速核对表名称、字段含义与表间关系,为SQL取数、报表定制、接口开发和日常运维排错提供依据,也能帮助新成员缩短熟悉系统数据结构的时间。
1. 一份表结构包,比源码更早暴露系统底牌
接手陌生系统时,先拿到它的 SQL 表结构,往往比读代码更快看懂这套系统在做什么。易助 8.0 表结构.rar 就是这类东西:它不提供能跑起来的业务系统,只给 8.0 这一版的库表定义——建表语句、字段类型、注释、索引,一般还附带数据字典文档。对要接手老系统、做数据对接、评估升级改动的后端和 DBA 来说,这是成本最低的地图:哪张表是主数据、哪张是流水、哪些字段是软删除标记、哪些表被拆成主子结构,扫一遍表名和注释就有七成把握。前提是你会读、会导、会验证,否则一堆 .sql 躺在压缩包里,和没下载没区别。
2. 拆包先看清单:8.0 表结构包里的四类文件与表名前缀规律
2.1 先列清单再解压:文件类型与体积分布
拿到 RAR 别急着双击解压到桌面,先看清单能省掉后面很多麻烦。表结构包常见的构成是四类:纯 DDL 的.sql、说明文档(.docx/.pdf)、数据字典表格(.xlsx/.csv),以及偶尔混进来的模型文件或备份脚本。知道哪一类占大头,决定后面走「导入数据库」还是「只读文档」的路线。
# 1) 不解压,先看压缩包清单:文件名、原始大小、修改时间 7z l yizhu_8.0_schema.rar # 2) 解到独立目录,别和现有工程混在一起,后面整目录删掉也干净 mkdir -p ./schema_v8 7z x yizhu_8.0_schema.rar -o./schema_v8 -y # 3) 按扩展名统计,一眼看出包里是纯 SQL 还是混了文档 find ./schema_v8 -type f -name '*.*' \ | sed 's/.*\.//' | tr 'A-Z' 'a-z' | sort | uniq -c | sort -rn7z l只读不写,用来确认压缩包完整性;-o指定输出目录,-y跳过交互确认,适合写进脚本。第三步的sed取扩展名、tr统一小写、uniq -c计数,是为了避免.SQL和.sql被算成两类。如果文件名是乱码(Windows 下用 GBK 打的包很常见),7-Zip 加-mcp=936指定代码页重解一次,通常就正常了。
# 4) 逐个确认真实编码,后面导入时要用这个结果去配客户端字符集 file -i ./schema_v8/*.sqlfile -i输出的是 MIME 加 charset,比如charset=utf-8或charset=iso-8859-1(后者很多时候其实是 GBK 被误判)。这一步别省,导入时的乱码九成来自这里没对齐。
2.2 用表名前缀切模块:一条 SQL 看出系统分层
DDL 导进库之后,第一件事不是逐个打开表看字段,而是统计表名前缀。商业系统给表命名通常有强规律:sys_放系统配置和权限,biz_或t_放业务主表,_main/_detail表示主子结构,log_/_his是流水和历史,tmp_/temp_是中间表。前缀一统计,模块边界自己就浮出来了。
-- 取第一个下划线之前的部分当模块前缀,统计表数量、估算行数、占用空间 SELECT SUBSTRING_INDEX(table_name, '_', 1) AS module_prefix, COUNT(*) AS table_cnt, SUM(table_rows) AS approx_rows, ROUND(SUM(data_length + index_length)/1024/1024, 1) AS size_mb FROM information_schema.tables WHERE table_schema = 'schema_v8' AND table_type = 'BASE TABLE' GROUP BY module_prefix ORDER BY table_cnt DESC;SUBSTRING_INDEX(table_name,'_',1)只取第一段,适合「前缀_业务名_后缀」这种三段式命名;如果命名是「业务名_前缀」的反向风格,把索引改成-1取最后一段。table_rows在 InnoDB 上是采样估算值,空库导入后基本全是 0 或很小的数,只能看量级不能当准数;size_mb反而更可靠,因为它来自实际分配的页数。真要知道行数,得对重点表单独跑COUNT(*)。
统计完之后按前缀分组读表名,比按字母顺序读快得多。我一般会先把sys_这一组看完,因为权限表、字典表、参数表决定了整套系统的语义,比如有没有多租户字段(tenant_id/org_id)、有没有统一的操作人字段(create_by/update_by)、删除到底是物理删还是is_deleted标记。这几个问题的答案会贯穿后面所有的数据对接工作。
2.3 判定 DDL 方言:四种数据库的建表特征对照
表结构包不一定来自 MySQL。老系统从 Oracle 迁过来的、或者主库是 SQL Server 的,DDL 语法完全不同,直接往 MySQL 里灌必然报一片语法错。打开任意一个.sql文件看头部几十行,按下面这张表对一遍就能定方言。
| 特征位置 | MySQL | Oracle | SQL Server | PostgreSQL |
|---|---|---|---|---|
| 字段类型写法 | int(11)、datetime | NUMBER(10)、DATE | int、datetime2 | integer、timestamp |
| 标识符包裹 | 反引号 | 双引号或无 | [方括号] | 双引号 |
| 自增实现 | AUTO_INCREMENT | 序列 + 触发器 | IDENTITY(1,1) | serial/IDENTITY |
| 存储声明 | ENGINE=InnoDB | TABLESPACE | ON [PRIMARY] | TABLESPACE |
| 表/字段注释 | 内联COMMENT '...' | 独立COMMENT ON COLUMN | sp_addextendedproperty | 独立COMMENT ON |
| 批量分隔符 | ; | / | GO | ; |
判别顺序建议按「类型写法 → 自增 → 注释形式」来。类型写法最不容易骗人,NUMBER和int(11)一眼就分得开;注释形式决定你后面能不能直接靠information_schema反推语义,MySQL 把注释放在建表语句里,Oracle 和 PostgreSQL 是单独语句,一旦导出时漏了COMMENT ON,字典里就只剩字段名可读。真遇到混合方言的包(比如主体是 MySQL、少数几张表是 Oracle 语法),别硬导,先把异构的那几个文件单独拎出来手工转换,混在一起导只会让你分不清哪个错是语法错、哪个错是编码错。
3. 把 DDL 跑进本地库:MySQL 8 建库、分批导入与四条校验 SQL
3.1 建一个只放结构的独立 schema,别往业务库塞
最容易犯的错是把表结构导进已有的开发库。一旦表名撞上,CREATE TABLE报table already exists,你分不清是包里的表本来就重名,还是自己库里已有同名表;更麻烦的是后面做版本对比时,基准被污染了。正确做法是单开一个库,名字带版本号,用完整个DROP DATABASE就能清干净。
-- 结构仓单独建库;排序规则按本机版本选,8.0 用 0900_ai_ci,5.7 只能 general_ci CREATE DATABASE schema_v8 DEFAULT CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_0900_ai_ci;字符集选utf8mb4而不是utf8,是因为老 DDL 里存表情符号或生僻字的场景虽然少,但字段注释里出现四字节字符的概率不低,utf8三字节存不下会直接截断。排序规则是个隐形坑:MySQL 8.0 默认utf8mb4_0900_ai_ci,5.7 根本没这个排序规则,反过来的话把 5.7 导出的 DDL 灌进 8.0 一般没事,但 8.0 的 DDL 灌 5.7 会直接报 1273。所以建库前先SELECT VERSION();确认本机版本,再决定写哪一行。
# 单文件导入的标准姿势 mysql --default-character-set=utf8mb4 \ -h 127.0.0.1 -P 3306 -u root -p \ --max-allowed-packet=64M \ schema_v8 < ./schema_v8/table_all.sql--default-character-set是客户端解释字节的方式,必须和文件真实编码一致,它和库的字符集是两回事。--max-allowed-packet=64M防的是长注释和超宽表导致的数据包超限,报错形如Packet too large。这里刻意不加--force——它会让每条失败语句被静默跳过,最后你得到一个缺了几十张表的「半成品结构仓」,还查不出缺在哪。
3.2 分批导入脚本:把一条大命令拆成可回滚的循环
表结构包经常不是单个大文件,而是按模块拆成十几个.sql。一条条手敲不现实,写个循环又容易在某个文件失败时整体中断。下面的脚本思路是:不管单个文件成不成功,全部跑完,失败的记进清单,最后统一看。
#!/usr/bin/env bash # 分批导入:单文件失败不阻塞后续,但失败清单必须留痕 set -uo pipefail DB="schema_v8" : > failed.list # 清空上一轮的失败清单 : > import.err.log for f in ./schema_v8/*.sql; do printf '==> %s\n' "$f" if ! mysql --default-character-set=utf8mb4 \ -h 127.0.0.1 -P 3306 -u root -p"$MYSQL_PWD" \ --max-allowed-packet=64M \ "$DB" < "$f" 2>>import.err.log; then echo "$f" >> failed.list fi done echo "失败文件数: $(wc -l < failed.list)"set -uo pipefail里故意不写-e,因为要的是遍历完全部文件而不是遇错即停;-u保证变量名写错时立刻报错,pipefail让管道里任一环节失败都能被捕获。密码走MYSQL_PWD环境变量而不是命令行参数,避免明文落进 shell history 和进程列表。2>>import.err.log把错误累积到一个文件里,比在终端里翻屏找错高效得多。跑完之后先看failed.list有几行,再grep -n 'ERROR' import.err.log | head -30看错误类型分布——如果几十个文件报的是同一个错误码,那基本是环境问题(排序规则、字符集、sql_mode),改一处重跑全部就行,不用逐个文件修。
3.3 导入后必跑的四条校验 SQL
导入成功不等于导对了。下面四条校验按「数量、一致性、完整性、可读性」四层往下查,跑完心里就有底。
-- 校验 1:表数量是否与 DDL 里的 CREATE TABLE 条数对得上 SELECT COUNT(*) AS base_table_cnt FROM information_schema.tables WHERE table_schema = 'schema_v8' AND table_type = 'BASE TABLE';配套在命令行数一遍源文件里的建表语句条数,两边对齐才算真导全了:
# 统计所有 SQL 文件里 CREATE TABLE 出现的次数,忽略大小写 grep -ric 'create table' ./schema_v8/*.sql | awk -F: '{s+=$2} END {print s}'grep -c统计的是匹配行数而不是出现次数,如果一行里出现两次会少算;表结构文件一般一行一条建表语句,所以够用。两边数字差一两个,通常是有CREATE TABLE IF NOT EXISTS被跳过了,差得多就是整块文件没导进去。
-- 校验 2:排序规则是否统一,混排会在 JOIN 时直接报 Illegal mix of collations SELECT table_collation, COUNT(*) AS cnt FROM information_schema.tables WHERE table_schema = 'schema_v8' GROUP BY table_collation;这条一定要跑。包里如果混了utf8mb4_general_ci和utf8mb4_0900_ai_ci两种,平时查单表没事,一旦关联查询就炸,而且报错信息只告诉你哪两个字段冲突,不告诉你哪张表是历史遗留。
-- 校验 3:没有主键的表先记下来,后面做数据核对会非常难受 SELECT t.table_name FROM information_schema.tables t LEFT JOIN information_schema.statistics s ON s.table_schema = t.table_schema AND s.table_name = t.table_name AND s.index_name = 'PRIMARY' WHERE t.table_schema = 'schema_v8' AND t.table_type = 'BASE TABLE' AND s.index_name IS NULL;-- 校验 4:注释缺失比例,判断这份表结构是不是「裸奔版」 SELECT table_name, COUNT(*) AS col_cnt, SUM(column_comment = '') AS no_comment_cnt, ROUND(SUM(column_comment = '') / COUNT(*) * 100, 1) AS miss_pct FROM information_schema.columns WHERE table_schema = 'schema_v8' GROUP BY table_name HAVING miss_pct > 50 ORDER BY miss_pct DESC;第四条的结论直接决定后面的工作方式。如果缺失率普遍在 10% 以下,说明这份包是认真维护过的,靠注释就能反推大部分业务语义;如果一半以上的表注释全空,那说明导出时用了精简模式,你得靠字段名、类型和索引去猜,这时更应该去找包里附带的.xlsx数据字典对不对得上。
4. 反推数据字典:information_schema 三表联查与索引缺口清单
4.1 字段字典:columns 与 tables 联查导出 TSV
表结构包最大的价值不是能建库,而是能把「表—字段—注释」整理成一份可检索的数据字典。information_schema.columns里有字段名、类型、可空、默认值、注释,information_schema.tables里有表级注释,两表按库名加表名关联就成。
-- 生成全库字段字典:表级注释 + 字段级元信息 SELECT c.table_name AS 表名, t.table_comment AS 表说明, c.ordinal_position AS 序号, c.column_name AS 字段, c.column_type AS 类型, c.is_nullable AS 可空, c.column_default AS 默认值, c.column_comment AS 注释 FROM information_schema.columns c JOIN information_schema.tables t ON t.table_schema = c.table_schema AND t.table_name = c.table_name WHERE c.table_schema = 'schema_v8' ORDER BY c.table_name, c.ordinal_position;ordinal_position是字段在建表语句里的物理顺序,按它排序才能保持和原始 DDL 一致的阅读顺序,这点在做人工比对时很重要。导成文件用-B批处理模式,输出是制表符分隔,比默认的表格边框格式好处理:
# -B 批处理模式输出 TSV;补 BOM 是为了 Excel 双击打开时中文不乱码 mysql --default-character-set=utf8mb4 -u root -p -B schema_v8 -e "$(cat dict.sql)" > data_dict.tsv printf '\xEF\xBB\xBF' > data_dict_bom.tsv && cat data_dict.tsv >> data_dict_bom.tsv-e后面直接跟 SQL 字符串,用$(cat dict.sql)把文件内容塞进去,比在命令行里写一长串引号安全。BOM 那一步不是可选项——Excel 打开不带 BOM 的 UTF-8 TSV 会把中文按本地编码解释,整个注释列变乱码,白白浪费一次导出。
4.2 索引缺口:以_id结尾的列有多少没进索引首列
很多商业系统导出表结构时会刻意去掉外键约束(为了分库、为了导入顺序可控),但索引一般会留。反过来查「哪些关联字段没有索引」比查外键更有意义,因为真正影响性能的是索引,不是约束。
-- 找以 _id 结尾但没被任何索引首列覆盖的字段 SELECT c.table_name, c.column_name, c.column_type FROM information_schema.columns c LEFT JOIN information_schema.statistics s ON s.table_schema = c.table_schema AND s.table_name = c.table_name AND s.column_name = c.column_name AND s.seq_in_index = 1 -- 必须是联合索引的第一列才算有效命中 WHERE c.table_schema = 'schema_v8' AND c.column_name LIKE '%\_id' AND s.index_name IS NULL ORDER BY c.table_name, c.column_name;seq_in_index = 1这个条件是整条 SQL 的关键。如果只写s.column_name = c.column_name不加它,一个字段挂在联合索引的第二位也会被算成「有索引」,而实际上WHERE user_id = ?这种单条件查询用不上它。LIKE '%\_id'里的反斜杠是转义下划线,否则_在 LIKE 里匹配任意单字符,%_id会把paid、valid这类结尾也捞进来。
查出来的清单不要直接拿去建索引。先分清哪些是真正的关联键(user_id、order_id、product_id),哪些只是名字以_id结尾的普通字段(比如external_id存的是外部单号,查询模式完全不同)。判断方法是看表规模和字段基数,大表上的高频关联字段才值得加。
4.3 注释即语义:用 tinyint 与 COMMENT 还原状态枚举
状态字段是最容易读错的地方。同一套系统里status、state、flag、is_xxx可能同时存在,含义还不一样。tinyint的显示宽度是个有用的线索:tinyint(1)绝大多数是布尔语义(0/1),tinyint(2)或tinyint(4)往往是多状态枚举,而char(1)一般是更老的写法。
-- 把所有带注释的 tinyint 字段捞出来,注释里通常写着枚举映射 SELECT table_name, column_name, column_type, column_default, column_comment FROM information_schema.columns WHERE table_schema = 'schema_v8' AND column_type LIKE 'tinyint%' AND column_comment <> '' ORDER BY table_name, ordinal_position;注释里常见的写法是1:待付款 2:已付款 3:已发货 0:已取消,也有写成状态 1未处理 2处理中的。两种都能读,但前者有明确键值对,可以直接转成 Java 枚举或字典表;后者遇到注释里只写「状态」两个字的,就只能靠默认值和历史数据推。顺带一提,金额字段的类型也要在这步一起看:decimal(18,2)是正常写法,出现float或double存金额的,说明这套表结构的历史包袱不轻,后面做对账要多留个心眼。
5. 避坑与排查:导入表结构时最容易翻车的五个点
5.1 排序规则不兼容:utf8mb4_0900_ai_ci在 5.7 上直接报 1273
现象:导入刚开始就整片失败,错误日志里刷屏ERROR 1273 (HY000): Unknown collation: 'utf8mb4_0900_ai_ci',一张表都没建起来。
原因:DDL 是在 MySQL 8.0 上导出的,建表语句里显式写了 8.0 才有的排序规则,而本地环境是 5.7。反过来的情况(5.7 的包导进 8.0)通常不报错,因为 8.0 向下兼容旧的排序规则名。
解决:先确认本机版本,如果是 5.7 又不想为了看结构去升版本,就批量替换排序规则。改之前先备份,sed -i.bak会自动留一份.bak。
# 把 8.0 专有的排序规则降级成 5.7 可识别的等价物 sed -i.bak \ -e 's/utf8mb4_0900_ai_ci/utf8mb4_general_ci/g' \ -e 's/utf8mb4_0900_as_cs/utf8mb4_bin/g' \ ./schema_v8/*.sql_ai_ci是不区分重音不区分大小写,对应 5.7 的general_ci语义最接近;_as_cs是区分重音区分大小写,只能用utf8mb4_bin近似替代。替换完重跑导入,别在同一个库上反复试,先DROP DATABASE重来一遍更干净。
5.2 中文注释变问号:客户端字符集和文件编码没对齐
现象:表建成功了,但字段注释全是???或者一堆乱码字符,表名正常的、注释不正常。
原因:源文件实际是 GBK 编码(老系统导出工具常见),导入时没指定字符集,客户端按默认的utf8mb4去解释 GBK 字节,非法序列被替换成问号。另一种情况是文件确实是 UTF-8,但连接字符集设成了latin1,同样会坏掉。
解决:先用file -i确认文件编码,导入时把客户端字符集设成一致的值。GBK 文件就指定--default-character-set=gbk,让 MySQL 自己做从 gbk 到 utf8mb4 的转换。已经导坏了的库没法原地修,注释信息在转换时已经丢失,只能删库重导。导完跑一条检查 SQL 确认干净:
-- 找出注释里出现连续问号的字段,正常业务注释不会这样 SELECT table_name, column_name, column_comment FROM information_schema.columns WHERE table_schema = 'schema_v8' AND column_comment REGEXP '\\?{2,}';5.3 零日期默认值:严格 sql_mode 卡住老 DDL
现象:绝大多数表都成功,只有零星几张报ERROR 1067 (42000): Invalid default value for 'create_time',打开 DDL 一看写的是datetime NOT NULL DEFAULT '0000-00-00 00:00:00'。
原因:MySQL 5.7 之后默认开启STRICT_TRANS_TABLES和NO_ZERO_DATE,禁止零日期作为默认值。老系统的 DDL 大量使用零日期表示「未设置」,在新版本上直接不合法。
解决:在当前会话里临时放宽 sql_mode,只影响这一次连接,不动全局配置。这是我最推荐的折中方案——改全局会影响这台机器上所有应用,风险远大于收益。
-- 会话级放宽,导入连接断开后自动失效 SET SESSION sql_mode = 'NO_ENGINE_SUBSTITUTION';如果用的是命令行导入,可以在导入前用--init-command预置这条语句,或者把SET SESSION sql_mode=...;写在每个 SQL 文件的第一行。要是这个包需要长期使用,更彻底的做法是把零日期默认值批量替换成NULL并把字段改成可空,但那会改变原始结构,做版本对比时会引入噪声,得权衡。
5.4 关键字表名:反引号丢一个就 1064
现象:报ERROR 1064 (42000): You have an error in your SQL syntax,位置指向某个表名或字段名,但那条语句看着完全正常。
原因:表名或字段名用了 MySQL 保留字,比如order、group、desc、key、status、condition、range。原包里靠反引号包住所以能建,一旦经过复制粘贴、编码转换、或者某个中间工具把反引号吃掉了,语句就废了。
解决:先确认反引号是不是真的丢了,grep -c '' 数一下;如果是丢了,重新从原始压缩包解一份,别在中间文件上修。如果是包本身就没加反引号(有些导出工具会省),那就得给保留字字段批量补上,或者干脆在导入时把这类表单独拎出来手工处理。
# 检查是否有建表语句里完全没有反引号,这种大概率会踩保留字 grep -rLn '`' ./schema_v8/*.sql-L列出的是「不匹配」的文件,也就是一个反引号都没有的那些,通常是导出工具配置不同造成的,值得单独看一眼。
5.5 没有外键不等于没有关联:别把 FK 缺失当成无关系
现象:查information_schema.table_constraints,外键约束一条都没有,于是判断这套表结构是「扁平设计、表间无关联」,结果写出来的关联查询全是错的。
原因:商业系统在导出表结构时删掉外键约束是常见做法——分库分表场景下外键没法用,批量导入时外键还会拖慢速度、打乱导入顺序。约束没了,但语义上的关联还在,只是从数据库层挪到了应用层。
解决:别信 FK,靠三样东西反推关联——字段命名(xxx_id指向xxx表的主键)、字段类型一致性(关联两侧的类型和长度必须相同,bigint(20)对int(11)这种就是错的)、以及主键命名规律(id还是xxx_id还是复合主键)。找到候选关联后,用抽样查询验一下命中率:
-- 验证候选关联:命中率接近 100% 说明关系成立,接近 0 说明猜错了 SELECT COUNT(*) AS total_rows, SUM(CASE WHEN u.id IS NOT NULL THEN 1 ELSE 0 END) AS matched_rows, ROUND(SUM(CASE WHEN u.id IS NOT NULL THEN 1 ELSE 0 END) / COUNT(*) * 100, 2) AS hit_pct FROM biz_order o LEFT JOIN sys_user u ON u.id = o.user_id;把表名换成包里实际的表。hit_pct在 95% 以上基本可以确认关系;在 60% 到 90% 之间要警惕,可能是软删除导致的对不上,或者字段存的是历史用户 ID;低于 10% 就是猜错了,换下一个候选。这个验证必须在有数据的库上做,纯结构仓里跑出来全是 0,没有意义。
6. 两份 DDL 做 diff:把升级风险提前到上线前一天
真正让表结构包产生复利的地方,是手里同时有新旧两版的 DDL。把 7.x 和 8.0 各导一个 schema,用information_schema做列级对比,比人工翻文档快一个数量级,而且不会漏。
-- 第一步:新增与变更。以新版为基准左连旧版,逐列打标 SELECT v8.table_name AS 表名, v8.column_name AS 字段, v7.column_type AS 旧类型, v8.column_type AS 新类型, CASE WHEN v7.column_name IS NULL THEN '新增字段' WHEN v7.column_type <> v8.column_type THEN '类型变更' WHEN IFNULL(v7.is_nullable,'') <> IFNULL(v8.is_nullable,'') THEN '可空变更' WHEN IFNULL(v7.column_default,'~') <> IFNULL(v8.column_default,'~') THEN '默认值变更' ELSE '无变化' END AS 变更类型 FROM ( SELECT table_name, column_name, column_type, is_nullable, column_default FROM information_schema.columns WHERE table_schema = 'schema_v8' ) v8 LEFT JOIN ( SELECT table_name, column_name, column_type, is_nullable, column_default FROM information_schema.columns WHERE table_schema = 'schema_v7' ) v7 ON v7.table_name = v8.table_name AND v7.column_name = v8.column_name HAVING 变更类型 <> '无变化' ORDER BY 表名, 字段;IFNULL(...,'~')里的~是个不可能出现在默认值里的哨兵字符。直接用<>比较时,只要有一边是 NULL,结果就是 NULL 而不是 true,默认值从「无」变成「有」这种变更会被漏掉。HAVING里直接用变更类型这个别名,MySQL 允许在HAVING中引用SELECT别名,WHERE里不行,所以过滤条件只能放这儿。
-- 第二步:只在旧版存在的表和字段——删表删列,升级时最危险 SELECT v7.table_name AS 表名, v7.column_name AS 字段, '仅旧版存在' AS 标记 FROM (SELECT table_name, column_name FROM information_schema.columns WHERE table_schema = 'schema_v7') v7 LEFT JOIN (SELECT table_name, column_name FROM information_schema.columns WHERE table_schema = 'schema_v8') v8 ON v7.table_name = v8.table_name AND v7.column_name = v8.column_name WHERE v8.column_name IS NULL ORDER BY 表名, 字段;拿到结果别急着改代码,先按变更类型分风险等级。经验上风险排序是:删列 > 类型收窄 > 新增非空无默认 > 索引删除 > 可空性放宽。
| 变更类型 | 典型风险 | 上线前必做 |
|---|---|---|
| 删除列 | 老代码显式引用或SELECT *后按位置取值,直接崩 | 全量搜代码和报表 SQL,确认无人引用 |
varchar(255)收窄到varchar(64) | 存量数据截断或写入报错 | 先跑长度分布统计,确认最大值 |
| 新增非空无默认列 | 存量写入语句不兼容,插入失败 | 补默认值或改为可空,分两步上线 |
| 索引删除 | 慢查询,压测才发现 | 对比压测 QPS 和慢日志 |
字符集utf8升utf8mb4 | 索引长度超 767 字节限制 | 检查前缀索引长度,必要时重建索引 |
那以后每次拿到新版表结构包,我都会先跑这两条 diff SQL 出一份变更清单,再动手改任何一行查询。血泪经验是:一次升级里看着最无害的「把varchar(255)改成varchar(64)」,往往就是上线当天下午最先炸的那个。希望帮到你。
本文还有配套的精品资源,点击获取