Aqui estão três testes simples que leem os mesmos dados, mas relatam leituras lógicas muito diferentes:
Configurar
O script a seguir cria uma tabela de teste com 100 linhas idênticas, cada uma contendo uma coluna xml com dados suficientes para garantir que ela seja armazenada fora da linha. No meu banco de dados de teste, o comprimento do xml gerado é de 20.204 bytes para cada linha.
-- 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;
testes
Os três testes a seguir leem a coluna xml com:
SELECT
Uma declaração simples- Atribuindo o xml a uma variável
- Usando
SELECT INTO
para criar uma tabela temporária
-- 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;
Resultados
A saída é:
=== SELEÇÃO Simples ==== Tabela 'XMLTest'. Contagem de varredura 1, leituras lógicas 3, leituras físicas 1, leituras antecipadas 0, lob lógico lê 795, lob físico lê 37, lob read-ahead lê 796. === Atribuir a uma variável ==== Tabela 'XMLTest'. Contagem de varredura 1, leituras lógicas 3, leituras físicas 1, leituras antecipadas 0, lob leituras lógicas 0, lob leituras físicas 0, leitura antecipada de lob 0. === SELECIONE EM ==== Tabela 'XMLTest'. Contagem de varredura 1, leituras lógicas 3, leituras físicas 1, leituras antecipadas 0, lob lógico lê 300, lob físico lê 37, lob read-ahead lê 400.
Perguntas
- Por que as leituras de LOB são tão diferentes?
- Certamente os mesmos dados exatos foram lidos em cada teste?
Nem todas as leituras são iguais. O SQL Server sabe que o acesso a dados LOB é caro e tenta evitá-lo sempre que possível. Também existem diferenças detalhadas na forma como os dados LOB são lidos em cada caso:
Resumo
Os números são diferentes porque:
Detalhe
Avião
SELECT
O Clustered Index Scan não lê nenhum dado LOB. Ele apenas atribui um identificador de LOB do mecanismo de armazenamento . O identificador não é usado até que o controle retorne à raiz do plano.
O conteúdo LOB da linha atual é lido em blocos do tamanho do pacote TDS e transmitido para o cliente. As leituras lógicas contam o número de vezes que uma página é tocada, então:
O número de leituras relatadas é igual ao número de leituras em partes executadas, mais uma para cada vez que ocorre uma transição de página LOB.
Por exemplo: Uma leitura lógica é contada no início de cada bloco conforme o processo toca a página correspondente à posição atual do fluxo. Onde os pacotes são menores que uma página de banco de dados (o caso usual), várias leituras lógicas são contadas para a mesma página. Se o tamanho do pacote fosse tão grande que todo o LOB pudesse caber em um bloco, o número de leituras lógicas relatadas seria o número de páginas do LOB.
Atribuição variável
O Clustered Index Scan atribui um identificador LOB como antes. Na raiz do plano, o identificador LOB é copiado para a variável. Os próprios dados LOB nunca são acessados (zero leituras de LOB), porque a variável nunca é lida. Mesmo que fosse, seria apenas por meio do identificador LOB atribuído pela última vez.
Não há leituras LOB porque os dados LOB nunca são acessados.
SELECT INTO
Este plano usa o provedor de conjunto de linhas em massa para copiar os dados LOB da tabela de origem para a nova tabela. Ele processa uma página LOB completa em cada leitura (sem streaming ou fragmentação).
O número de leituras lógicas corresponde ao número de páginas LOB na tabela de teste.