¿Cómo determina SQL Server el orden de las columnas clave en las solicitudes de índice que faltan?

Respuestas:

44

Cuando SQL Server crea una recomendación de índice faltante para un plan de consulta en particular, separa las posibles columnas clave en 2 grupos. El primer conjunto contiene todas las columnas recomendadas que forman parte de un predicado de IGUALDAD. El segundo conjunto contiene todas las columnas recomendadas que forman parte de un predicado de DESIGUALDAD.

Dentro de cada conjunto, las columnas se ordenan por la posición ordinal de las columnas, según la definición de la tabla.

(¡Muchas gracias a Brent Ozar por crear un script de repro en la base de datos Stack Overflow para probar esto!)

1. Cree 3 tablas idénticas , pero coloque sus columnas en un orden diferente. (La razón aquí es utilizar una variedad de nombres de columnas y tipos de datos para mostrar que eso no afecta el orden de las columnas en la recomendación de índice que falta).

CREATE TABLE dbo.NumberLetterDate (ID INT IDENTITY(1,1) PRIMARY KEY CLUSTERED, 
fINT INT, fNVARCHAR NVARCHAR(40), fDATE DATETIME, AboutMe NVARCHAR(MAX));
GO
CREATE TABLE dbo.LetterDateNumber (ID INT IDENTITY(1,1) PRIMARY KEY CLUSTERED, 
fNVARCHAR NVARCHAR(40), fDATE DATETIME, fINT INT, AboutMe NVARCHAR(MAX));
GO
CREATE TABLE dbo.DateNumberLetter (ID INT IDENTITY(1,1) PRIMARY KEY CLUSTERED,
fDATE DATETIME, fINT INT, fNVARCHAR NVARCHAR(40), AboutMe NVARCHAR(MAX));
GO

2. Rellene las tablas con los mismos datos. Obtenga 100,000 filas de la tabla Usuarios con distribución de datos del mundo real.

INSERT INTO dbo.NumberLetterDate(fINT, fNVARCHAR, fDATE, AboutMe)
SELECT TOP 100000 Age, DisplayName, LastAccessDate, AboutMe
  FROM dbo.Users WITH (NOLOCK)
  ORDER BY Id;
GO
INSERT INTO dbo.LetterDateNumber(fINT, fNVARCHAR, fDATE, AboutMe)
SELECT TOP 100000 Age, DisplayName, LastAccessDate, AboutMe
  FROM dbo.Users WITH (NOLOCK)
  ORDER BY Id;
GO
INSERT INTO dbo.DateNumberLetter(fINT, fNVARCHAR, fDATE, AboutMe)
SELECT TOP 100000 Age, DisplayName, LastAccessDate, AboutMe
  FROM dbo.Users WITH (NOLOCK)
  ORDER BY Id;
GO

3. Escriba una consulta que necesite un índice. Comience con 3 filtros de igualdad, filtrando por un valor exacto en los 3 campos. Tenga en cuenta que las 3 consultas tienen los mismos campos en el mismo orden:

SELECT ID
  FROM dbo.NumberLetterDate
  WHERE fINT = 100
  AND fNVARCHAR = 'Brent Ozar'
  AND fDATE = '2018/01/01'
  AND 1 = (SELECT 1);

SELECT ID
  FROM dbo.LetterDateNumber
  WHERE fINT = 100
  AND fNVARCHAR = 'Brent Ozar'
  AND fDATE = '2018/01/01'
  AND 1 = (SELECT 1);

SELECT ID
  FROM dbo.DateNumberLetter
  WHERE fINT = 100
  AND fNVARCHAR = 'Brent Ozar'
  AND fDATE = '2018/01/01'
  AND 1 = (SELECT 1);
GO

Las tres tablas tienen exactamente los mismos datos y las consultas son idénticas. La única diferencia es el orden de los campos, y esa es también la diferencia en nuestras solicitudes de índice que faltan:

Planes de ejecución con 3 campos de igualdad.

En los planes de ejecución, el orden de las columnas en la solicitud de índice que falta coincide exactamente con el orden de las columnas en la tabla. Por ejemplo, en dbo.NumberLetterDate, la columna de números es la primera, por lo que también es la primera en la solicitud de índice que falta:

  • En dbo.NumberLetterDate, el índice faltante está en fINT (número), fLetter (nvarchar), fDate, el mismo orden de los campos en la tabla
  • En dbo.LetterDateNumber, el orden del índice cambia a fNVARCHAR, fDATE, fINT
  • En dbo.DateNumberLetter, el orden del índice cambia a fDATE, fINT, fNVARCHAR

Para una operación de tabla única como esta, el orden del campo de índice no parece depender de la selectividad, el tipo de datos o la posición en la consulta. (Dejo que otras personas lo prueben con consultas y uniones más complejas).

4. Mezclar en un filtro de desigualdad. En el campo INT, por ejemplo, ponga <> 100 como filtro:

SELECT ID
  FROM dbo.NumberLetterDate
  WHERE fINT <> 100
  AND fNVARCHAR = 'Brent Ozar'
  AND fDATE = '2018/01/01'
  AND 1 = (SELECT 1);

SELECT ID
  FROM dbo.LetterDateNumber
  WHERE fINT <> 100
  AND fNVARCHAR = 'Brent Ozar'
  AND fDATE = '2018/01/01'
  AND 1 = (SELECT 1);

SELECT ID
  FROM dbo.DateNumberLetter
  WHERE fINT <> 100
  AND fNVARCHAR = 'Brent Ozar'
  AND fDATE = '2018/01/01'
  AND 1 = (SELECT 1);
GO

En los planes de ejecución, los campos de igualdad van primero, luego los campos de desigualdad, así que aquí, fINT aparece en último lugar en las 3 solicitudes de índice que faltan porque es una búsqueda de desigualdad:

Planes de ejecución con 2 búsquedas de igualdad y 1 de desigualdad

5. Use 3 filtros de desigualdad. Use la misma búsqueda para todos los campos (<>):

SELECT ID
  FROM dbo.NumberLetterDate
  WHERE fINT <> 100
  AND fNVARCHAR <> 'Brent Ozar'
  AND fDATE <> '2018/01/01'
  AND 1 = (SELECT 1);

SELECT ID
  FROM dbo.LetterDateNumber
  WHERE fINT <> 100
  AND fNVARCHAR <> 'Brent Ozar'
  AND fDATE <> '2018/01/01'
  AND 1 = (SELECT 1);

SELECT ID
  FROM dbo.DateNumberLetter
  WHERE fINT <> 100
  AND fNVARCHAR <> 'Brent Ozar'
  AND fDATE <> '2018/01/01'
  AND 1 = (SELECT 1);
GO

Como no hay búsquedas de igualdad, los 3 campos tienen el mismo orden de prioridad en la recomendación de índice que falta, y ahora volvemos a ordenarlos simplemente por orden de campo:

Planes de ejecución con 3 búsquedas de desigualdad

Bryan Rebok
fuente