mysql的create table if not exists不支持动态表名,必须用prepare+execute;安全生成分表名需用date_format(?, '%y%m')并校验日期合法性,建表前须查information_schema防止重复。

为什么不能直接用 CREATE TABLE IF NOT EXISTS 动态拼表名
MySQL 的 DDL 语句(如 CREATE TABLE)在存储过程中不支持变量直接插入选项,比如写成 CREATE TABLE tbl_202401 (...) 中的 tbl_202401 不能是拼接的字符串变量。直接写会报错:ERROR 1064 (42000): You have an error in your SQL syntax。必须用 PREPARE + EXECUTE 才能动态执行建表语句。
如何安全生成带年月后缀的分表名
关键在于用 DATE_FORMAT(NOW(), '%Y%m') 或传入日期参数来构造规范表名,避免手动拼接出错(比如把 2024-1-5 格式化成 202415)。建议统一用 DATE_FORMAT(?, '%Y%m'),并校验输入是否为合法日期。
- 推荐用
CONCAT('log_', DATE_FORMAT(NOW(), '%Y%m'))生成类似log_202407的表名 - 如果按业务日期而非当前时间建表,存储过程应接收
IN p_date DATE参数,并用DATE_FORMAT(p_date, '%Y%m') - 表名必须符合 MySQL 标识符规则:不能含斜杠、空格、特殊符号;长度建议 ≤ 64 字符
存储过程里建表前必须检查表是否存在
重复执行建表会报错 ERROR 1050 (42S01): Table 'xxx' already exists,但 IF NOT EXISTS 在 PREPARE 中无效,所以得先查 information_schema.tables。
- 用
SELECT COUNT(*) INTO @cnt FROM information_schema.tables WHERE table_schema = DATABASE() AND table_name = @tbl_name - 只在
@cnt = 0时才PREPARE建表语句,否则跳过 - 注意:
information_schema查询在某些低版本 MySQL(如 5.6)可能有缓存延迟,但对建表这种低频操作影响不大
实际可用的最小可行存储过程示例
以下是一个可直接创建、带防重逻辑的分表存储过程,适用于日志类按月分表场景:
DELIMITER $$
CREATE PROCEDURE sp_create_monthly_table()
BEGIN
DECLARE tbl_name VARCHAR(64);
DECLARE stmt TEXT;
DECLARE cnt INT DEFAULT 0;
SET tbl_name = CONCAT('log_', DATE_FORMAT(NOW(), '%Y%m'));
SELECT COUNT(*) INTO cnt
FROM information_schema.tables
WHERE table_schema = DATABASE()
AND table_name = tbl_name;
IF cnt = 0 THEN
SET stmt = CONCAT(
'CREATE TABLE `', tbl_name, '` (',
'id BIGINT PRIMARY KEY AUTO_INCREMENT, ',
'content TEXT, ',
'created_at DATETIME DEFAULT CURRENT_TIMESTAMP',
') ENGINE=InnoDB DEFAULT CHARSET=utf8mb4'
);
SET @sql = stmt;
PREPARE p FROM @sql;
EXECUTE p;
DEALLOCATE PREPARE p;
END IF;
END$$
DELIMITER ;
调用:CALL sp_create_monthly_table();。注意:该过程默认用当前月份;若需指定月份,改用 IN p_month CHAR(6) 参数并替换 DATE_FORMAT 部分即可。
容易被忽略的是权限——执行存储过程的用户必须有 CREATE 权限,且不能是 DEFINER 指向无权用户。另外,sql_mode 若含 STRICT_TRANS_TABLES,字段定义不严谨也会导致建表失败。











