Asumiré que tiene datos asimétricos, que no desea utilizar sugerencias de consulta para forzar al optimizador a qué hacer y que necesita obtener un buen rendimiento para todos los valores de entrada posibles @Id
. Puede obtener un plan de consulta que solo requiere unos pocos puñados de lecturas lógicas para cualquier valor de entrada posible si está dispuesto a crear el siguiente par de índices (o su equivalente):
CREATE INDEX GetMinSomeTimestamp ON dbo.MyTable (Id, SomeTimestamp) WHERE SomeBit = 1;
CREATE INDEX GetMaxSomeInt ON dbo.MyTable (Id, SomeInt) WHERE SomeBit = 1;
A continuación se muestran mis datos de prueba. Puse 13 M filas en la tabla e hice que la mitad de ellas tuviera un valor '3A35EA17-CE7E-4637-8319-4C517B6E48CA'
para la Id
columna.
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 puede parecer un poco extraña al principio:
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;
Está diseñado para aprovechar el orden de los índices para encontrar el valor mínimo o máximo con algunas lecturas lógicas. El CROSS JOIN
está ahí para obtener resultados correctos cuando no hay filas coincidentes para el @Id
valor. Incluso si filtro en el valor más popular en la tabla (que coincide con 6.5 millones de filas) solo obtengo 8 lecturas lógicas:
Tabla 'MyTable'. Escaneo recuento 2, lecturas lógicas 8
Aquí está el plan de consulta:
Ambos índices buscan encontrar 0 o 1 filas. Es extremadamente eficiente, pero crear dos índices puede ser excesivo para su escenario. En su lugar, puede considerar el siguiente índice:
CREATE INDEX CoveringIndex ON dbo.MyTable (Id) INCLUDE (SomeTimestamp, SomeInt) WHERE SomeBit = 1;
Ahora el plan de consulta para la consulta original (con una MAXDOP 1
pista opcional ) se ve un poco diferente:
Las búsquedas clave ya no son necesarias. Con una mejor ruta de acceso que debería funcionar bien para todas las entradas, no debería tener que preocuparse de que el optimizador elija el plan de consulta incorrecto debido al vector de densidad. Sin embargo, esta consulta e índice no serán tan eficientes como la otra si busca un @Id
valor popular .
Tabla 'MyTable'. Escaneo recuento 1, lecturas lógicas 33757