Samozłączenie na kluczu podstawowym

9

Rozważ to zapytanie, które składa się z Nsamodzielnego łączenia:

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

Tworzy plan wykonania z N skanami indeksów klastrowych i łączeniami łączenia N-1.

Szczerze mówiąc, nie widzę żadnych powodów, aby nie optymalizować wszystkich połączeń i wykonać tylko jedno skanowanie indeksu klastrowego, tzn. Zoptymalizować oryginalne zapytanie do tego:

select
    t1.*
from [Table] as t1

pytania

  • Dlaczego dołączenia nie są zoptymalizowane?
  • Czy matematycznie błędne jest twierdzenie, że każde złączenie nie zmienia zestawu wyników?

Testowane na:

  • Wersja serwera źródłowego: SQL Server 2014 (12.0.4213)
  • Source Source Engine Edition: Microsoft SQL Server Standard Edition
  • Typ aparatu źródłowej bazy danych: samodzielny serwer SQL
  • Poziom zgodności: SQL Server 2008 (100)

Zapytanie nie ma znaczenia; właśnie przyszło mi do głowy i jestem teraz ciekawa.

Oto skrzypce przy tworzeniu tabel i 3 zapytaniach: z inner join's, z left join' i mieszane. Możesz również sprawdzić tam plan wykonania.

Wydaje się, że left joins są eliminowane w planie wykonania wyniku, podczas gdy inner joins nie są. Nadal nie rozumiem dlaczego .

pkuderov
źródło

Odpowiedzi:

18

Po pierwsze, załóżmy, że (id)jest to klucz podstawowy tabeli. W takim przypadku połączenia są (można udowodnić) zbędne i można je wyeliminować.

To tylko teoria - lub matematyka. Aby optymalizator dokonał faktycznej eliminacji, teorię należy przekonwertować na kod i dodać do pakietu optymalizacyjnego / przepisywania / eliminacji optymalizatora. Aby tak się stało, programiści (DBMS) muszą myśleć, że przyniesie to dobre korzyści w zakresie wydajności i że jest to dość powszechny przypadek.

Osobiście to nie brzmi jak jeden (dość powszechny). Zapytanie - jak przyznajesz - wygląda raczej głupio, a recenzent nie powinien pozwolić, aby przejrzał recenzję, chyba że zostanie poprawiony, a zbędne złączenie usunięte.

To powiedziawszy, istnieją podobne zapytania, w których następuje eliminacja. Jest bardzo miły pokrewny post na blogu Roba Farleya: JOIN uproszczenie w SQL Server .

W naszym przypadku wszystko, co musimy zrobić, to zmienić sprzężenia na LEFTsprzężenia. Zobacz dbfiddle.uk . Optymalizator w tym przypadku wie, że połączenie można bezpiecznie usunąć bez możliwości zmiany wyników. (Logika uproszczenia jest dość ogólna i nie jest specjalnie opracowana dla samodzielnych połączeń).

Oczywiście w pierwotnym zapytaniu usunięcie INNERzłączeń również nie może zmienić wyników. Ale łączenie się z kluczem podstawowym wcale nie jest powszechne, więc optymalizator nie ma zaimplementowanej tej skrzynki. Powszechne jest jednak łączenie (lub lewe łączenie), gdzie połączona kolumna jest kluczem podstawowym jednej z tabel (i często występuje ograniczenie klucza obcego). Co prowadzi do drugiej opcji eliminacji złączeń: Dodaj ograniczenie klucza obcego (samodzielne odwołanie!):

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

I voila, połączenia są eliminowane! (testowane w tym samym skrzypcach): tutaj

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
Wpływ na 4 rzędy
--------------------------------------------------------------------------------
-- 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 | dok                                      
-: | : ----------------------------------------
 1 | Wprowadź jedną partię na pole, nie używaj „GO”
 2 | Pola rosną podczas pisania                  
 3 | Użyj przycisków [+], aby dodać więcej          
 4 | Zobacz przykłady poniżej dotyczące zaawansowanego użycia    

wprowadź opis zdjęcia tutaj

--------------------------------------------------------------------------------
-- 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 | dok                                      
-: | : ----------------------------------------
 1 | Wprowadź jedną partię na pole, nie używaj „GO”
 2 | Pola rosną podczas pisania                  
 3 | Użyj przycisków [+], aby dodać więcej          
 4 | Zobacz przykłady poniżej dotyczące zaawansowanego użycia    

wprowadź opis zdjęcia tutaj

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 | dok                                      
-: | : ----------------------------------------
 1 | Wprowadź jedną partię na pole, nie używaj „GO”
 2 | Pola rosną podczas pisania                  
 3 | Użyj przycisków [+], aby dodać więcej          
 4 | Zobacz przykłady poniżej dotyczące zaawansowanego użycia    

wprowadź opis zdjęcia tutaj

ypercubeᵀᴹ
źródło
2

W kategoriach relacyjnych każde samodzielne przyłączenie bez zmiany nazwy atrybutu nie ma możliwości i można je bezpiecznie wyeliminować z planów wykonania. Niestety SQL nie jest relacyjny, a sytuacja, w której optymalizator może wyeliminować samozłączenie, jest ograniczona do niewielkiej liczby przypadków brzegowych.

Składnia SELECT języka SQL daje logiczne pierwszeństwo przed projekcją. Reguły zakresu SQL dla nazw kolumn oraz fakt, że dozwolone są zduplikowane nazwy kolumn i kolumny bez nazw, sprawiają, że optymalizacja zapytań SQL jest znacznie trudniejsza niż optymalizacja algebry relacyjnej. Dostawcy SQL DBMS mają ograniczone zasoby i muszą wybiórczo wybierać rodzaje optymalizacji, które chcą obsługiwać.

nvogel
źródło
1

Klucze podstawowe są zawsze unikalne, a wartości null są niedozwolone, więc dołączenie tabeli do siebie na kluczach podstawowych (nie z kluczem wtórnym z własnym odniesieniem i bez instrukcji where) spowoduje wygenerowanie takiej samej liczby wierszy jak oryginalna tabela.

Jeśli chodzi o to, dlaczego tego nie optymalizują, powiedziałbym, że to przypadek, którego albo nie planowali, albo zakładali, że ludzie nie zrobią tego. Łączenie tabeli ze sobą na gwarantowanych unikatowych kluczach podstawowych nie służy żadnemu celowi.

indiri
źródło