Auto-junção na chave primária

9

Considere esta consulta que consiste Nem associações automáticas:

select
    t1.*
from [Table] as t1
join [Table] as t2 on
    t1.Id = t2.Id
-- ...
join [Table] as tN on
    t1.Id = tN.Id

Ele produz um plano de execução com N varreduras de índice em cluster e junções de mesclagem N-1.

Sinceramente, não vejo motivos para não otimizar todas as junções e fazer apenas uma verificação de índice em cluster, ou seja, otimizar a consulta original para isso:

select
    t1.*
from [Table] as t1

Questões

  • Por que as junções não são otimizadas?
  • É matematicamente incorreto dizer que toda junção não altera o conjunto de resultados?

Testado em:

  • Versão do servidor de origem: SQL Server 2014 (12.0.4213)
  • Source Database Engine Edition: Microsoft SQL Server Standard Edition
  • Tipo de mecanismo de banco de dados de origem: SQL Server autônomo
  • Nível de compatibilidade: SQL Server 2008 (100)

A consulta não é significativa; isso veio à minha mente e estou curioso sobre isso agora.

Aqui está o violino com a criação da tabela e 3 consultas: com inner join's, com left join' s e misturado. Você também pode ver o plano de execução lá também.

Parece que left joins são eliminados no plano de execução de resultados, enquanto inner joins não são. Ainda não entendo o porquê .

pkuderov
fonte

Respostas:

18

Primeiro, vamos supor que essa (id)seja a chave primária da tabela. Nesse caso, sim, as junções são (podem ser provadas) redundantes e podem ser eliminadas.

Agora isso é apenas teoria - ou matemática. Para que o otimizador faça uma eliminação real, a teoria precisa ser convertida em código e adicionada ao conjunto de otimizações / reescritas / eliminações do otimizador. Para que isso aconteça, os desenvolvedores (DBMS) devem pensar que eles terão bons benefícios para a eficiência e que é um caso bastante comum.

Pessoalmente, não soa como um (bastante comum). A consulta - como você admite - parece um tanto boba e um revisor não deve deixar passar a revisão, a menos que tenha sido aprimorada e a junção redundante removida.

Dito isto, existem consultas semelhantes em que a eliminação ocorre. Há uma postagem de blog relacionada muito legal de Rob Farley: simplificação de JOIN no SQL Server .

No nosso caso, tudo o que precisamos fazer é alterar as junções para LEFTjunções. Veja dbfiddle.uk . O otimizador nesse caso sabe que a junção pode ser removida com segurança sem alterar os resultados. (A lógica de simplificação é bastante geral e não é especial para auto-uniões.)

Obviamente, na consulta original, a remoção das INNERjunções também não pode alterar os resultados. Mas não é comum se auto-associar na chave primária, para que o otimizador não tenha esse caso implementado. No entanto, é comum ingressar (ou ingressar à esquerda) em que a coluna unida é a chave primária de uma das tabelas (e geralmente há uma restrição de chave estrangeira). O que leva a uma segunda opção para eliminar as junções: Adicione uma restrição de chave estrangeira (auto-referência!):

ALTER TABLE "Table"
    ADD FOREIGN KEY (id) REFERENCES "Table" (id) ;

E pronto, as junções são eliminadas! (testado no mesmo violino): aqui

create table docs
(id int identity primary key,
 doc varchar(64)
) ;
GO
insert
into docs (doc)
values ('Enter one batch per field, don''t use ''GO''')
     , ('Fields grow as you type')
     , ('Use the [+] buttons to add more')
     , ('See examples below for advanced usage')
  ;
GO
4 linhas afetadas
--------------------------------------------------------------------------------
-- Or use XML to see the visual representation, thanks to Justin Pealing and
-- his library: https://github.com/JustinPealing/html-query-plan
--------------------------------------------------------------------------------
set statistics xml on;
select d1.* from docs d1 
    join docs d2 on d2.id=d1.id
    join docs d3 on d3.id=d1.id
    join docs d4 on d4.id=d1.id;
set statistics xml off;
GO
id | doc                                      
-: | : ----------------------------------------
 1 | Insira um lote por campo, não use 'GO'
 2 Os campos crescem à medida que você digita                  
 3 Use os botões [+] para adicionar mais          
 4 Veja exemplos abaixo para uso avançado    

insira a descrição da imagem aqui

--------------------------------------------------------------------------------
-- Or use XML to see the visual representation, thanks to Justin Pealing and
-- his library: https://github.com/JustinPealing/html-query-plan
--------------------------------------------------------------------------------
set statistics xml on;
select d1.* from docs d1 
    left join docs d2 on d2.id=d1.id
    left join docs d3 on d3.id=d1.id
    left join docs d4 on d4.id=d1.id;
set statistics xml off;
GO
id | doc                                      
-: | : ----------------------------------------
 1 | Insira um lote por campo, não use 'GO'
 2 Os campos crescem à medida que você digita                  
 3 Use os botões [+] para adicionar mais          
 4 Veja exemplos abaixo para uso avançado    

insira a descrição da imagem aqui

alter table docs
  add foreign key (id) references docs (id) ;
GO
--------------------------------------------------------------------------------
-- Or use XML to see the visual representation, thanks to Justin Pealing and
-- his library: https://github.com/JustinPealing/html-query-plan
--------------------------------------------------------------------------------
set statistics xml on;
select d1.* from docs d1 
    join docs d2 on d2.id=d1.id
    join docs d3 on d3.id=d1.id
    join docs d4 on d4.id=d1.id;
set statistics xml off;
GO
id | doc                                      
-: | : ----------------------------------------
 1 | Insira um lote por campo, não use 'GO'
 2 Os campos crescem à medida que você digita                  
 3 Use os botões [+] para adicionar mais          
 4 Veja exemplos abaixo para uso avançado    

insira a descrição da imagem aqui

ypercubeᵀᴹ
fonte
2

Em termos relacionais, qualquer autoingressão sem renomear atributos é um no-op e pode ser eliminada com segurança dos planos de execução. Infelizmente, o SQL não é relacional e a situação em que uma associação automática pode ser eliminada pelo otimizador é limitada a um pequeno número de casos extremos.

A sintaxe SELECT do SQL fornece precedência lógica de junção sobre a projeção. As regras de escopo do SQL para nomes de colunas e o fato de nomes de colunas duplicados e sem nome serem permitidos tornam a otimização de consultas SQL significativamente mais difícil do que a otimização da álgebra relacional. Os fornecedores de SQL DBMS têm recursos finitos e precisam ser seletivos sobre os tipos de otimização que desejam oferecer suporte.

nvogel
fonte
1

As Chaves Primárias são sempre únicas e os valores nulos não são permitidos; portanto, a junção de uma tabela a elas próprias nas chaves primárias (não com uma chave secundária auto-referente e sem instruções where) produzirá o mesmo número de linhas que a tabela original.

Quanto ao motivo pelo qual eles não otimizam isso, eu diria que é um caso que eles não planejaram ou presumiram que as pessoas não o fariam. Unir uma tabela a si mesmo com chaves primárias exclusivas garantidas não serve para nada.

indiri
fonte