在多个条件下聚合:转换为单个查询

时间:2012-10-05 08:46:32

标签: sql ruby-on-rails postgresql aggregate-functions

鉴于表格:

reservations (id, place_id, confirmed_at, paid_at)places (id, name)

我需要返回可以分别表达以下查询的聚合:

-- Confirmed
SELECT places.id, places.name, COUNT(reservations.*) as total_confirmed
FROM reservations
  INNER JOIN places ON places.id = reservations.place_id
WHERE
  reservations.confirmed_at IS NOT NULL
GROUP BY places.id, places.name

--  Paid
SELECT places.id, places.name, COUNT(reservations.*) as total_paid
FROM reservations
  INNER JOIN places ON places.id = reservations.place_id
WHERE
  reservations.paid_at IS NOT NULL
GROUP BY places.id, places.name

-- Paid Uncofirmed
SELECT places.id, places.name, COUNT(reservations.*) as total_paid_unconfirmed
FROM reservations
  INNER JOIN places ON places.id = reservations.place_id
WHERE
  reservations.paid_at IS NOT NULL AND reservations.confirmed_at IS NULL
GROUP BY places.id, places.name

如何将这些查询重写为一个并返回所有必要的内容?

3 个答案:

答案 0 :(得分:3)

SELECT places.id, places.name,
sum(case when (reservations.confirmed_at IS NOT NULL) then 1 else 0 end) as total_confirmed,
sum(case when (reservations.paid_at  IS NOT NULL) then 1 else 0 end) as total_paid,
sum(case when  (reservations.paid_at IS NOT NULL AND reservations.confirmed_at IS NULL) then 1 else 0 end) as total_confirmed_paid
FROM reservations
  INNER JOIN places ON places.id = reservations.place_id
GROUP BY places.id, places.name

答案 1 :(得分:1)

我更喜欢这样的查询,因为如果有一些places尚未拥有reservation或者可能已经有多个预订。仍然可以安全地计算SUM

SELECT  d.*, a.total_confirmed, b.total_paid, c.total_paid_unconfirmed
FROM    places d
        LEFT JOIN
        (
            SELECT places.id, places.name, COUNT(reservations.*) as total_confirmed
            FROM reservations
              INNER JOIN places ON places.id = reservations.place_id
            WHERE
              reservations.confirmed_at IS NOT NULL
            GROUP BY places.id, places.name
        ) a ON d.id = a.id
        LEFT JOIN
        (
            SELECT places.id, places.name, COUNT(reservations.*) as total_paid
            FROM reservations
              INNER JOIN places ON places.id = reservations.place_id
            WHERE
              reservations.paid_at IS NOT NULL
            GROUP BY places.id, places.name
        ) b ON d.id = b.id
        LEFT JOIN
        (
            SELECT places.id, places.name, COUNT(reservations.*) as total_paid_unconfirmed
            FROM reservations
              INNER JOIN places ON places.id = reservations.place_id
            WHERE
              reservations.paid_at IS NOT NULL AND reservations.confirmed_at IS NULL
            GROUP BY places.id, places.name
        ) c ON d.id = c.id

答案 2 :(得分:1)

这些查询可以使用union组合在一起,但在联合中,所有查询都必须返回相同的结果集,因为所有查询的输出必须相同。这可以按如下方式完成:

SELECT places.id, places.name,'total_confirmed' as totalType, COUNT(reservations.*) as total
FROM reservations   INNER JOIN places ON places.id = reservations.place_id 
WHERE   reservations.confirmed_at IS NOT NULL 
GROUP BY places.id, places.name  
union all
SELECT places.id, places.name,'total_paid' as totalType, COUNT(reservations.*) as total
FROM reservations   INNER JOIN places ON places.id = reservations.place_id 
WHERE   reservations.paid_at IS NOT NULL 
GROUP BY places.id, places.name  
union all
SELECT places.id, places.name,'total_paid_unconfirmed' as totalType, COUNT(reservations.*) as total
FROM reservations   INNER JOIN places ON places.id = reservations.place_id 
WHERE   reservations.paid_at IS NOT NULL AND reservations.confirmed_at IS NULL 
GROUP BY places.id, places.name