2013-05-25 11 views
6

mam bazy PostgreSQL z 4 tabelach:łączące tabeli, jeśli występuje odniesienie

Tabela A

--------------------------- 
| ID | B_ID | C_ID | D_ID | 
--------------------------- 
| 1 | 1 | NULL | NULL | 
--------------------------- 
| 2 | NULL | 1 | NULL | 
--------------------------- 
| 3 | 2 | 2 | 1 | 
--------------------------- 
| 4 | NULL | NULL | 2 | 
--------------------------- 

Tabela B

------------- 
| ID | DATA | 
------------- 
| 1 | 123 | 
------------- 
| 2 | 456 | 
------------- 

Tabela C

------------- 
| ID | DATA | 
------------- 
| 1 | 789 | 
------------- 
| 2 | 102 | 
------------- 

Tabela D

------------- 
| ID | DATA | 
------------- 
| 1 | 654 | 
------------- 
| 2 | 321 | 
------------- 

Próbuję pobrać zestaw wyników, która dołączyła do danych z tabeli B i dane z tabeli C, tylko jeśli jeden z identyfikatorów stoisk nie jest null.

SELECT "Table_A"."ID", "Table_A"."ID_B", "Table_A"."ID_C", "Table_A"."ID_D", "Table_B"."DATA", "Table_C"."DATA" 
    FROM "Table_A" 
     LEFT JOIN "Table_B" on "Table_A"."ID_B" = "Table_B"."ID" 
     LEFT JOIN "Table_C" on "Table_A"."ID_C" = "Table_C"."ID" 
    WHERE "Table_A"."ID_B" IS NOT NULL OR "Table_A"."ID_C" IS NOT NULL; 

Czy jest to zalecane, czy lepiej podzielić to na kilka zapytań?

Czy istnieje sposób połączenia wewnętrznego między tymi tabelami?

Wynik Spodziewam się:

------------------------------------------------- 
| ID | ID_B | ID_C | ID_D | DATA (B) | DATA (C) | 
------------------------------------------------- 
| 1 | 1 | NULL | NULL | 123 | NULL | 
------------------------------------------------- 
| 2 | NULL | 1 | NULL | NULL | 789 | 
------------------------------------------------- 
| 3 | 2 | 2 | NULL | 456 | 102 | 
------------------------------------------------- 

EDIT:ID_B, ID_C, ID_D są klucze obce do tabel table_b, table_c, table_d

+0

Edytowałem swoją odpowiedź i dodałem wynik, którego oczekuję. – wiizzard

+1

Nie pokazałeś więzów FK. Czy table_a.id_b i table_a.id_c FK do tabel B i C? (wydaje się, że są, ale nie jest to wspomniane w pytaniu) – wildplasser

+0

Masz rację. Są to FK. – wiizzard

Odpowiedz

5

WHERE "Table_A"."ID_B" IS NOT NULL OR "Table_A"."ID_C" IS NOT NULL; może być zastąpiony przez odpowiednią klauzulę w sprawie Tabele B i C: WHERE "Table_B"."ID" IS NOT NULL OR "Table_C"."ID" IS NOT NULL;. Będzie to również działać, jeśli table_a.id_b i table_a.id_c nie są FK do tabel B i C. W przeciwnym razie wiersz table_a z {5, 5,5,5} będzie pobierał dwa NULL wiersze z tabel B i C.

SELECT ta."ID" AS a_id 
     , ta."ID_B" AS b_id 
     , ta."ID_C" AS c_id 
     , ta."ID_D" AS d_id 
     , tb."DATA" AS bdata 
     , tc."DATA" AS cdata 
FROM "Table_a" ta 
LEFT JOIN "Table_B" tb on ta."ID_B" = tb."ID" 
LEFT JOIN "Table_C" tc on ta."ID_C" = tc."ID" 
WHERE tb."ID" IS NOT NULL OR tc."ID" IS NOT NULL 
     ; 
+0

Lepiej :-). Oprócz tego, że zapytanie jest lepszym rozwiązaniem w przypadku braku ograniczeń klucza obcego, czy masz jakieś testy wydajności dla tych dwóch wariantów? Myślę, że z indeksami umieszczonymi na kluczu obcym pierwotne zapytanie byłoby szybsze (i bardziej czytelne), chyba że optymalizator może dokonać niezbędnej transformacji ... –

+0

Tak, różni się od zapytania OQ, z wyjątkiem przypadku gdzie ra.id_b i ta.id_c mają ograniczenia FK do tabel B i C. Zobacz mój komentarz na temat OQ. W przeciwnym razie: semantyka będzie inna, plany będą inne, wydajność będzie inna. (w prostych/małych przypadkach będą używane dwa połączenia skrótów, a różnica będzie bardzo mała) – wildplasser

2

Biorąc pod uwagę Państwa wymagania, zapytanie wydaje się dobrze mnie.

Alternatywą byłoby użycie zagnieżdżonych selekcji w projekcji, ale w zależności od danych, indeksów i wiązań, które mogą być wolniejsze, ponieważ zagnieżdżone selekcje zwykle skutkują zagnieżdżonymi pętlami, natomiast łączenia mogą być wykonywane jako połączenia scalone lub zagnieżdżone pętle:

SELECT 
    "Table_A"."ID", 
    "Table_A"."ID_B", 
    "Table_A"."ID_C", 
    "Table_A"."ID_D", 
    (SELECT "DATA" FROM "Table_B" WHERE "Table_A"."ID_B" = "Table_B"."ID"), 
    (SELECT "DATA" FROM "Table_C" WHERE "Table_A"."ID_C" = "Table_C"."ID") 
FROM "Table_A" 
WHERE "Table_A"."ID_B" IS NOT NULL OR "Table_A"."ID_C" IS NOT NULL; 

Jeśli Postgres robi scalar subquery caching (podobnie jak Oracle), a następnie zagnieżdżone Wybiera może pomóc w przypadku gdy masz dużo powtórzeń danych w Table_A

0

Ogólnie spealking zalecanym sposobem jest to zrobić w jednej kwerendzie tylko i niech baza robić tak dużo pracy, jak to możliwe, zwłaszcza jeśli dodać inne operacje, takie jak sortowanie (kolejność BY) lub paginacji później (limit ... offset ...) później. Wykonaliśmy pewne pomiary i nie ma sposobu na szybsze sortowanie/paginację w Javie/Scali, jeśli korzystasz z kolekcji o wyższym poziomie, takich jak listy itp.

RDBMS radzi sobie bardzo dobrze z pojedynczymi złożonymi instrukcjami, ale ma trudności w obsłudze wielu małych zapytań.Na przykład, jeśli zapytasz "jeden" i "wiele relacji" w jednym zapytaniu, będzie to szybsze niż w przypadku instrukcji 1 + n select.

Jeśli chodzi o sprzężenie zewnętrzne, wykonaliśmy pomiary i nie ma rzeczywistej kary za wydajność w porównaniu z połączeniami wewnętrznymi. Więc jeśli twój model danych i/lub zapytanie wymagają zewnętrznego sprzężenia, po prostu zrób to. Jeśli był to problem z wydajnością, możesz go ustawić później.

Jeśli chodzi o Twoje zerowe porównania, może może wskazują, że twój model danych może być zoptymalizowany, ale to tylko zgadnij. Możliwe, że możesz ulepszyć projekt, aby null nie był dozwolony w tych kolumnach.

+0

Dotyczy Twojego ostatniego akapitu: często jest tak, że w takich kolumnach są wartości normalne. Nie widzę, jak to by wskazywało na problem. –

+0

@Erwin To właśnie zamierzałem opisać. Skoro angielski nie jest moim ojczystym językiem, co mogę poprawić, żeby to wyjaśnić? – Beryllium

+0

Twój angielski wydaje się w porządku. Usunąłbym ostatni akapit. –

2

Ponieważ masz ograniczenia dotyczące klucza obcego, gwarantowana jest integralność referencyjna, a zapytanie w twoim Q to już najlepsza odpowiedź.

Podano również indeksy na Table_B.ID i Table_C.ID.

If przypadkach dopasowania w Table_Arzadko (mniej niż ~ 5% w zależności od rzędu z i dystrybucji danych) ą partial multi-column index pomogłoby wydajność:

CREATE INDEX table_a_special_idx ON "Table_A" ("ID_B", "ID_C") 
WHERE "ID_B" IS NOT NULL OR "ID_C" IS NOT NULL; 

PostgreSQL 9,2 o wskaźniku pokrycia (index-only scan w języku Postgres) może pomóc jeszcze bardziej - w takim przypadku uwzględnisz wszystkie kolumny zainteresowania w indeksie (nie w moim przykładzie). Zależy od kilku czynników, takich jak szerokość wiersza i częstotliwość aktualizacji w tabeli.

Powiązane problemy