2009-09-04 14 views
10

Ho una tabella che ha diverse relazioni uno a molti con altre tabelle. Diciamo che il tavolo principale è una persona, e le altre tabelle rappresentano animali domestici, automobili e bambini. Vorrei una richiesta che restituisca i dettagli della persona, il numero di animali domestici, automobili e bambini che hanno, ad es.Query SQL per conteggio() più tabelle

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

John Smith 3   2    4 
Bob Brown  1   3    0 

Qual è il modo migliore per farlo?

risposta

6

sottoquery Factoring (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 

Utilizzo delle viste in linea:

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

Nota, che dipende dal vostro gusto del RDBMS, se supporta seleziona nidificati come la seguente:

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, questo funziona, almeno con PostgreSQL e MSSQL. Non testato, quindi il tuo chilometraggio può variare.

+0

Questo * dovrebbe * funzionare in Oracle: non l'ho testato –

+2

Questa è la soluzione più semplice, ma probabilmente si tradurrebbe in scarse prestazioni a causa delle subquery correlate. Se il tuo database è piccolo, potrebbe non avere importanza. –

+0

interessante vedere che dai la tua risposta tecnica al tuo gusto personale;) – paweloque

0

Utilizzando subselect non pratica molto buona, ma può essere qui sarà bene

 
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

Si potrebbe fare questo con tre outer join:

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 

I belive che Oracle supporta ora l'case when sintassi, ma se no potresti usare una decodifica.

+0

Oracle ha supportato le istruzioni CASE dal 9i. –

+0

Rexem, Grazie per il chiarimento! Ho scritto alcune brutte dichiarazioni di decodifica e mi sarebbe piaciuto essere stato in grado di usare la dichiarazione del caso! Doug. –

+0

Puoi anche usare count (cars.id), ecc. Non conterrà valori nulli. –

1

avrei probabilmente fare in questo modo:

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

Avresti bisogno di includere più istruzioni di conteggio nella query. Fuori della parte superiore della mia testa,

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

è possibile utilizzare il 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

Votato per questo perché è molto semplice. – jvangeld