Setup
以下脚本创建了一个包含100个相同行的测试表,每个行包含一个包含足够数据的xml列,以确保将其存储在行外。在我的测试数据库中,每行生成的xml的长度为20204字节。
-- Conditional drop
IF OBJECT_ID(N'dbo.XMLTest', N'U') IS NOT NULL
DROP TABLE dbo.XMLTest;
GO
-- Create test table
CREATE TABLE dbo.XMLTest
(
ID integer IDENTITY PRIMARY KEY,
X xml NULL
);
GO
-- Add 100 wide xml rows
DECLARE @X xml;
SET @X =
(
SELECT TOP (100) *
FROM sys.columns AS C
FOR XML
PATH ('row'),
ROOT ('root'),
TYPE
);
INSERT dbo.XMLTest
(X)
SELECT TOP (100)
@X
FROM sys.columns AS C;
-- Flush dirty buffers
CHECKPOINT;
测试
以下三个测试读取了带有以下内容的xml列:
简单的
SELECT
语句将xml分配给变量
使用
SELECT INTO
创建临时表-- No row count messages or graphical plan
-- Show I/O statistics
SET NOCOUNT ON;
SET STATISTICS XML OFF;
SET STATISTICS IO ON;
GO
PRINT CHAR(10) + '=== Plain SELECT ===='
DBCC DROPCLEANBUFFERS WITH NO_INFOMSGS;
SELECT XT.X
FROM dbo.XMLTest AS XT;
GO
PRINT CHAR(10) + '=== Assign to a variable ===='
DBCC DROPCLEANBUFFERS WITH NO_INFOMSGS;
DECLARE @X xml;
SELECT
@X = XT.X
FROM dbo.XMLTest AS XT;
GO
PRINT CHAR(10) + '=== SELECT INTO ===='
IF OBJECT_ID(N'tempdb..#T', N'U') IS NOT NULL
DROP TABLE #T;
DBCC DROPCLEANBUFFERS WITH NO_INFOMSGS;
SELECT
XT.X
INTO #T
FROM dbo.XMLTest AS XT
GO
SET STATISTICS IO OFF;
结果
输出为:
=== Plain SELECT ==== Table 'XMLTest'. Scan count 1, logical reads 3, physical reads 1, read-ahead reads 0, lob logical reads 795, lob physical reads 37, lob read-ahead reads 796. === Assign to a variable ==== Table 'XMLTest'. Scan count 1, logical reads 3, physical reads 1, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. === SELECT INTO ==== Table 'XMLTest'. Scan count 1, logical reads 3, physical reads 1, read-ahead reads 0, lob logical reads 300, lob physical reads 37, lob read-ahead reads 400.
问题
>为什么LOB的读数有如此大的差异?
每个测试中肯定都读取了完全相同的数据吗?
#1 楼
并非所有读取均相等。 SQL Server知道访问LOB数据很昂贵,因此在可能的情况下尽量避免访问。在每种情况下,读取LOB数据的方式也有详细的区别:摘要
数字不同是因为: /> select读取数据包大小的块中的LOB
变量赋值测试根本不读取LOB
“ select into”测试将读取整页中的LOB
详细信息
普通
SELECT
聚簇索引扫描可以不读取任何LOB数据。它仅分配一个存储引擎LOB句柄。直到控制权返回到计划的根目录时才使用该句柄。逻辑读取计数页面被触摸的次数,因此:
报告的读取数量等于执行的分块读取数量,加上每次发生LOB页面转换时的计数。
例如:当进程触摸与流的当前位置相对应的页面时,将在每个块的开头对逻辑读取进行计数。在数据包小于数据库页面的情况下(通常情况),对同一页面进行几次逻辑读取。如果数据包大小太大,以至于整个LOB可以容纳一个块,那么报告的逻辑读取数就是LOB页数。
变量分配br />
聚簇索引扫描像以前一样分配一个LOB句柄。在计划的根部,将LOB句柄复制到变量。永远不会访问LOB数据本身(读取零个LOB),因为永远不会读取变量。即使存在,也只能通过最后分配的LOB句柄来进行。
由于从未访问过LOB数据,因此没有LOB读取。
该计划使用批量行集提供程序将LOB数据从源表复制到新表。它在每次读取时都处理完整的LOB页(不进行流式传输或分块)。
逻辑读取的数量与测试表中的LOB页的数量相对应。 br />