select into 会创建没有主键、索引和约束的新表,仅根据查询结果隐式建表,继承列名、数据类型和null性,但不继承源表的主键、外键、默认值、标识属性、check约束等元数据。

SELECT INTO 会创建没有主键、索引和约束的新表
SELECT INTO 的本质是「复制数据 + 隐式建表」,它只根据查询结果列的名称、数据类型和 NULL 性生成目标表结构,完全忽略源表的主键、外键、默认值、计算列、标识属性(除非显式用 IDENTITY() 函数)、CHECK 约束等。这意味着:如果你指望它做“完整备份”,结果会漏掉关键元数据。
常见错误现象:SELECT INTO backup_table FROM original_table 执行后,backup_table 看似一样,但 sp_help backup_table 一查,发现没有主键、没有索引、所有列都允许 NULL(哪怕原表是 NOT NULL)——这是因为 SELECT INTO 默认不继承 NOT NULL 属性,除非源列是计算列或有表达式参与。
- 使用场景:适合快速导出快照、ETL 中间表、临时分析表,不适合替代
CREATE TABLE + INSERT做结构一致备份 - 若需保留
NOT NULL,必须在SELECT列中显式用ISNULL(col, default_val)或CASE转换,否则目标列一律为可空 - 标识列不会自动继承:原表
id INT IDENTITY(1,1),目标表id只是普通INT;如需新表也有标识列,得写IDENTITY(INT, 1, 1) AS id
SELECT INTO 要求目标表不存在,且不能跨数据库直接指定完整三段名
SELECT INTO 语法强制要求目标表名不能预先存在,否则报错 Msg 2714, Level 16, State 6: There is already an object named 'xxx' in the database.。同时,虽然可以写 SELECT ... INTO otherdb.dbo.newtable,但前提是当前登录用户对 otherdb 有 db_owner 或至少 db_ddladmin 权限;否则会提示 Permission denied on database。
容易踩的坑:
- 误以为能覆盖已有表——不行,必须先
DROP TABLE IF EXISTS otherdb.dbo.newtable,再执行SELECT INTO - 跨库写成
SELECT ... INTO [otherdb]..[newtable](缺少dbo),SQL Server 会尝试在当前数据库的otherdb架构下建表,而非目标数据库,导致逻辑错误 - 在含只读文件组的数据库中执行
SELECT INTO,可能因无法分配空间而失败,需确认PRIMARY文件组可写
性能与日志影响:SELECT INTO 是最小化日志记录操作,但仅限于简单恢复模式
SELECT INTO 在简单(Simple)或大容量日志(Bulk-Logged)恢复模式下,以最小化日志方式写入数据,速度快、日志体积小;但在完整(Full)恢复模式下,它仍会完整记录每一行插入,日志增长剧烈,尤其处理千万级数据时可能撑爆日志文件。
实操建议:
- 执行前用
SELECT DATABASEPROPERTYEX('dbname', 'Recovery') AS recovery_model确认恢复模式 - 若在完整模式下做备份,优先改用
CREATE TABLE + INSERT INTO ... SELECT并配合TABLOCK提示(如INSERT INTO newtable WITH (TABLOCK) SELECT ...),它也能触发最小化日志(前提是表无索引且未启用行版本控制) -
SELECT INTO创建的表默认位于用户默认文件组,如需指定文件组,只能通过后续ALTER TABLE ... MOVE TO,不能在INTO语句中指定
真正可靠的表级备份应结合系统视图还原结构
如果目标是“结构+数据”双备份,SELECT INTO 远不够。更稳妥的做法是:先用 sys.columns、sys.indexes、sys.key_constraints 等视图生成建表脚本,再用 INSERT INTO ... SELECT 导入数据。SQL Server Management Studio(SSMS)右键表 → “生成脚本”功能底层就是调用这些视图。
一个容易被忽略的细节:即使你用 SELECT INTO 备份了数据,若源表后来加了新索引或约束,这个备份表永远不会自动同步——它从诞生起就和源表彻底解耦。所以别把它当“活备份”,只当一次性快照用。










