【问题标题】:SQL Query to Count() multiple tablesSQL查询Count()多个表
【发布时间】:2009-09-04 14:47:12
【问题描述】:

我有一个表,它与其他表有几个一对多的关系。假设主表是一个人,其他表代表宠物、汽车和儿童。我想要一个返回此人详细信息的查询,例如他们拥有的宠物、汽车和孩子的数量。

Person.Name 计数(汽车) 计数(儿童) 计数(宠物) 约翰史密斯3 2 4 鲍勃布朗 1 3 0

最好的方法是什么?

【问题讨论】:

    标签: sql oracle plsql


    【解决方案1】:

    子查询分解 (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
    

    使用内联视图:

       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
    

    【讨论】:

      【解决方案2】:

      您可以使用COUNT(distinct x.id) 合成器:

      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
      

      【讨论】:

      • 投了这个票,因为它非常简单。
      【解决方案3】:

      我可能会这样做:

      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
      

      【讨论】:

        【解决方案4】:

        请注意,这取决于您的 RDBMS 风格,是否支持嵌套选择,如下所示:

        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,这至少适用于 PostgreSQL 和 MSSQL。未经测试,因此您的里程可能会有所不同。

        【讨论】:

        • 这是最简单的解决方案,但由于相关子查询可能会导致性能不佳。但是,如果您的数据库很小,则可能无关紧要。
        • 很高兴看到你给出你的技术答案你的个人风格;)
        • @Eric:你是对的,但是,数据库可能会重写这个语句。所以最终它可能会成为你所提议的。
        • 睡眠不足会导致失去自制力:-)
        【解决方案5】:

        使用子选择不是很好的做法,但可能在这里会很好

        select p.name, (select count(0) from cars c where c.idperson = p.idperson), (从子 ch 中选择 count(0),其中 ch.idperson = p.idperson), (从宠物 pt 中选择 count(0),其中 pt.idperson = p.idperson) 来自人 p

        【讨论】:

          【解决方案6】:

          你可以用三个外连接来做到这一点:

          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
          

          我相信 Oracle 现在支持 case when 语法,但如果不支持,您可以使用解码。

          【讨论】:

          • Oracle 从 9i 开始就支持 CASE 语句。
          • Rexem,感谢您的澄清!我写了一些讨厌的解码语句,并且很想能够使用 case 语句!道格。
          • 你也可以只使用count(cars.id)等,它不会计算空值。
          【解决方案7】:

          您需要在查询中包含多个计数语句。在我的脑海中,

          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
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2012-12-26
            • 2011-12-11
            • 1970-01-01
            • 2020-02-12
            • 2014-10-22
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多