Suponho que você tenha inclinado dados, que não deseja usar dicas de consulta para forçar o otimizador a fazer o que deve ser feito e que seja necessário obter um bom desempenho para todos os valores possíveis de entrada de @Id
. Você pode obter um plano de consulta garantido para exigir apenas algumas poucas leituras lógicas para qualquer valor de entrada possível, se estiver disposto a criar o seguinte par de índices (ou seu equivalente):
CREATE INDEX GetMinSomeTimestamp ON dbo.MyTable (Id, SomeTimestamp) WHERE SomeBit = 1;
CREATE INDEX GetMaxSomeInt ON dbo.MyTable (Id, SomeInt) WHERE SomeBit = 1;
Abaixo estão os meus dados de teste. Coloquei 13 M linhas na tabela e fiz com que metade delas tivesse um valor '3A35EA17-CE7E-4637-8319-4C517B6E48CA'
para a Id
coluna.
DROP TABLE IF EXISTS dbo.MyTable;
CREATE TABLE dbo.MyTable (
Id uniqueidentifier,
SomeTimestamp DATETIME2,
SomeInt INT,
SomeBit BIT,
FILLER VARCHAR(100)
);
INSERT INTO dbo.MyTable WITH (TABLOCK)
SELECT NEWID(), CURRENT_TIMESTAMP, 0, 1, REPLICATE('Z', 100)
FROM master..spt_values t1
CROSS JOIN master..spt_values t2;
INSERT INTO dbo.MyTable WITH (TABLOCK)
SELECT '3A35EA17-CE7E-4637-8319-4C517B6E48CA', CURRENT_TIMESTAMP, 0, 1, REPLICATE('Z', 100)
FROM master..spt_values t1
CROSS JOIN master..spt_values t2;
Esta consulta pode parecer um pouco estranha no começo:
DECLARE @Id UNIQUEIDENTIFIER = '3A35EA17-CE7E-4637-8319-4C517B6E48CA'
SELECT
@Id,
st.SomeTimestamp,
si.SomeInt
FROM (
SELECT TOP (1) SomeInt, Id
FROM dbo.MyTable
WHERE Id = @Id
AND SomeBit = 1
ORDER BY SomeInt DESC
) si
CROSS JOIN (
SELECT TOP (1) SomeTimestamp, Id
FROM dbo.MyTable
WHERE Id = @Id
AND SomeBit = 1
ORDER BY SomeTimestamp ASC
) st;
Ele foi desenvolvido para aproveitar a ordem dos índices para encontrar o valor mínimo ou máximo com algumas leituras lógicas. O CROSS JOIN
que há para obter resultados corretos quando não existem quaisquer linhas correspondentes para o @Id
valor. Mesmo se eu filtrar o valor mais popular da tabela (correspondendo a 6,5 milhões de linhas), recebo apenas 8 leituras lógicas:
Tabela 'MinhaTabela'. Contagem de digitalizações 2, leituras lógicas 8
Aqui está o plano de consulta:
Ambos os índices procuram encontrar 0 ou 1 linhas. É extremamente eficiente, mas a criação de dois índices pode ser um exagero para o seu cenário. Você pode considerar o seguinte índice:
CREATE INDEX CoveringIndex ON dbo.MyTable (Id) INCLUDE (SomeTimestamp, SomeInt) WHERE SomeBit = 1;
Agora, o plano de consulta da consulta original (com uma MAXDOP 1
dica opcional ) parece um pouco diferente:
As principais pesquisas não são mais necessárias. Com um caminho de acesso melhor que funcione bem para todas as entradas, você não precisa se preocupar com o otimizador que escolhe o plano de consulta errado devido ao vetor de densidade. No entanto, essa consulta e esse índice não serão tão eficientes quanto o outro se você buscar um @Id
valor popular .
Tabela 'MinhaTabela'. Contagem de varreduras 1, leituras lógicas 33757