2013-11-01 14 views
6

Mam cztery tabele, TopLevelParent, dwie tabele średniego poziomu MidParentA i MidParentB oraz tabelę Child, która może mieć rodzica MidParentA lub MidParentB (Jeden lub drugi midParent musi być w miejsce). Obie tabele na poziomie średnim mają tabelę nadrzędną TopLevelParent.Serwer SQL został połączony z operatorem "Lub"

tabela wygląd Top Level tak:

TopLevelId | Name 
-------------------------- 
1   | name1 
2   | name2 

Tabele MidParent wyglądać następująco:

MidParentAId | TopLevelParentId |   MidParentBId | TopLevelParentId | 
------------------------------------  ------------------------------------ 
1   |  1   |   1   |  1   | 
2   |  1   |   2   |  1   | 

tabeli podrzędnej wyglądać następująco:

ChildId | MidParentAId | MidParentBId 
-------------------------------- 
1  |  1  | NULL 
2  | NULL  |  2 

Użyłem następujące po lewej stronie dołączyć w większej procedura przechowywana, która jest limit czasu i wygląda jak operato OR r na ostatnim sprzężeniu po lewej jest winowajcą:

SELECT *  
FROM TopLevelParent tlp 
LEFT JOIN MidParentA a ON tlp.TopLevelPatientId = a.TopLevelPatientId 
LEFT JOIN MidParentB a ON tlp.TopLevelPatientId = b.TopLevelPatientId 
LEFT JOIN Child c ON c.ParentAId = a.ParentAId OR c.ParentBId = b.ParentBId 

Czy istnieje bardziej wydajny sposób, aby to dołączyć?

Odpowiedz

2

Oto co zrobiłem w końcu, który dostał czas wykonania w dół od 52 sekund do 4 sek.

SELECT * 
FROM (
    SELECT tpl.*, a.MidParentAId as 'MidParentId', 1 as 'IsMidParentA' 
    FROM TopLevelParent tpl 
    INNER JOIN MidParentA a ON a.TopLevelParentId = tpl.TopLevelParentID 
UNION 
    SELECT tpl.*, b.MidParentBId as 'MidParentId', 0 as 'IsMidParentA' 
    FROM TopLevelParent tpl 
    INNER JOIN MidParentB b ON b.TopLevelParentId = tpl.TopLevelParentID 
UNION 
    SELECT tpl.*, 0 as 'MidParentId', 0 as 'IsMidParentA' 
    FROM TopLevelParent tpl 
    WHERE tpl.TopLevelParentID NOT IN (
     SELECT pa.TopLevelParentID 
     FROM TopLevelParent tpl 
     INNER JOIN MidParentA a ON a.TopLevelParentId = tpl.TopLevelParentID 
    UNION 
     SELECT pa.TopLevelParentID 
     FROM TopLevelParent tpl 
     INNER JOIN MidParentB b ON h.TopLevelParentId = tpl.TopLevelParentID 
    ) 
) tpl 
LEFT JOIN MidParentA a ON a.TopLevelParentId = tpl.TopLevelParentID 
LEFT JOIN MidParentB b ON b.TopLevelParentId = tpl.TopLevelParentID 
LEFT JOIN 
(
     SELECT [ChildId] 
       ,[MidParentAId] as 'MidParentId' 
       ,1 as 'IsMidParentA' 
     FROM Child c 
     WHERE c.MidParentAId IS NOT NULL 
    UNION 
     SELECT [ChildId] 
       ,[MidParentBId] as 'MidParentId' 
       ,0 as 'IsMidParentA' 
     FROM Child c 
     WHERE c.MidParentBId IS NOT NULL 
) AS c 
ON c.MidParentId = tpl.MidParentId AND c.IsMidParentA = tpl.IsMidParentA 

Eliminuje to skanowanie tabeli, która się dzieje, jak już dopasowane górną rekord poziomu jego Midlevel rodzica z przodu, jeśli istnieje, i opieczętowane go na tej płycie.

Też zrobiłem to samo z rekordem podrzędnym, co oznacza, że ​​mogę po prostu dołączyć rekord potomny do rekordu najwyższego poziomu na MidParent Id, i używam flagi bitowej IsMidParentA do rozróżnienia, gdzie są dwa identyczne dokumenty MidParentID (tj. Identyfikator 1 dla IsMidParentA i IsMidParentB).

Dziękuję wszystkim, którzy poświęcili czas na odpowiedź.

+3

To jest trochę skomplikowana sprawa – Roel

2

Powinieneś zadbać o używanie predykatów w obrębie On.

"Bardzo ważne jest, aby zrozumieć, że przy połączeniach zewnętrznych, klauzule ON i WHERE odgrywają bardzo różne role, a zatem nie są wymienne, klauzula WHERE nadal odgrywa prostą rolę filtrującą - a mianowicie utrzymuje prawdziwe przypadki i odrzuca przypadki fałszywe i nieznane Użyj czegoś takiego i używaj predykatów w klauzuli where, jednak klauzula ON nie odgrywa prostą rolę filtrującą, raczej jest bardziej pasującą rolą, innymi słowy, wiersz w zachowana strona zostanie zwrócona niezależnie od tego, czy predykat ON znajdzie dla niej dopasowanie, czy też nie, więc predykat ON tylko określa, które wiersze od niezarezerwowanej strony są dopasowywane do wierszy od strony zachowanej - nie, czy zwracać wiersze od strony zakonserwowanej. " ** Egzamin 70-461: Querying Microsoft SQL Server 2012

9

Biorąc pod uwagę, jak mało zapytania jest narażone; bardzo chropowatą zasadą jest zastąpienie Or z Unią w celu uniknięcia skanowania stołu.

Select.. 
LEFT JOIN Child c ON c.ParentAId = a.ParentAId 
union 
Select.. 
left Join Child c ON c.ParentBId = b.ParentBId 
+0

Zmontowałem pytanie, aby pokazać więcej zapytania –

+1

To pomaga :) Możesz mieć dwa sprzężenia z dzieckiem, zamiast z każdym wykonującym jedną stronę OR; a następnie użyj koalescji, aby uzyskać wartość z C lub D, gdy chcesz tylko jedną wartość. – u07ch

0

inny sposób napisać go:

LEFT JOIN Child c ON c.ParentAId = COALESCE(a.ParentAId, b.ParentBId)

Edycja

Jednym z możliwych rozwiązań jest odpytywanie pierwszy MidParentA a następnie MidParentB a następnie UNION wyniki:

SELECT tlp.*, 
     a.MidParentAId, 
     null MidParentBId, 
     c.ChildId 
FROM TopLevelParent tlp 
LEFT JOIN MidParentA a ON tlp.TopLevelPatientId = a.TopLevelPatientId 
LEFT JOIN Child c ON c.MidParentAId = a.MidParentAId 
UNION 
SELECT tlp.*, 
     null MidParentAId, 
     b.MidParentBId, 
     c.ChildId 
FROM TopLevelParent tlp 
LEFT JOIN MidParentB b ON tlp.TopLevelPatientId = b.TopLevelPatientId 
LEFT JOIN Child c ON c.MidParentBId = b.MidParentBId 

Demo w SQLFiddle

+0

Oryginalne zapytanie miało c.ParentBId = b.ParentBId zamiast c.parentAID = b.parentBID, więc to może nie być w porządku – u07ch

+0

masz rację , na pierwszy rzut oka nie czytałem uważnie pytania – mucio

Powiązane problemy