如何在同一输出行

时间:2016-03-11 15:56:59

标签: sql postgresql aggregate

我正在尝试从两个已存在的表中提取事务详细信息:

  1. transactions,包含收到的总金额,

  2. bills,在交易中收到的每张账单都有一行,并包含账单的面额。

  3. 两者都使用常见的session ID编制索引。 [更正:只有transactions表在session ID上编入索引。]

    我加入了表格并制作子查询来计算每笔交易的每个账单面额的数量(多少10s,20s等)。我想为每个交易获得一条记录,同一行包含所有计数。

    我在这个查询中做到了:

    SELECT
    t.session,
    to_char(t.amount::numeric, '"$"9990D99') AS "USD",
    (select count(b.denom) where b.denom = '50' ) AS "50",
    (select count(b.denom) where b.denom = '20') AS "20",
    (select count(b.denom) where b.denom = '10') AS "10",
    (select count(b.denom) where b.denom = '5') AS "5",
    (select count(b.denom) where b.denom = '1') AS "1"
    
    FROM transactions AS t JOIN bills AS b USING (session)
    GROUP BY
    t.session, t.amount, b.denom
    ORDER BY 
    t.session,
    b.denom ASC;
    

    ...这正确地给了我账单的数量,但每个面额有一行:

       session    |    USD    | 50 | 20 | 10 | 5 | 1
    --------------+-----------+----+----+----+---+----
     c64af32f1815 | $  135.00 |    |    |    | 1 |
     c64af32f1815 | $  135.00 |    |    |  1 |   |
     c64af32f1815 | $  135.00 |    |  6 |    |   |
     643e096b6542 | $  175.00 |    |    |    |   | 10
     643e096b6542 | $  175.00 |    |    |    | 1 |
     643e096b6542 | $  175.00 |    |  8 |    |   |
     ce7d2c647eff | $  200.00 |  4 |    |    |   |
    

    我想要的是每个交易一行:

       session    |    USD    | 50 | 20 | 10 | 5 | 1
    --------------+-----------+----+----+----+---+----
     c64af32f1815 | $  135.00 |    |  6 |  1 | 1 |
     643e096b6542 | $  175.00 |    |  8 |    | 1 | 10
     ce7d2c647eff | $  200.00 |  4 |    |    |   |
    

    修复此查询需要了解哪些内容?

    修改查询(遵循@erwin建议以避免子查询):

    SELECT
    t.session,
    to_char(t.amount::numeric, '"$"9990D99') AS "USD",
    COUNT(NULLIF(b.denom = '100', FALSE)) AS "100",
    COUNT(NULLIF(b.denom = '50', FALSE)) AS "50",
    COUNT(NULLIF(b.denom = '20', FALSE)) AS "20",
    COUNT(NULLIF(b.denom = '10', FALSE)) AS "10",
    COUNT(NULLIF(b.denom = '5', FALSE)) AS "5",
    COUNT(NULLIF(b.denom = '1', FALSE)) AS "1"
    
    FROM transactions AS t JOIN bills AS b USING (session)
    GROUP BY
    t.session, t.amount, b.denom
    ORDER BY 
    t.session,
    b.denom ASC;
    

    此查询仍会为每个聚合(计数)函数调用生成一行输出。

3 个答案:

答案 0 :(得分:0)

我想你差不多了。您需要在查询后执行另一个组。例如:

WITH q1 as (SELECT
    t.session,
    to_char(t.amount::numeric, '"$"9990D99') AS "USD",
    (select count(b.denom) where b.denom = '50' ) AS "50",
    (select count(b.denom) where b.denom = '20') AS "20",
    (select count(b.denom) where b.denom = '10') AS "10",
    (select count(b.denom) where b.denom = '5') AS "5",
    (select count(b.denom) where b.denom = '1') AS "1"

    FROM transactions AS t JOIN bills AS b USING (session)
    GROUP BY t.session, t.amount, b.denom)

SELECT session, "USD", SUM("50") AS "50", SUM("20") AS "20", SUM("10") AS "10",
       SUM("5") AS "5", SUM("1") AS "1"
FROM q1
GROUP BY session, "USD"

答案 1 :(得分:0)

不要使用相关子查询。效率低下 在<{1}}子句中 包含b.denom 。这是您的主要错误。

Postgres 9.4 +

在Postgres 9.4 或更高版本中使用专用聚合GROUP BY功能:

FILTER

解释和更多链接:

Postgres 9.3 -

对于旧版本(Postgres 9.3 或更早版本),有多种(不那么优雅)替代品:

SELECT t.session
     , to_char(t.amount::numeric, '"$"9990D99') AS "USD"
     , count(*) FILTER (WHERE b.denom = '50')   AS "50"  -- !
     , count(*) FILTER (WHERE b.denom = '20')   AS "20"  -- !
     , ...
FROM   ...
GROUP  BY t.session, t.amount  -- !
ORDER  BY ...

清理后:

SELECT t.session
     , to_char(t.amount::numeric, '"$"9990D99') AS "USD"  -- why cast to numeric?
     , count(b.denom = '100' OR NULL) AS "100"  -- bad column name
     , count(b.denom =  '50' OR NULL) AS "50"
     , count(b.denom =  '20' OR NULL) AS "20"
     , count(b.denom =  '10' OR NULL) AS "10"
     , count(b.denom =   '5' OR NULL) AS "5"
     , count(b.denom =   '1' OR NULL) AS "1"
FROM   transactions t
JOIN   bills b USING (session)
GROUP  BY t.session, t.amount
ORDER  BY t.session;

改变了什么?

  • 如果您可以避免使用非法标识符,那么您也不需要双引号。

  • 假设SELECT t.session, to_char(t.amount, '"$"9990D99') AS usd , d100, d50, d20, d10, d5, d1 FROM transactions t LEFT JOIN ( SELECT session , nullif(count(denom = 100 OR NULL), 0) AS d100 , nullif(count(denom = 50 OR NULL), 0) AS d50 , nullif(count(denom = 20 OR NULL), 0) AS d20 , nullif(count(denom = 10 OR NULL), 0) AS d10 , nullif(count(denom = 5 OR NULL), 0) AS d5 , nullif(count(denom = 1 OR NULL), 0) AS d1 FROM bills GROUP BY 1 ) b USING (session) ORDER BY session; integer,因此我们也不需要围绕常量使用单引号。

  • 在您没有找到帐单的列中,您似乎想要bills.denom。将结果包装在NULL

  • 由于您检索整个表格,因此在加入之前汇总的速度会更快。

SQL Fiddle.

更多技巧和解释:

更快的替代方案:NULLIF()

要获得最佳效果,请使用crosstab()的实际交叉制表。您需要安装附加模块crosstab() 如果您不熟悉,请先阅读

tablefunc

答案 2 :(得分:0)

你可以添加查询全局:

SELECT
session,
"USD",
sum("50") AS "50",
sum("20") AS "20",
sum("10") AS "10",
sum("5") AS "5",
sum("1") AS "1"

from (SELECT
t.session,
to_char(t.amount::numeric, '"$"9990D99') AS "USD",
(select count(b.denom) where b.denom = '50' ) AS "50",
(select count(b.denom) where b.denom = '20') AS "20",
(select count(b.denom) where b.denom = '10') AS "10",
(select count(b.denom) where b.denom = '5') AS "5",
(select count(b.denom) where b.denom = '1') AS "1"

FROM transactions AS t JOIN bills AS b USING (session)
GROUP BY
t.session, t.amount, b.denom
ORDER BY 
t.session,
b.denom ASC)youre_query
GROUP BY session,"USD"