2009-09-04 15 views
10

Mam tabelę, która ma kilka do wielu relacji z innymi tabelami. Powiedzmy, że głównym stołem jest osoba, a pozostałe stoły przedstawiają zwierzęta domowe, samochody i dzieci. Chciałbym zapytanie, które zwraca szczegóły osoby, liczbę zwierząt domowych, samochodów i dzieci, które mają np.Zapytanie SQL do liczenia() wielu tabel

 
Person.Name Count(cars) Count(children) Count(pets) 

John Smith 3   2    4 
Bob Brown  1   3    0 

Jaki jest najlepszy sposób na zrobienie tego?

Odpowiedz

6

Podkwerenda Faktoring (9i +):

WITH count_cars AS (
    SELECT t.person_id 
      COUNT(*) num_cars 
     FROM CARS c 
    GROUP BY t.person_id), 
    count_children AS (
    SELECT t.person_id 
      COUNT(*) num_children 
     FROM CHILDREN c 
    GROUP BY t.person_id), 
    count_pets AS (
    SELECT p.person_id 
      COUNT(*) num_pets 
     FROM PETS p 
    GROUP BY p.person_id) 
    SELECT t.name, 
      NVL(cars.num_cars, 0) 'Count(cars)', 
      NVL(children.num_children, 0) 'Count(children)', 
      NVL(pets.num_pets, 0) 'Count(pets)' 
    FROM PERSONS t 
LEFT JOIN count_cars cars ON cars.person_id = t.person_id 
LEFT JOIN count_children children ON children.person_id = t.person_id 
LEFT JOIN count_pets pets ON pets.person_id = t.person_id 

Używanie wbudowanych odsłony:

SELECT t.name, 
      NVL(cars.num_cars, 0) 'Count(cars)', 
      NVL(children.num_children, 0) 'Count(children)', 
      NVL(pets.num_pets, 0) 'Count(pets)' 
    FROM PERSONS t 
LEFT JOIN (SELECT t.person_id 
        COUNT(*) num_cars 
      FROM CARS c 
     GROUP BY t.person_id) cars ON cars.person_id = t.person_id 
LEFT JOIN (SELECT t.person_id 
        COUNT(*) num_children 
      FROM CHILDREN c 
     GROUP BY t.person_id) children ON children.person_id = t.person_id 
LEFT JOIN (SELECT p.person_id 
        COUNT(*) num_pets 
      FROM PETS p 
     GROUP BY p.person_id) pets ON pets.person_id = t.person_id 
0

Zauważ, że to zależy od smaku RDBMS, czy obsługuje zagnieżdżonych wybiera się jak następuje:

SELECT p.name AS name 
    , (SELECT COUNT(*) FROM pets e WHERE e.owner_id = p.id) AS pet_count 
    , (SELECT COUNT(*) FROM cars c WHERE c.owner_id = p.id) AS world_pollution_increment_device_count 
    , (SELECT COUNT(*) FROM child h WHERE h.parent_id = p.id) AS world_population_increment 
FROM person p 
ORDER BY p.name 

IIRC to działa przynajmniej z PostgreSQL i MSSQL. Nie testowane, więc Twój przebieg może się różnić.

+0

To * powinno * działać w Oracle - nie testowałem tego –

+2

To jest najprostsze rozwiązanie, ale prawdopodobnie spowoduje słabą wydajność ze względu na skorelowane podzapytania. Jeśli twoja baza danych jest niewielka, może nie mieć znaczenia. –

+0

Ciekawe, aby dać odpowiedź techniczną na swój osobisty smak;) – paweloque

0

Korzystanie podselekcji niezbyt dobre praktyki, ale może być tu będzie dobrze

 
select p.name, (select count(0) from cars c where c.idperson = p.idperson), 
       (select count(0) from children ch where ch.idperson = p.idperson), 
       (select count(0) from pets pt where pt.idperson = p.idperson) 
    from person p 
0

Można to zrobić ze trzy zewnętrzne przyłącza:

SELECT 
    Person.Name, 
    sum(case when cars.id is not null then 1 else 0 end) car_count, 
    sum(case when children.id is not null then 1 else 0 end) child_count, 
    sum(case when pets.id is not null then 1 else 0 end) pet_count 
FROM 
    Person 
LEFT OUTER JOIN 
    cars on 
    Person.id = cars.person_id 
LEFT OUTER JOIN 
    children on 
    Person.id = children.person_id 
LEFT OUTER JOIN 
    pets on 
    Person.id = pets.person_id 
GROUP BY 
    Person.Name 

wierzę, że Oracle obsługuje teraz case when składni, ale jeśli nie, możesz użyć dekodowania.

+0

Oracle obsługuje instrukcje CASE od 9i. –

+0

Rexem, Dzięki za wyjaśnienie! Napisałem kilka nieprzyjemnych instrukcji dekodowania i bardzo bym chciał móc skorzystać z tego oświadczenia! Doug. –

+0

Możesz także użyć count (cars.id), itp. Nie policzy wartości null. –

1

to pewnie zrobić to tak:

SELECT Name, PersonCars.num, PersonChildren.num, PersonPets.num 
FROM Person p 
LEFT JOIN (
    SELECT PersonID, COUNT(*) as num 
    FROM Person INNER JOIN Cars ON Cars.PersonID = Person.PersonID 
    GROUP BY Person.PersonID 
) PersonCars ON PersonCars.PersonID = p.PersonID 
LEFT JOIN (
    SELECT PersonID, COUNT(*) as num 
    FROM Person INNER JOIN Children ON Children.PersonID = Person.PersonID 
    GROUP BY Person.PersonID 
) PersonChildren ON PersonChildren.PersonID = p.PersonID 
LEFT JOIN (
    SELECT PersonID, COUNT(*) as num 
    FROM Person INNER JOIN Pets ON Pets.PersonID = Person.PersonID 
    GROUP BY Person.PersonID 
) PersonPets ON PersonPets.PersonID = p.PersonID 
-1

trzeba by zawierać wiele instrukcji liczy się w zapytaniu. Od szczytu głowy,

SELECT p.Name, COUNT(DISTINCT t.Cars), COUNT(DISTINCT o.Children), Count(DISTINCT p.Pets) 
FROM Person p 
INNER JOIN Transport t ON p.ID = t.PersonID 
LEFT JOIN Offspring o ON p.ID = o.PersonID 
LEFT JOIN Pets p ON p.ID = o.OwnerID 
GROUP BY p.Name 
ORDER BY p.Name 
5

można użyć COUNT(distinct x.id) synthax:

SELECT person.name, 
     COUNT(DISTINCT car.id) cars, 
     COUNT(DISTINCT child.id) children, 
     COUNT(DISTINCT pet.id) pets 
    FROM person 
    LEFT JOIN car ON (person.id = car.person_id) 
    LEFT JOIN child ON (person.id = child.person_id) 
    LEFT JOIN pet ON (person.id = pet.person_id) 
GROUP BY person.name 
+0

Zagłosowano na tę, ponieważ jest bardzo prosta. – jvangeld