Como o SQL Server determina a ordem das colunas principais nas solicitações de índice ausentes?

32

Como o SQL Server determina a ordem das colunas principais nas recomendações de índice ausentes para um plano de consulta?

Bryan Rebok
fonte

Respostas:

44

Quando o SQL Server cria uma recomendação de índice ausente para um plano de consulta específico, ele separa possíveis colunas-chave em 2 grupos. O primeiro conjunto contém todas as colunas recomendadas que fazem parte de um predicado EQUALITY. O segundo conjunto contém todas as colunas recomendadas que fazem parte de um predicado INEQUALITY.

Dentro de cada conjunto, as colunas são ordenadas pela posição ordinal das colunas, com base na definição da tabela.

(Muito obrigado a Brent Ozar por criar um script de reprodução contra o banco de dados Stack Overflow para provar isso!)

1. Crie 3 tabelas idênticas , mas coloque suas colunas em ordem diferente. (O motivo aqui é usar vários nomes e tipos de dados de colunas para mostrar que isso não afeta a ordem das colunas na recomendação de índice ausente).

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. Preencha as tabelas com os mesmos dados. Obtenha 100.000 linhas da tabela Usuários com distribuição de dados no 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. Escreva uma consulta que precise de um índice. Comece com 3 filtros de igualdade, filtrando um valor exato em todos os 3 campos. Observe que todas as 3 consultas têm os mesmos campos na mesma ordem:

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

Todas as três tabelas têm exatamente os mesmos dados e as consultas são idênticas. A única diferença é a ordem do campo - e essa também é a diferença em nossas solicitações de índice ausentes:

Planos de execução com 3 campos de igualdade

Nos planos de execução, a ordem das colunas na solicitação de índice ausente corresponde exatamente à ordem das colunas na tabela. Por exemplo, em dbo.NumberLetterDate, a coluna numérica é a primeira, portanto é a primeira na solicitação de índice ausente também:

  • Em dbo.NumberLetterDate, o índice ausente está em fINT (número), fLetter (nvarchar), fDate, na mesma ordem dos campos na tabela
  • Em dbo.LetterDateNumber, a ordem do índice alterna para fNVARCHAR, fDATE, fINT
  • No dbo.DateNumberLetter, a ordem do índice alterna para fDATE, fINT, fNVARCHAR

Para uma operação de tabela única como essa, a ordem do campo de índice não parece depender da seletividade, tipo de dados ou posição na consulta. Deixo para outras pessoas provar isso com consultas e associações mais complexas.

4. Misture um filtro de desigualdade. No campo INT, por exemplo, coloque <> 100 como o 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

Nos planos de execução, os campos de igualdade vão primeiro, depois os campos de desigualdade - então aqui, o fINT aparece por último em todas as três solicitações de índice ausentes, porque é uma pesquisa de desigualdade:

Planos de execução com 2 pesquisas de igualdade e 1 de desigualdade

5. Use 3 filtros de desigualdade. Use a mesma pesquisa para todos os 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 não há pesquisas de igualdade, todos os três campos têm a mesma ordem de prioridade na recomendação de índice ausente e agora voltamos a classificar apenas por ordem de campo:

Planos de execução com 3 pesquisas de desigualdade

Bryan Rebok
fonte