所有数据库动态建表均需动态sql,mysql用prepare/execute+反引号+双写单引号+deallocate;sql server用sp_executesql+quotename;postgresql用format()区分%i/%l;oracle/db2用execute immediate但需注意权限与校验。

不能直接写 CREATE TABLE @table_name,所有数据库都报错,必须走动态 SQL 流程。
MySQL 必须用 PREPARE + EXECUTE,否则必报 ERROR 1064
MySQL 存储过程语法层禁止变量出现在标识符位置,CREATE TABLE @t 或 CREATE TABLE CONCAT('log_', '202610') 都会在解析阶段失败。唯一可行路径是拼字符串 → 预处理 → 执行 → 释放。
-
@sql必须是用户变量(SET @sql = ...),局部变量无法被PREPARE识别 - 表名必须用反引号包裹:
CONCAT('CREATE TABLE `', @tbl_name, '` (...)'),否则含横线或关键字(如order_202610)直接崩 - 字段默认值里的单引号要双写:
content TEXT DEFAULT '''hello''',否则 SQL 解析器提前截断 - 每次
EXECUTE后必须DEALLOCATE PREPARE stmt,否则第二次调用会因句柄冲突报 ERROR 1243 - 建表前务必查
information_schema.tables,避免重复触发 ERROR 1050
SQL Server 要用 sp_executesql,EXEC 不安全且不支持参数化值
SQL Server 允许拼接表名/列名进字符串,但结构部分(表名、列名)不能参数化,只有值部分(如默认值、CHECK 表达式中的字面量)可安全参数化。直接 EXEC('CREATE TABLE ' + @name + '...') 容易被注入,且无法传入参数值。
- 所有对象名必须经
QUOTENAME(@name)转义,否则含空格、中划线或关键字(如user_log)会报错 - 用
sp_executesql传参:例如默认值'pending'可作为@default参数传入,避免二次拼串 - 执行前查
sys.tables或sys.columns,防止重复建表或加列失败 - 不要在事务里建表后立刻查刚建的表——DDL 隐式提交,事务已结束
PostgreSQL 推荐 EXECUTE format(),%I 和 %L 必须分清
PostgreSQL 的 format() 是目前最安全的动态建表方式,它自动处理引号、转义和关键字包裹,但误用占位符会导致语法错误。比如把表名塞进 %L 会生成 'my_table',变成字符串字面量而非标识符。
- 表名、列名、约束名一律用
%I:format('CREATE TABLE %I (...)', table_name) - 字符串字面量(如默认值、CHECK 条件值)用
%L:format('... DEFAULT %L', 'active') - 数据类型等需拼成字符串的片段(如
VARCHAR(255))不能直接代入%s,得先确保是合法字符串再传 - 建表语句过长时,
format()对嵌套括号敏感,建议拆成多段format()拼接 - 同样需要
IF NOT EXISTS或查pg_tables防重
Oracle 和 Db2 用 EXECUTE IMMEDIATE,但 Oracle 要注意权限与角色
Oracle 存储过程中不能直接执行 DDL,但 EXECUTE IMMEDIATE 可绕过限制。不过它不自动继承调用者的角色权限,如果建表需 CREATE TABLE 权限,而该权限来自角色(非直接授予),就得先 SET ROLE,否则报 ORA-01031。
- Oracle 中建表语句直接拼接即可:
EXECUTE IMMEDIATE 'CREATE TABLE ' || table_name || ' (...)' - 若涉及用户输入,必须手动过滤或白名单校验,
EXECUTE IMMEDIATE不提供类似QUOTENAME或%I的安全包裹 - Db2 也支持
EXECUTE IMMEDIATE,但不能用于SELECT;建表前建议查SYSCAT.TABLES防重 - 所有数据库都需注意:DDL 隐式提交事务,建表后不能再回滚前面的 DML
真正麻烦的不是怎么拼 SQL,而是怎么让拼出来的 SQL 在任意输入下都不崩——表名含横线、字段叫 order、默认值带单引号、并发调用两次……这些边界情况漏一个,线上就挂。











