2009-09-04 9 views
10

J'ai une table qui a plusieurs relations un à plusieurs avec d'autres tables. Disons que la table principale est une personne, et les autres tables représentent des animaux domestiques, des voitures et des enfants. Je voudrais une requête qui retourne les détails de la personne, le nombre d'animaux de compagnie, les voitures et les enfants qu'ils ont par exemple.SQL Query to Count() plusieurs tables

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

John Smith 3   2    4 
Bob Brown  1   3    0 

Quelle est la meilleure façon de faire cela?

Répondre

6

sous-requête 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 

Utilisation des vues inline:

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

Notez que cela dépend de votre goût de SGBDR, si elle prend en charge les sélections imbriquées comme les suivantes:

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, cela fonctionne au moins avec PostgreSQL et MSSQL. Non testé, votre kilométrage peut varier.

+0

Ce * devrait * fonctionner dans Oracle - Je ne l'ai pas testé –

+2

Ceci est la solution la plus simple, mais entraînerait probablement des performances médiocres en raison des sous-requêtes corrélées. Si votre base de données est petite, cela n'a pas d'importance. –

+0

intéressant de voir que vous donnez votre réponse technique à votre goût personnel;) – paweloque

0

L'utilisation subselects pas très bonne pratique, mais peut-être ici, il sera bon

 
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

Vous pouvez le faire avec trois jointures externes:

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 

Je belive que Oracle prend désormais en charge le case when syntaxe, mais sinon vous pourriez utiliser un décodage.

+0

Oracle a pris en charge les instructions CASE depuis 9i. –

+0

Rexem, Merci pour la clarification! J'ai écrit de vilaines déclarations de décodage et j'aurais aimé pouvoir utiliser la déclaration de cas! Doug. –

+0

Vous pouvez également utiliser count (cars.id), etc. Il ne comptera pas les valeurs nulles. –

1

je ne serais probablement pas comme ça:

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

Vous auriez besoin d'inclure des états de comptage multiples dans la requête. Du haut de ma tête,

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

vous pouvez utiliser le COUNT(distinct x.id) synthaxe:

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

A voté pour celui-ci parce que c'est très simple. – jvangeld