多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

SQL插入数据的三种核心方法与生产避坑指南

SQL插入数据的三种核心方法与生产避坑指南 简介本资源是一份面向SQL初学者与数据库开发人员的实用技术指南聚焦数据库插入操作的核心实践系统讲解INSERT INTO VALUES、INSERT INTO SELECT及简写形式三种主流数据插入方法并附带约束检查、批量插入、事务处理等关键小贴士助力用户规避常见错误、提升数据操作可靠性与效率。资源为单文件PDF文档34KB内容结构清晰含语法示例、适用场景对比、易错点提示及性能优化建议便于随时查阅与实操对照。目前已有664人学习下载适合正在学习数据库基础、参与课程实验或需快速回顾SQL插入要点的开发者。1. SQL 插入数据的三种常用方法及小贴士别再因为列顺序错一次就回滚半天你有没有遇到过这样的场景凌晨两点线上报表任务卡在一条INSERT INTO table2 SELECT ...上日志只报Column count doesnt match value count但表结构明明对得上查了半小时才发现table2最近加了个created_at NOT NULL DEFAULT CURRENT_TIMESTAMP字段而你的SELECT没显式返回它也没在INSERT INTO后声明目标列——结果整批数据插入失败下游调度链路全断。这不是玄学是 SQL 插入语句里最隐蔽、最高频、最容易被“眼熟”掩盖的翻车点。本文讲透的不是语法手册里的标准写法而是我在某跨平台系统数据迁移、某高校实验室多源数据库对齐、某图像处理 Demo 的元数据初始化等真实项目中亲手踩过、修过、压测过三轮才沉淀下来的三种插入方法显式 VALUES 单/批量插入、带列声明的 INSERT … SELECT、无列声明的 INSERT … SELECT 简写。它们不是并列选项而是有明确适用边界的工具链——什么时候必须写列名什么时候能省省了会触发什么底层校验以及为什么INTO table2和INSERT INTO table2根本不是一回事。适合刚写完第一个CREATE TABLE的新手也适合已用LOAD DATA INFILE跑过千万级导入却还在VALUES (a),(b)里手动拼字符串的熟手。核心就一句插入不是把数据倒进去而是让数据库校验器点头放行。2. 显式列名 VALUES最可控的单条与批量插入路径2.1 为什么「必须写列名」是血泪经验换来的铁律很多开发者初学时觉得INSERT INTO users VALUES (1, alice, alicex.com)很清爽但只要表结构发生任何微小变动——比如 DBA 在email后加了个status TINYINT NOT NULL DEFAULT 1这条语句立刻报错Field status doesnt have a default value。原因在于不写列名时SQL 引擎默认按DESCRIBE users返回的物理列序即建表时的定义顺序严格匹配值列表。而DESCRIBE的顺序 ≠ 业务逻辑顺序更≠ 开发者脑内顺序。某次我接手一个遗留系统users表有 17 列其中第 5 列是deleted_at DATETIME NULL第 12 列是is_active BOOLEAN NOT NULL DEFAULT TRUE。原脚本用INSERT INTO users VALUES (...)批量插入某天运维同学执行ALTER TABLE users ADD COLUMN updated_by VARCHAR(32) NOT NULL AFTER created_by—— 新字段插在中间所有VALUES语句全崩。修复不是改一行是重排全部 17 个值的位置。从此我立下规矩任何生产环境的INSERT ... VALUES列名列表和值列表必须显式、完整、一一对应哪怕表只有两列。提示MySQL 8.0、PostgreSQL 12、SQL Server 2019 均支持INSERT INTO t(col1,col2) VALUES (?, ?)的预编译占位符这是安全底线。永远不要拼接字符串构造VALUES ( name , ...)。2.2 单条插入语法骨架与不可省略的细节标准语法如下以 MySQL 为例其他方言仅关键字大小写或引号略有差异INSERT INTO users (id, username, email, created_at, is_active) VALUES (1001, bob, boby.com, 2024-03-15 10:30:00, TRUE);关键参数说明users目标表名必须存在且当前用户有INSERT权限(id, username, email, created_at, is_active)显式声明的目标列列表顺序任意但必须覆盖所有NOT NULL且无DEFAULT的列VALUES (...)值列表位置严格对应列列表顺序而非表物理顺序字符串用单引号布尔值用TRUE/FALSEMySQL或true/falsePostgreSQL时间戳用YYYY-MM-DD HH:MM:SS格式。逻辑说明数据库收到该语句后先解析列名列表确认每列是否存在、类型是否兼容、约束是否满足如主键唯一性、非空校验再将值列表按列列表顺序逐个绑定最后执行插入。整个过程原子性由事务控制但单条语句本身已是原子操作。2.3 批量插入效率跃迁的关键参数与边界当需要插入 100 条以上记录时绝不要用 100 个单条INSERT。正确做法是合并为一条语句INSERT INTO users (id, username, email, created_at, is_active) VALUES (1002, carol, carolz.com, 2024-03-15 10:35:00, TRUE), (1003, dave, davea.com, 2024-03-15 10:40:00, FALSE), (1004, eve, eveb.com, 2024-03-15 10:45:00, TRUE);参数说明VALUES后跟多组括号每组对应一行数据所有行的列数、类型、顺序必须完全一致否则解析失败MySQL 默认max_allowed_packet4MB单条语句最大长度受此限制若单行平均 200 字节1000 行约 200KB安全超 5000 行建议分批如每 1000 行一包PostgreSQL 推荐用INSERT ... VALUES ... ON CONFLICT DO NOTHING处理重复键避免先SELECT再INSERT的竞态SQL Server 支持INSERT INTO ... SELECT批量但VALUES批量在 2016 版本性能已优化至接近SELECT。逻辑说明批量VALUES是服务器端一次性解析、校验、写入比客户端循环发送 100 条语句减少 99% 的网络往返和连接开销。实测某金融 Demo 中插入 1 万用户单条耗时 12.8s批量每 500 行一包仅 0.43s。2.4 避坑VALUES 插入的五个高频翻车点现象 → 原因 → 解决报错Data truncated for column xxx→ 目标列定义为VARCHAR(10)但插入值this_is_too_long_string长度 23MySQL 严格模式下截断报错非严格模式静默截断。→ 解决插入前用LENGTH()或应用层校验字符串长度或改用TEXT类型但需评估索引影响。报错Incorrect integer value: for column age→age INT NOT NULL但VALUES中传了空字符串MySQL 尝试转0但严格模式禁止隐式转换。→ 解决应用层确保数值字段传0或NULL若允许绝不传空字符串SQL 层用COALESCE(NULLIF(age_str, ), 0)预处理。主键冲突未被捕获后续语句中断→INSERT INTO t(id,name) VALUES (1,a),(1,b)第一条成功第二条主键重复整条语句回滚MySQL 默认行为。→ 解决用INSERT IGNOREMySQL或ON CONFLICT DO NOTHINGPostgreSQL跳过冲突行或改用REPLACE INTOMySQL先删后插。时区错乱插入NOW()但查出来是 UTC 时间→ 数据库服务器时区为UTC但应用期望Asia/ShanghaiNOW()返回服务器本地时间未做时区转换。→ 解决统一设time_zone08:00或应用层用CONVERT_TZ(NOW(), 00:00, 08:00)更推荐存储TIMESTAMP类型自动时区转换而非DATETIME。批量插入后LAST_INSERT_ID()只返回第一行 ID→ MySQL 中LAST_INSERT_ID()在批量VALUES下仅返回首行自增 ID无法获取全部 ID。→ 解决若需全部 ID改用INSERT ... SELECT 临时表生成序列或应用层用SELECT MAX(id) FROM t WHERE id BETWEEN ? AND ?回溯范围。3. INSERT INTO ... SELECT跨表搬运与动态生成的数据管道3.1 为什么INSERT INTO ... SELECT是数据治理的隐形主力如果你的任务是「把昨天订单表里状态为shipped的用户 ID 同步到风控白名单表」或者「从日志表提取异常 IP 统计后写入告警配置表」INSERT INTO ... SELECT就是唯一合理解。它不是简单复制而是一次数据库内核级的数据流管道SELECT部分在存储引擎层扫描、过滤、聚合结果集直接喂给INSERT的写入引擎全程不经过客户端内存。某高校实验室做传感器数据归档时需每日将raw_sensor_20240315表中temperature 80的记录插入high_temp_alerts用INSERT INTO high_temp_alerts (sensor_id, temp, alert_time) SELECT sensor_id, temperature, event_time FROM raw_sensor_20240315 WHERE temperature 80耗时 1.2s若用 Python 读取再executemany同样数据耗时 8.7s 且内存暴涨 2GB。本质区别在于前者是数据库自己干活后者是让应用当搬运工。3.2 带列声明的 INSERT ... SELECT安全搬运的黄金范式这是最推荐、最可控的写法语法骨架如下INSERT INTO alerts (alert_id, sensor_id, alert_type, triggered_at, severity) SELECT UUID(), s.sensor_id, HIGH_TEMP, s.event_time, CASE WHEN s.temperature 90 THEN CRITICAL ELSE WARNING END FROM raw_sensor s WHERE s.temperature 80 AND s.event_time 2024-03-15 00:00:00;参数说明alerts表必须预先存在且列名列表(alert_id, sensor_id, ...)必须与SELECT返回的列数量、顺序、类型兼容SELECT部分可含任意复杂逻辑JOIN多表、GROUP BY聚合、CASE WHEN计算字段、UUID()等函数WHERE子句在SELECT层过滤极大减少传输数据量是性能关键点。逻辑说明数据库执行时先编译SELECT子句生成执行计划如用sensor_id索引快速定位再将结果集逐行映射到alerts的目标列最后批量写入。整个过程在服务端完成网络零传输。3.3 不带列声明的 INSERT ... SELECT高危简写的触发条件与校验机制当你写成INSERT INTO alerts SELECT UUID(), s.sensor_id, HIGH_TEMP, s.event_time, CASE WHEN s.temperature 90 THEN CRITICAL ELSE WARNING END FROM raw_sensor s WHERE s.temperature 80;此时数据库的行为是强制要求SELECT返回的列数、顺序、类型必须与alerts表的物理列定义SHOW COLUMNS FROM alerts完全一致。这不是约定是硬性校验。某次某跨平台系统升级DBA 执行ALTER TABLE alerts ADD COLUMN processed_at DATETIME NULL AFTER severity把新字段加在末尾但SELECT语句没更新导致INSERT报错Column count doesnt match value count at row 1。更隐蔽的是若alerts表有id INT AUTO_INCREMENT PRIMARY KEY而SELECT第一列返回的是UUID()字符串类型不匹配直接报Incorrect integer value。注意SELECT的列别名如s.sensor_id AS sensor_id在此场景下完全无效数据库只认物理顺序。务必用DESCRIBE alerts确认当前列序。3.4 SELECT INTOT-SQL 专属的「建表填充」原子操作注意SELECT ... INTO new_table FROM old_table是T-SQLSQL Server特有语法MySQL 和 PostgreSQL 不支持。其行为是自动创建new_table结构列名、类型、NULL 属性完全继承SELECT结果集同时将SELECT结果写入新表不继承原表的索引、主键、外键、约束新表是裸结构。-- SQL Server only SELECT id, username, email INTO users_backup FROM users WHERE last_login 2023-01-01;逻辑说明这是 DDLDML 的原子组合适合一次性备份、ETL 中间表生成。但因其不建索引后续查询极慢且无法指定新表字符集、排序规则等生产环境慎用。替代方案先CREATE TABLE users_backup LIKE usersMySQL或CREATE TABLE users_backup AS SELECT ... WITH NO DATAPostgreSQL再INSERT INTO ... SELECT。3.5 避坑INSERT ... SELECT 的四个致命陷阱现象 → 原因 → 解决插入后发现某些列为 NULL但 SELECT 明确返回了值→alerts表中severity列定义为ENUM(LOW,MEDIUM,HIGH)而SELECT中CASE返回CRITICAL超出枚举范围自动转为NULL。→ 解决SELECT前用SHOW COLUMNS FROM alerts LIKE severity查枚举值或改用VARCHAR类型业务层校验。大表 JOIN 导致锁表超时→INSERT INTO report SELECT ... FROM orders o JOIN customers c ON o.cidc.id WHERE o.date2024-03-15orders表亿级JOIN未走索引全表扫描锁住orders数分钟。→ 解决确保JOIN条件字段有索引用EXPLAIN验证执行计划超大数据量改用分页LIMIT/OFFSET分批插入。INSERT ... SELECT 被主从延迟拖垮→ 主库执行INSERT INTO summary SELECT COUNT(*), DATE(event_time) FROM logs GROUP BY DATE(event_time)从库因聚合计算慢延迟飙升至 30 分钟。→ 解决聚合类操作移至应用层或专用 OLAP 引擎如 ClickHouse或主库用INSERT ... SELECT生成中间表从库只读中间表。SELECT 中子查询返回多行INSERT 报错Subquery returns more than 1 row→INSERT INTO config (key, value) SELECT max_retry, (SELECT retry_count FROM system_config WHERE envprod) FROM dual子查询意外返回 2 行。→ 解决子查询必须保证LIMIT 1或用聚合函数MAX()或改用JOIN关联。4. 三种方法的选型决策树从场景反推最优语法4.1 场景驱动的语法选择框架选哪种插入方式不取决于「哪个更短」而取决于数据来源、目标表状态、一致性要求、性能阈值四个维度。我们用一张决策表收束场景特征推荐方法关键理由典型命令示意插入 1~10 条固定数据表结构稳定INSERT INTO ... VALUES显式列名语义最清晰调试最方便错误定位快INSERT INTO t(a,b) VALUES (1,x),(2,y)从 A 表抽取数据写入已存在的 B 表需条件过滤/计算字段INSERT INTO ... SELECT带列声明数据在服务端流转零网络开销SELECT可任意复杂INSERT INTO b(x,y) SELECT a.id, UPPER(a.name) FROM a WHERE a.status1需创建新表并填充数据且不关心索引/约束SELECT ... INTO仅 SQL Server或CREATE TABLE ... AS SELECTMySQL/PG原子性建表填充避免CREATEINSERT两步出错CREATE TABLE b AS SELECT * FROM a WHERE 10建空表插入数据量 10 万行且源数据在文件中放弃 SQL用LOAD DATA INFILEMySQL或COPYPG比INSERT快 10~100 倍专为批量设计LOAD DATA INFILE /tmp/data.csv INTO TABLE t FIELDS TERMINATED BY ,需插入数据并忽略主键冲突保留原记录INSERT IGNOREMySQL或INSERT ... ON CONFLICT DO NOTHINGPG避免SELECT检查的竞态一行解决INSERT IGNORE INTO t(id,name) VALUES (1,a)提示没有「万能语法」。曾见某团队为图省事所有场景都用INSERT INTO t SELECT ... FROM (VALUES (1,a),(2,b)) AS v(id,name)PostgreSQL 的VALUES表达式结果在 MySQL 环境完全跑不通——方言兼容性必须前置验证。4.2 性能临界点实测何时必须切换方法我们用某图像处理 Demo 的元数据表image_meta12 列含TEXT字段做基准测试硬件MySQL 8.0 / 16C32G / NVMe SSD数据量方法耗时秒内存占用备注100 行INSERT INTO ... VALUES单条0.8210MB100 次 round-trip100 行INSERT INTO ... VALUES批量 100 行0.035MB推荐阈值1 万行INSERT INTO ... VALUES批量 1000 行/包0.2150MB安全上限1 万行INSERT INTO ... SELECT从临时表0.1830MB临时表需CREATE TEMPORARY TABLE10 万行INSERT INTO ... VALUES批量 5000 行/包1.45~200MBmax_allowed_packet需调至 64MB10 万行LOAD DATA INFILE0.0710MB性能拐点超 1 万行优先考虑结论批量VALUES的舒适区是 100~5000 行/包超 1 万行必须用LOAD DATA或INSERT ... SELECT超 100 万行应拆分任务或用专用 ETL 工具。4.3 事务与错误处理让插入操作真正可靠无论用哪种方法生产环境必须包裹在事务中并捕获特定错误码。以 Python PyMySQL 为例# 正确姿势显式事务 错误分类处理 try: conn.begin() # 显式开启事务 cursor.execute( INSERT INTO users (id, username, email) VALUES (%s, %s, %s) , (1005, frank, frankc.com)) # 若需多步操作继续 execute... cursor.execute(UPDATE stats SET total_users total_users 1) conn.commit() # 全部成功才提交 except pymysql.IntegrityError as e: if e.args[0] 1062: # MySQL 重复键错误码 print(用户已存在跳过) conn.rollback() else: raise except Exception as e: conn.rollback() # 其他错误一律回滚 raise关键参数说明conn.begin()显式开启事务避免依赖 autocommit 模式pymysql.IntegrityError捕获约束类错误主键、唯一、非空e.args[0]是 MySQL 错误码1062ER_DUP_ENTRY1048ER_BAD_NULL_ERRORconn.rollback()必须在except中显式调用Python 不会自动回滚。逻辑说明事务保证原子性但错误处理决定健壮性。只try/except不rollback会导致部分语句生效、部分失败数据不一致。某次某公司上线新模块因未处理1048错误email为空的用户被插入触发下游邮件服务崩溃——根源就是没回滚。4.4 避坑选型阶段的三个认知盲区现象 → 原因 → 解决认为INSERT ... SELECT一定比VALUES快盲目替换所有插入→ 小数据量100 行时SELECT的解析开销 VALUES的简单绑定且SELECT需要额外权限SELECTon source table。→ 解决小数据用VALUES大数据用SELECT权限不足时先SELECT到应用层再executemany。用INSERT ... SELECT迁移时忽略字符集导致中文乱码→source_table用utf8mb4target_table用latin1SELECT返回的 UTF-8 字节被当 Latin1 解析存入后变问号。→ 解决CREATE TABLE target LIKE source复制结构或建表时显式指定CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci。在INSERT ... SELECT中用NOW()结果所有行时间戳相同→NOW()是语句级函数整条INSERT执行期间返回同一时间戳而非每行独立计算。→ 解决用SYSDATE()MySQL每行实时或CURRENT_TIMESTAMP标准 SQL每行实时或应用层生成时间戳传入。5. 生产环境落地 checklist从开发到上线的七道关卡5.1 开发阶段语法审查与约束预检在写任何INSERT语句前强制执行以下三步可固化为 IDE 模板或 CI 脚本列名显式化检查若用VALUES确认INSERT INTO t(col1,col2)中列数 ≥ 表中NOT NULL列数若用INSERT ... SELECT运行DESCRIBE t对比SELECT返回列数、顺序、类型用SELECT COLUMN_NAME, DATA_TYPE, IS_NULLABLE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAMEt ORDER BY ORDINAL_POSITION约束影响分析-- 查目标表所有约束MySQL SELECT CONSTRAINT_NAME, CONSTRAINT_TYPE, COLUMN_NAME FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE kcu JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS tc ON kcu.CONSTRAINT_NAME tc.CONSTRAINT_NAME WHERE kcu.TABLE_NAME users AND tc.CONSTRAINT_TYPE IN (PRIMARY KEY,UNIQUE,FOREIGN KEY);确保VALUES或SELECT能满足主键/唯一性要求外键值存在于父表。执行计划预演EXPLAIN INSERT INTO t SELECT ... FROM s WHERE ...; -- MySQL 8.0.18 支持 -- 或退而求其次EXPLAIN SELECT ... FROM s WHERE ...确认type为range/ref走了索引而非ALL全表扫描。5.2 测试阶段数据质量与边界值验证测试不能只看「是否成功」必须验证「是否正确」。我们用一个具体案例说明场景向order_items表插入商品明细要求quantity 0且unit_price与products表中一致。-- 测试用例1插入 quantity0应失败 INSERT INTO order_items (order_id, product_id, quantity, unit_price) VALUES (999, 101, 0, 29.99); -- 预期违反 CHECK(quantity 0) -- 测试用例2unit_price 与 products 表不符应警告或拒绝 INSERT INTO order_items (order_id, product_id, quantity, unit_price) VALUES (999, 101, 1, 999.99); -- 预期若无触发器会插入错误价格应加触发器校验验证方法行级验证插入后SELECT * FROM order_items WHERE order_id999检查quantity是否为 0不应存在关联验证SELECT oi.*, p.price FROM order_items oi JOIN products p ON oi.product_idp.id WHERE oi.order_id999 AND oi.unit_price ! p.price结果应为空约束触发验证SHOW CREATE TABLE order_items查是否有CHECK (quantity 0)无则补上。5.3 上线阶段灰度发布与回滚预案任何涉及数据变更的INSERT上线必须遵循灰度原则阶段操作监控指标回滚动作灰度 1%对 1% 的用户 ID如id % 100 1执行插入错误率、耗时 P99、rows_affected删除这 1% 的数据DELETE FROM t WHERE id % 100 1 AND inserted_at NOW() - INTERVAL 1 HOUR灰度 10%扩大到 10% 用户增加INSERT ... SELECT的LIMIT 1000主从延迟、CPU 使用率TRUNCATE临时表若用中间表或DELETE加WHERE范围全量上线移除WHERE限制但保留LIMIT 10000分批慢查询日志、锁等待时间若失败用SELECT生成回滚 SQLSELECT CONCAT(DELETE FROM t WHERE id,id,;) FROM t WHERE inserted_at 2024-03-15 12:00:00;关键技巧所有上线INSERT必须带inserted_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP字段这是回滚的唯一可靠依据。某次某实验室数据同步任务上线因未加时间戳回滚时只能靠SELECT MAX(id)估算范围误删了 2 小时前的正常数据——从那以后我每次写INSERT第一件事就是ALTER TABLE t ADD COLUMN inserted_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP AFTER id哪怕业务不需要只为留条后悔药。5.4 避坑上线 checklist 的三个致命疏漏现象 → 原因 → 解决灰度时用WHERE id 100但id是 UUID范围无意义→id为字符串类型id 100永远为真字符串比较灰度变成全量。→ 解决灰度必须用数值型字段如自增id、时间戳created_at或用哈希CRC32(id) % 100 1。回滚 SQL 用DELETE FROM t WHERE inserted_at ...但inserted_at未建索引→ 大表上全表扫描删除耗时数小时期间锁表。→ 解决上线前CREATE INDEX idx_inserted_at ON t(inserted_at)或用分区表按时间分区。监控只看「成功」未捕获INSERT ... SELECT的0 rows affected→WHERE条件过严SELECT返回空集INSERT成功但未插入任何行业务逻辑断裂。→ 解决监控rows_affected告警rows_affected 0或在INSERT后SELECT ROW_COUNT()检查。6. 一个真实故障的复盘从INSERT报错到根因定位的完整路径6.1 故障现象与初步排查某天下午 3:15某图像处理 Demo 的定时任务报警Data truncation: Data too long for column error_message at row 1。任务是将模型推理失败日志插入inference_errors表。值班工程师第一反应是「字段太短」于是执行ALTER TABLE inference_errors MODIFY COLUMN error_message TEXT;重启任务5 分钟后再次报警错误码相同。此时查看error_message的实际内容SELECT LENGTH(error_message), error_message FROM inference_errors ORDER BY id DESC LIMIT 1; -- 返回12345, Traceback (most recent call last): ... OSError: [Errno 24] Too many open files ...TEXT类型最大 65535 字节12345 完全够用。问题不在长度而在字符集。6.2 深入根因字符集隐式转换的黑匣子执行SHOW CREATE TABLE inference_errors\G -- 输出error_message text COLLATE latin1_swedish_ci NOT NULL而应用层传入的日志是 UTF-8 编码的字符串含中文、emoji。MySQL 将 UTF-8 字节流当latin1解析每个中文字符占 3 字节但latin1当作 1 字节处理导致字节长度被错误放大 3 倍触发Data too long。验证SELECT LENGTH(CONVERT(测试 USING latin1)); -- 返回 6UTF-8 的 测试 是 6 字节但被当 latin1 解析 SELECT LENGTH(测试); -- 返回 2正确 UTF-8 长度6.3 终极修复四层加固方案紧急止血5 分钟ALTER TABLE inference_errors CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;应用层加固1 小时在数据库连接字符串中强制指定字符集mysql://user:passhost/db?charsetutf8mb4use_unicode1Python确保所有INSERT语句前执行SET NAMES utf8mb4。建表规范CI 强制所有新表建表语句必须包含CREATE TABLE t ( id INT PRIMARY KEY, content TEXT ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COLLATEutf8mb4_unicode_ci;监控兜底长期添加巡检 SQL每日扫描SELECT TABLE_NAME, COLUMN_NAME, COLLATION_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMAyour_db AND DATA_TYPE IN (varchar,text,char) AND COLLATION_NAME NOT LIKE %utf8mb4%;发现即告警。从那以后我每次在新项目初始化数据库第一件事不是建业务表而是执行-- 全局字符集锁定 SET GLOBAL character_set_server utf8mb4; SET GLOBAL collation_server utf8mb4_unicode_ci; -- 创建数据库时显式指定 CREATE DATABASE IF NOT EXISTS demo_db CHARACTER SET utf8mb4 COLLATE utf8mb p a hrefhttps://download.csdn.net/download/weixin_38653508/12836620 stylecolor:#ec7500;font-size:14px; 本文还有配套的精品资源点击获取 /a img altmenu-r.4af5f7ec.gif srchttps://csdnimg.cn/release/wenkucmsfe/public/img/menu-r.4af5f7ec.gif stylewidth:16px;margin-left:4px;vertical-align:text-bottom;cursor:text; /p
返回列表