Acho que já encontrei a resposta para isso, mas espero obter alguma perspectiva adicional.
Suponha que estamos JOIN
juntando duas tabelas em uma coluna compartilhada e cada tabela tem uma diferente column
na qual faremos uma Constant
pesquisa. Quando construímos um index
para suportar a consulta, para cada tabela queremos colocar o JOIN
ing column
primeiro ou o Constant
column
primeiro? Estou pensando agora que é o Constant
column
primeiro. Quando observo um plano de consulta para uma consulta diferente que gerou essa pergunta, parece que ele tenta criar um subconjunto de cada tabela e, em seguida JOIN
, juntá-los. Em vez de JOIN
juntar as duas tabelas e filtrar a partir daí.
EX: Juntar Remessas a Clientes onde a Remessa é Enviada e o Cliente está Ativo
SELECT [Columns]
FROM Shipment S
INNER JOIN Customer C
ON S.CustomerID = C.CustomerID
WHERE S.IsShipped = 1
AND C.IsActive = 1
Estou pensando que os dois melhores índices para usar estão abaixo. Porque Query Optimizer
preferiria escanear o Constant
primeiro e depois JOIN
o segundo , column
em vez de juntar JOIN
os dois tables
e filtrar o constant
depois.
CREATE NONCLUSTERED INDEX [IX_IsActive-CustomerID] ON [dbo].[Customer]
(
[IsActive] ASC,
[CustomerID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
CREATE NONCLUSTERED INDEX [IX_IsShipped-CustomerID] ON [dbo].[Shipment]
(
[IsShipped] ASC,
[CustomerID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
Ao invés de:
CREATE NONCLUSTERED INDEX [IX_CustomerID-IsActive] ON [dbo].[Customer]
(
[CustomerID] ASC,
[IsActive] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
CREATE NONCLUSTERED INDEX [IX_CustomerID-IsShipped] ON [dbo].[Shipment]
(
[CustomerID] ASC,
[IsShipped] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
Isso está certo?
Assumindo que não há outras questões de interesse, a resposta geral é que você deseja critérios mais seletivos nas colunas iniciais do índice (observe que a seletividade aqui é em relação ao predicado e não uma medida de quão únicos os valores particulares na coluna são). Em geral, você deseja que o otimizador elimine o maior número possível de linhas o mais rápido possível.
Assumindo que
CustomerID
inShipment
éNOT NULL
e tem uma chave estrangeira paraCustomer
, o que significa queinner join
é garantido que não elimine nenhuma linha deSupplier
, entãos.IsShipped = 1
é o único predicado seletivo e faria sentido que fosse a coluna principal. Se, por outro lado,inner join
fosse mais seletivo (imagine que alguém movesse linhas deCustomer
paraCustomer_Archive
periodicamente eShipment
pudesse juntar-se a qualquer uma dessas tabelas), faria sentido terCustomerID
como coluna principal.