考虑以下简单的 MCVE:
SET STATISTICS IO, TIME OFF;
USE tempdb;
IF OBJECT_ID(N'tempdb..#t1', N'U') IS NOT NULL DROP TABLE #t1;
CREATE TABLE #t1
(
r int NOT NULL
);
IF OBJECT_ID(N'tempdb..##t1', N'U') IS NOT NULL DROP TABLE ##t1;
CREATE TABLE ##t1
(
r int NOT NULL
);
IF OBJECT_ID(N'dbo.s1', N'U') IS NOT NULL DROP TABLE dbo.s1;
CREATE TABLE dbo.s1
(
r int NOT NULL
PRIMARY KEY CLUSTERED
);
INSERT INTO dbo.s1 (r)
SELECT TOP(10000) ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM sys.syscolumns sc1
CROSS JOIN sys.syscolumns sc2;
GO
当我运行以下插入时,插入#t1
显示临时表没有统计 I/O。但是,插入##t1
确实会显示临时表的 stats I/O。
SET STATISTICS IO, TIME ON;
GO
INSERT INTO #t1 (r)
SELECT r
FROM dbo.s1;
统计输出:
SQL Server 解析和编译时间: CPU 时间 = 0 毫秒,经过时间 = 1 毫秒。 表's1'。扫描计数 1,逻辑读取 19,物理读取 0,预读读取 0,lob 逻辑读取 0,lob 物理读取 0,lob 预读读取 0。 SQL Server 执行时间: CPU 时间 = 16 毫秒,经过时间 = 9 毫秒。 (10000 行受影响)
INSERT INTO ##t1 (r)
SELECT r
FROM dbo.s1;
SQL Server 解析和编译时间: CPU 时间 = 0 毫秒,经过时间 = 1 毫秒。 表'##t1'。扫描计数 0,逻辑读取 10016,物理读取 0,预读读取 0,lob 逻辑读取 0,lob 物理读取 0,lob 预读读取 0。 表's1'。扫描计数 1,逻辑读取 19,物理读取 0,预读读取 0,lob 逻辑读取 0,lob 物理读取 0,lob 预读读取 0。 SQL Server 执行时间: CPU 时间 = 47 毫秒,经过时间 = 45 毫秒。 (10000 行受影响)
当我只插入时,为什么 ##temp 表上有这么多读取?
INSERT INTO
使用和全局临时表时未使用最小日志记录通过使用在全局临时表中插入一百万行
INSERT INTO
SELECT * FROM fn_dblog(NULL, NULL)
在执行上述查询时运行时,将返回约 1M 行。每行一个
LOP_INSERT_ROW
操作 + 其他日志数据。本地临时表上的相同插入
最多只能返回 700 行
SELECT * FROM fn_dblog(NULL, NULL)
最少的日志记录
通过使用在全局临时表中插入一百万行
SELECT INTO
SELECT INTO
具有 10k 条记录的全局临时表时间和 IO 统计
基于this blogpost,我们可以添加
TABLOCK
以启动堆表上的最小日志记录低逻辑读取
@PaulWhite 关于如何在临时表上实现最少日志记录的部分答案
创建一个常规表来测试这个:
用 1M 条记录填充它
此表上 >1M 逻辑读取
Paul White 的回答解释了全局临时表上报告的逻辑读取
结论
结论
INSERT INTO
是不能使用最少的日志记录,当与全局临时表/普通表结合使用时,导致在 tempdb 的日志文件中单独记录每个插入的行。而本地临时表/SELECT INTO
/INSERT INTO ... WITH(TABLOCK)
能够使用最少的日志记录。