必须用PREPARE+EXECUTE动态建表,表名需用CONCAT结合DATE_FORMAT实时生成,且须先查information_schema确认表不存在,否则重复执行报错1050。

分表逻辑必须用日期函数动态计算,不能写死表名
MySQL存储过程里没法直接拼接表名后执行 CREATE TABLE,除非用 PREPARE + EXECUTE。但更关键的是:表名里的日期部分(比如 log_202409)得从输入参数或当前时间实时算出来,不能硬编码。否则每次调用都建同一张表,或者漏掉某个月。
- 用
DATE_FORMAT(NOW(), '%Y%m')获取当前年月,或用DATE_FORMAT(date_param, '%Y%m')基于传入的date_param DATE参数生成 - 表名拼接必须用
CONCAT(),比如CONCAT('log_', DATE_FORMAT(NOW(), '%Y%m')) - 别用字符串相加(
+),MySQL里那会转成数字加法,'log_' + '202409'结果是202409,表名直接丢掉前缀
用 PREPARE + EXECUTE 才能建动态表名的表
CREATE TABLE 不接受变量,所以必须走预处理语句。这是唯一可靠方式,绕不开。但要注意作用域和错误捕获——PREPARE 语句在存储过程中是局部的,退出过程就失效,不用手动释放;可一旦 SQL 拼错,错误信息只会报“Error Code: 1064”,不提示哪段拼错了。
- 先拼完整 SQL 字符串到变量,比如
SET @sql = CONCAT('CREATE TABLE ', @table_name, ' (id INT, dt DATE)'); - 再
PREPARE stmt FROM @sql;,然后EXECUTE stmt;,最后DEALLOCATE PREPARE stmt; - 如果建表字段要随日期变化(比如只存当月数据),字段定义也得动态拼,不能写死结构
判断表是否存在再建,否则重复执行会报错 1050
自动分表过程常被定时任务反复调用,比如每天凌晨跑一次。如果不检查,第二次就会遇到 ERROR 1050 (42S01): Table 'log_202409' already exists,整个过程中断。MySQL 没有内置的 CREATE TABLE IF NOT EXISTS ... LIKE 跨库/跨名支持,得自己查元数据。
- 查
information_schema.TABLES:SELECT COUNT(*) INTO @cnt FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = @table_name; - 用
IF @cnt = 0 THEN ... END IF;包住建表逻辑 - 别用
SHOW TABLES LIKE @table_name,它不支持变量,会报语法错误
分区表不是替代方案,别混淆“分表”和“分区”
有人看到“按日期分”第一反应是 PARTITION BY RANGE (TO_DAYS(dt)),但这和题目说的“自动分表”完全不是一回事。分区是单表内部切片,所有分区共用一个表名、一套结构、一个锁;而分表是物理上多个独立表,各自可单独备份、归档、授权。
- 分区表无法实现“每张表不同结构”或“按业务线分散到不同库”,分表可以
- 分区 DDL 操作(如
REORGANIZE PARTITION)会锁整个表,分表建新表不影响旧表读写 - 如果真要用分区,
ALTER TABLE ... ADD PARTITION同样需要PREPARE动态执行,且 MySQL 8.0+ 才支持在线添加范围分区
PREPARE 是唯一路径,以及忘了在建表前查 information_schema —— 这两个点一漏,过程看着能跑通,实际定时任务一跑就崩。


















