select into是sql server中创建新表并填充数据的最快方式,一次性完成建表与插入,支持最小化日志、不支持目标表已存在、不复制索引或约束。

SELECT INTO 是创建新表并填充数据的最快方式
直接用 SELECT INTO 一次性完成建表 + 插入,不走事务日志全量记录(SQL Server 中默认最小化日志),比先 CREATE TABLE 再 INSERT INTO SELECT 快得多。它本质是原子操作:先按查询列推导结构建表,再把结果集写进去。
常见错误现象:SELECT INTO 在目标表已存在时会报错 “There is already an object named 'xxx' in the database”,不是覆盖,而是拒绝执行。
- 目标表名必须不存在,不能加
IF NOT EXISTS判断后重试——语法不支持 - 不能跨数据库用
database.schema.table作为目标(SQL Server 限制),只能在当前库执行 - 如果想带主键或索引,
SELECT INTO不支持;得建完再ALTER TABLE加 - 标识列需显式用
IDENTITY(int,1,1)构造,例如:SELECT IDENTITY(int,1,1) AS id, name, email INTO new_users FROM users
INSERT INTO SELECT 要求目标表预先存在且结构兼容
这是往已有表里追加数据的标准做法,但字段数、顺序、类型必须严格对齐,否则立刻报错,比如 Column count doesn't match value count 或 Cannot insert the value NULL into column 'xxx'。
使用场景:ETL 增量同步、归档老数据、测试环境构造样本数据。
- 别偷懒写
INSERT INTO t2 SELECT * FROM t1——只要 t1 新增一列,语句就崩 - 务必显式列出目标列和源列:
INSERT INTO t2 (a,b,c) SELECT x,y,z FROM t1 - 目标表有
NOT NULL列但没在SELECT中提供值?要么补上,要么确保该列有默认值 - MySQL 下若开启
STRICT_TRANS_TABLES,字符串插数值列(如'abc'→INT)会直接报错;SQL Server 默认转成 0 或截断,行为更“宽容”但易埋隐患
跨库、跨服务器插入时权限和链接是硬门槛
比如想从 otherdb.dbo.users 查数据插进当前库的 staging.users,表面只差个前缀,实际卡点一堆。
SQL Server 中需启用 Ad Hoc Distributed Queries 并配置 sp_addlinkedserver;MySQL 需打开 federated 引擎或用 FEDERATED 表;Oracle 则依赖数据库链接(DBLINK)。
- 权限常被忽略:执行用户必须对源表有
SELECT,对目标表有INSERT,对链接服务器有CONNECT - 字符集不一致会导致中文变问号,尤其 SQL Server 和 MySQL 混用时,建议统一用
UTF8MB4或Latin1_General_CI_AS - 网络延迟高时,大结果集可能超时;可加
TOP N或分页OFFSET FETCH控制单次传输量
大批量插入时,性能差异主要来自日志和锁机制
同样插 100 万行,SELECT INTO 可能 3 秒完事,INSERT INTO SELECT 却要 40 秒——关键在是否启用最小化日志(bulk-logged)。
SQL Server 要求:恢复模式为 BULK_LOGGED 或 SIMPLE,目标表无索引/禁用索引,语句加 TABLOCK 提示;MySQL 则靠 DISABLE KEYS 和关闭唯一检查提速。
-
SELECT INTO天然支持最小化日志,但无法回滚;INSERT INTO SELECT默认完整日志,可回滚但慢 - 目标表有非聚集索引时,
INSERT INTO SELECT会逐行维护索引,开销陡增;SELECT INTO建完表再建索引,快得多 - 别在生产高峰跑全表
SELECT INTO——它会对源表加 Sch-S 锁,阻塞 DDL,但一般不影响 DML
最易被忽略的是隐式类型转换和空值传播:比如源列是 VARCHAR(50),目标是 VARCHAR(20),SQL Server 默认截断不报错,MySQL 可能报错或警告,而应用层根本收不到提示。上线前务必用小数据集验证字段兼容性,而不是只看“执行成功”。











