A vista
CREATE VIEW [dbo].[vProductList]
WITH SCHEMABINDING
AS
SELECT
p.[Id]
,p.[Name]
,price.[Value] as CalculatedPrice
,orders.[Value] as OrdersWithThisProduct
FROM
products as p
INNER JOIN productMetadata as price ON p.Id = price.ProductId AND price.MetaId = 1
INNER JOIN productMetadata as orders ON p.Id = orders.ProductId AND orders.MetaId = 2
Para simplificar, suponha que productMetadata
tem colunas ProductId, MetaId, Value
com ~ 87 milhões de linhas e cerca de 400 mil linhas na products
tabela.
As consultas gerais nesta visão funcionam perfeitamente:
SELECT * FROM vProductList WHERE CalculatedPrice > 500
A consulta resulta em 2-4 segundos (por uma VPN e remota, então estou bem com isso).
Alterar o acima para uma contagem é igualmente rápido:
SELECT COUNT(*) from vProductList WHERE CalculatedPrice > 500
é executado quase ao mesmo tempo que a seleção bruta, com a qual novamente estou bem. Existem cerca de 10 mil produtos que atendem a esse critério.
Eu me deparei com dois casos separados onde as coisas ficam realmente estranhas e levam PARA SEMPRE.
Primeiro
Fazendo uma consulta em uma das colunas da tabela base na exibição:
SELECT * FROM vProductList WHERE Name = 'Hammer'
Essa consulta demora um pouco para ser executada (20 a 30 segundos) e retorna cerca de 30 mil resultados; no entanto, uma pequena alteração na consulta:
SELECT COUNT(*) FROM vProductList WHERE Name = 'Hammer'
leva treze MINUTOS para retornar uma contagem informando ~30k .
Segundo
Fazendo uma WHERE IN
subconsulta
SELECT * FROM vProductList WHERE Id IN (SELECT ProductId FROM TableThatHasFKToProductId and ColumnInTable = 'Yes')
Essa consulta retorna ~300k linhas e leva dois minutos para retornar (muito desse tempo é simplesmente gasto baixando os dados no SSMS, acredito); no entanto, alterar isso para um SELECT COUNT(*)
resulta em uma consulta que leva vinte minutos.
SELECT COUNT(*) FROM vProductList WHERE Id IN (SELECT ProductId FROM TableThatHasFKToProductId and ColumnInTable = 'Yes')
Por que é que SELECT *
é mais rápido do que SELECT COUNT
?
Estou usando o tempo total de execução fornecido pelo SSMS para todos os horários listados aqui.
Planos de execução
Nota: Eu tentei usar o PasteThePlan, mas ele continuava dizendo que o plano era xml inválido.