2010-12-26 18 views
38

Nie wiem, jak to sformułować, więc pomóż mi również z tytułem. :)Wybierz liczbę wierszy w innej tabeli w instrukcji Postgres SELECT

Mam dwa stoły. Nazwijmy je A i B. Tabela B ma klucz obcy a_id, który wskazuje na A.id. Teraz chciałbym napisać oświadczenie SELECT, które pobiera wszystkie zapisy A, z dodatkową kolumną zawierającą liczby rekordów B dla każdego wiersza A dla każdego wiersza w zestawie wyników.

Używam teraz Postgresql 9, ale myślę, że byłoby to ogólne pytanie SQL?

EDIT:

W końcu poszedłem do sporządzania roztworu spust-cache, gdzie A.b_count jest aktualizowany za pomocą funkcji za każdym razem B zmian.

+0

To może być lepiej użyć JOIN ze względu na wydajność. –

Odpowiedz

65
SELECT A.*, (SELECT COUNT(*) FROM B WHERE B.a_id = A.id) AS TOT FROM A 
+4

Czy ten typ zagnieżdżonego selektora ma obniżenie wydajności, o które warto się martwić? – hayavuk

+0

Tak, jest. Zagnieżdżone zaznaczenie zostanie wykonane dla każdego wiersza, który jest pobierany z tabeli A. –

+0

Hm, więc domyślam się, że znacznie wydajniej byłoby utworzyć kolumnę w tabeli A i zaktualizować wartość za pomocą wyzwalacza, gdy tabela B jest zmodyfikowany? – hayavuk

0

Aby odpowiedzieć na moje własne pytanie:

SELECT a.id, a.other_column, ..., 
(SELECT COUNT(*) FROM b where b.a_id = a.id) AS b_count 
FROM a; 
9

Rozwiązanie Podzapytanie podana powyżej jest nieefektywne. Rozwiązanie wyzwalania jest prawdopodobnie najlepszym w większości-odczytu bazy danych, ale dla przypomnienia oto dołączyć podejście, które będzie działać lepiej niż podkwerendzie:

SELECT a.id, a.xxx, count(*) 
FROM a JOIN b ON (b.a_id = a.id) 
GROUP BY a.id, a.xxx 

Jeśli używasz Django ORM można po prostu napisać:

res = A.objects.annotate(Count('b')) 
print res[0].b__count # holds the result count 
+0

Hm, wydaje się, że jest na to wiele sposobów.:) Wmówiłem sobie wyzwalacze, a ponieważ jest to głównie przeczytana część aplikacji (jest to lista elementów typu katalogów z liczbą elementów na rekord katalogu na desce rozdzielczej), myślę, że jest to najbezpieczniejszy zakład. – hayavuk

+0

co, jeśli masz dziesiątki kolumn w? –

+2

Od PostgreSQL 9.1 wystarczy "GROUP BY primary_key_column", we wcześniejszych wersjach musielibyśmy nazwać wszystkie wybrane kolumny w GROUP BY. – intgr

17

Myślę, że komentarz @intgr w innej odpowiedzi jest tak cenny Przedstawiam to jako alternatywną odpowiedź, ponieważ ta metoda pozwala skutecznie filtrować obliczoną kolumnę.

SELECT 
    a.* 
    COUNT(b.id) AS b_count 

FROM a 
INNER JOIN b on b.a_id = a.id 
WHERE a.id > 50 AND b.ID < 100 -- example of filtering joined tabled 

GROUP BY a.id 
HAVING COUNT(b.id) > 10 -- example of filtering calculated column 
ORDER BY a.id 
3

Przyjęta odpowiedź jest nieskuteczna (powolna) w oparciu o moje testy. Podzapytanie tabeli B wykonywane dla każdego wiersza tabeli A. Korzystam z następującego podejścia opartego na grupowaniu i łączeniu. To działa znacznie szybciej:

SELECT A.id, QTY.quantity FROM A 
LEFT JOIN 
    (SELECT COUNT(B.a_id) AS quantity, B.a_id FROM B GROUP BY B.a_id) AS QTY 
ON A.id = QTY.a_id 

Inny wariant:

SELECT A.id, COUNT(B.a_id) AS quantity FROM A 
LEFT JOIN B ON B.a_id = A.id 
GROUP BY A.id 
Powiązane problemy