select into 仅sql server支持,mysql/postgresql不支持;跨库应使用create table ... as select(建表+插入)或insert into ... select(要求目标表已存在)。

SELECT INTO 在 SQL Server 中能用,但在 MySQL 和 PostgreSQL 里直接报错
如果你在 MySQL 或 PostgreSQL 里写了 SELECT INTO new_table FROM old_table,会立刻收到类似 ERROR 1064 或 syntax error at or near "INTO" 的提示。这不是你写错了,是语法根本不支持——SELECT INTO 是 SQL Server 和旧版 Access 的专属语法,其他主流数据库不认。
真正跨库通用、且语义清晰的做法是 CREATE TABLE ... AS SELECT(MySQL/PostgreSQL)或 INSERT INTO ... SELECT(全平台支持,但要求目标表已存在)。
- SQL Server:支持
SELECT INTO和INSERT INTO ... SELECT - MySQL:只支持
CREATE TABLE ... AS SELECT和INSERT INTO ... SELECT;SELECT INTO被保留给文件导出(如SELECT ... INTO OUTFILE) - PostgreSQL:同 MySQL,
SELECT INTO是创建临时表的旧写法,已不推荐;应优先用CREATE TABLE ... AS SELECT
CREATE TABLE ... AS SELECT 是最省事的“一键建表+灌数据”方案
这条语句在 MySQL 和 PostgreSQL 中一步完成建表和插入,字段名、类型、值全部来自查询结果。它不复制索引、主键、默认值等约束,只取数据快照。
例如把用户活跃度大于 100 的记录导出成新表:
CREATE TABLE active_users AS SELECT id, name, login_count FROM users WHERE login_count > 100;
- 新表字段类型由 SELECT 中表达式推断(比如
COUNT(*)得到BIGINT,字符串拼接可能变成TEXT) - 如果想控制字段类型或加注释,得先
CREATE TABLE,再用INSERT INTO ... SELECT - PostgreSQL 中可加
WITH NO DATA只建结构不插数据:CREATE TABLE backup_users AS SELECT * FROM users WITH NO DATA
INSERT INTO ... SELECT 要求目标表已存在,但能复用现有约束和索引
当你需要保留主键、外键、唯一索引或触发器时,必须先建好目标表,再用 INSERT INTO ... SELECT 填充数据。这是唯一能在所有主流数据库中稳定工作的方案。
示例(假设 archived_orders 已存在且结构兼容):
INSERT INTO archived_orders (order_id, customer_id, total, created_at) SELECT id, customer_id, amount, created_at FROM orders WHERE status = 'completed' AND created_at
- 字段数量和顺序必须严格匹配,不能靠名字自动对齐(MySQL 8.0+ 支持列名映射,但不建议依赖)
- 遇到类型不兼容(比如往
TINYINT插入超限数值),MySQL 默认截断并警告,PostgreSQL 直接报错中止 - 大表迁移时加
LIMIT分批执行(MySQL)或用游标(PostgreSQL),避免长事务锁表
SQL Server 的 SELECT INTO 看似方便,但隐含三个硬限制
即使你在 SQL Server 环境下,也别无脑用 SELECT INTO。它会在当前数据库中创建一个**新表**,且有不可绕过的行为:
- 新表默认没有主键、索引、约束,哪怕源表有
- 不能跨数据库执行:
SELECT INTO other_db.dbo.table FROM this_db.dbo.src会失败,除非用四部分命名 + 显式指定目标库上下文 - 事务日志开销大,且无法在显式事务中使用(
SELECT INTO是隐式 DDL,会自动提交当前事务)
真要迁移带结构的数据,不如用 INSERT INTO target_table SELECT ... 配合预先建好的空表——可控性强,还能加 WITH (TABLOCK) 提速。
字段类型推断有时很坑:比如 SELECT TOP 0 CAST(NULL AS VARCHAR(50)) AS col 生成的列可能是 varchar(1),实际插入时被截断。这种细节,不查执行计划或 sp_help 很难发现。










