Por que a subconsulta usa paralelismo e a associação não?


16

Por que o SQL Server usa paralelismo ao executar esta consulta que usa uma subconsulta, mas não ao usar uma associação? A versão de junção é executada em série e leva cerca de 30 vezes mais para ser concluída.

Versão de junção: ~ 30seg

insira a descrição da imagem aqui

Versão da subconsulta: <1 segundo

insira a descrição da imagem aqui

EDIT: versões XML do plano de consulta:

JOIN version

Versão SUBQUERY

Respostas:


12

Como já indicado nos comentários, parece que você precisa atualizar suas estatísticas.

O número estimado de linhas saindo da junção entre locatione testrunsé extremamente diferente entre os dois planos.

Estimativas do plano de participação: 1

Plano 1

Estimativas do plano de subconsulta: 8.748

insira a descrição da imagem aqui

O número real de linhas saindo da associação é 14.276.

É claro que não faz absolutamente nenhum sentido intuitivo que a versão de junção calcule que três linhas devem surgir locatione produza uma única linha de junção, enquanto a subconsulta estima que uma única dessas linhas produzirá 8.748 da mesma junção, mas mesmo assim eu fui capaz para reproduzir isso.

Isso parece acontecer se não houver cruzamento entre os histogramas quando as estatísticas são criadas. A versão de junção assume uma única linha. E a busca de igualdade única da subconsulta assume as mesmas linhas estimadas que uma busca de igualdade em relação a uma variável desconhecida.

A cardinalidade dos testruns é 26244. Supondo que seja preenchido com três IDs de local distintos, a consulta a seguir estima que as 8,748linhas serão retornadas ( 26244/3)

declare @i int

SELECT *
FROM   testruns AS tr
WHERE  tr.location_id = @i

Dado que a tabela locationscontém apenas três linhas, é fácil (se não assumirmos chaves estrangeiras) inventar uma situação em que as estatísticas são criadas e os dados são alterados de uma maneira que afeta drasticamente o número real de linhas retornadas, mas é insuficiente para defina a atualização automática de estatísticas e recompile o limite.

À medida que o SQL Server obtém o número de linhas que saem dessa associação tão errado, todas as outras estimativas de linha no plano de associação são subestimadas em massa. Assim como significa que você obtém um plano serial, a consulta também recebe uma concessão de memória insuficiente e as classificações e hash se espalham tempdb.

Um cenário possível que reproduz as linhas reais vs estimadas mostradas no seu plano está abaixo.

CREATE TABLE location
  (
     id       INT CONSTRAINT locationpk PRIMARY KEY,
     location VARCHAR(MAX) /*From the separate filter think you are using max?*/
  )

/*Temporary ids these will be updated later*/
INSERT INTO location
VALUES      (101, 'Coventry'),
            (102, 'Nottingham'),
            (103, 'Derby')

CREATE TABLE testruns
  (
     location_id INT
  )

CREATE CLUSTERED INDEX IX ON testruns(location_id)

/*Add in 26244 rows of data split over three ids*/
INSERT INTO testruns
SELECT TOP (5984) 1
FROM   master..spt_values v1, master..spt_values v2
UNION ALL
SELECT TOP (5984) 2
FROM   master..spt_values v1, master..spt_values v2
UNION ALL
SELECT TOP (14276) 3
FROM   master..spt_values v1, master..spt_values v2

/*Create statistics. The location_id histograms don't intersect at all*/
UPDATE STATISTICS location(locationpk) WITH FULLSCAN;    
UPDATE STATISTICS testruns(IX) WITH FULLSCAN;

/* UPDATE location.id. Three row update is below recompile threshold*/
UPDATE location
SET    id = id - 100

A execução das consultas a seguir fornece a mesma discrepância estimada vs real

SELECT *
FROM   testruns AS tr
WHERE  tr.location_id = (SELECT id
                         FROM   location
                         WHERE  location = 'Derby')

SELECT *
FROM   testruns AS tr
       JOIN location loc
         ON tr.location_id = loc.id
WHERE  loc.location = ( 'Derby' ) 

Se uma restrição exclusiva for adicionada ao local, torna-se óbvio que "=" retornará exatamente uma linha. Em seguida, no seu exemplo, os planos de consulta se tornam idênticos (varreduras -> pesquisas): alter table Location adicionar restrição U_Location_Location unique nonclustered (Location);
crokusek

@crokusek yes. Percebi o que você quis dizer depois e excluiu meu comentário! Isso aumenta o número estimado de linhas para a versão de junção ser igual à subconsulta também? Não está no PC no momento para testar?
Martin Smith

@crokusek Sim. se parece com as mesmas linhas estimadas da junção que a subconsulta nesse caso de singleton.
Martin Smith

Sim. Plano de consulta idêntica, ambas as estimativas 8748 e ambas as reais 14276. Aliás, pensei que pré-computar o locationId resolveria essa diferença, mas isso não acontece.
crokusek

11
@ crokusek - também adicionarei a restrição exclusiva à localização e outros locais semelhantes no meu banco de dados. Devo admitir que não percebi que isso afetava a otimização de consultas. Eu pensei que era apenas para garantir a integridade dos dados. Obrigado pela sua contribuição sobre esta questão.
Chris L
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.