如何在 PostgreSQL 的同一个查询中使用两个 SUM() 聚合函数?

aCa*_*lla 1 sql postgresql group-by subquery aggregate-functions

我有一个 PostgreSQL 查询,它产生以下结果:

SELECT   o.order || '-' || osh.ordinal_number AS order, 
         o.company,
         o.order_total,
         SUM(osh.items) AS order_shipment_total,
         o.order_type
FROM     orders o
         JOIN order_shipments osh ON o.order_id = osh.order_id
WHERE    o.order = [some order number]
GROUP BY o.order,
         o.company,
         o.order_total,
         o.order_type;

order   | company | order_total | order_shipment_total | order_type
-------------------------------------------------------------------
123-1   | A corp. | null        |  125.00              | new
123-2   | B corp. | null        |  100.00              | new
Run Code Online (Sandbox Code Playgroud)

我需要替换o.order_total(它不能正常工作)并总结 order_shipment_total 列的总和,这样,对于上面的例子,每一行都说 225.00。我需要上面的结果如下所示:

order   | company | order_total | order_shipment_total | order_type
-------------------------------------------------------------------
123-1   | A corp. | 225.00      |  125.00              | new
123-2   | B corp. | 225.00      |  100.00              | new
Run Code Online (Sandbox Code Playgroud)

我试过的

1.) 要替换o.order_total,我已经尝试过,SUM(SUM(osh.items))但收到错误消息,提示您不能嵌套聚合函数。

2.) 我试图将整个查询作为子查询并对order_shipment_total列求和,但是当我这样做时,它只是重复列本身。见下文:

SELECT   order,
         company,
         SUM(order_shipment_total) AS order_shipment_total,
         order_shipment_total,
         order_type
FROM     (
    SELECT   o.order || '-' || osh.ordinal_number AS order, 
             o.company,
             o.order_total,
             SUM(osh.items) AS order_shipment_total,
             o.order_type
    FROM     orders o
             JOIN order_shipments osh ON o.order_id = osh.order_id
    WHERE    o.order = [some order number]
    GROUP BY o.order,
             o.company,
             o.order_total,
             o.order_type
) subquery
GROUP BY order,
         company,
         order_shipment_total,
         order_type;

order   | company | order_total | order_shipment_total | order_type
-------------------------------------------------------------------
123-1   | A corp. | 125.00      |  125.00              | new
123-2   | B corp. | 100.00      |  100.00              | new
Run Code Online (Sandbox Code Playgroud)

3.) 我试图在上面的子查询/查询示例中只包含我实际想要分组的行,因为我觉得我能够在 Oracle SQL 中做到这一点。但是当我这样做时,我收到一条错误消息,提示“列 [名称] 必须出现在 GROUP BY 子句中或用于聚合函数中”。

...
GROUP BY order,
         company,
         order_type;

ERROR:  column "[a column name]" must appear in the GROUP BY clause or be used in an aggregate function.
Run Code Online (Sandbox Code Playgroud)

我该如何实现?我确信子查询将是答案,但我很困惑为什么这种方法不起作用。

Cai*_*ard 8

您对查询/方法不太了解的事情是,您实际上想要在同一查询行结果中进行两个不同级别的分组。子查询方法对了一半,但是当您执行分组的子查询时,在另一个分组的查询中,您只能使用您已经获得的数据(来自子查询),并且您只能选择将其保留在聚合级别详细信息已经是,或者您可以选择失去精度以支持更多分组。你不能为了进一步总结而保留细节而失去细节。因此,子查询查询(实际上)相对毫无意义,因为您不妨在一次点击中分组到您想要的级别:

SELECT groupkey1, sum(y) FROM
(SELECT groupkey1, groupkey2, sum(x) as y FROM table GROUP BY groupkey1, groupkey2)
GROUP BY groupkey1
Run Code Online (Sandbox Code Playgroud)

是相同的:

SELECT groupkey1, sum(x) FROM
table
GROUP BY groupky1
Run Code Online (Sandbox Code Playgroud)

戈登的答案可能会奏效(除了您展示的相同错误,因为分组集错误/未涵盖所有列)但它可能对您的理解没有多大帮助,因为它是仅代码答案. 以下是您需要如何解决这个问题的细分,但使用更简单的数据和前面的窗口函数以支持您已经知道的内容。

假设库存中有不同类型的苹果和甜瓜。无论购买日期如何,您都需要一个提供每种特定水果总数的查询。您还需要为每种水果整体类型的总计列:

细节:

fruit | type             | purchasedate | count
apple | golden delicious | 2017-01-01   | 3
apple | golden delicious | 2017-01-02   | 4
apple | granny smith     | 2017-01-04   ! 2
melon | honeydew         | 2017-01-01   | 1
melon | cantaloupe       | 2017-01-05   | 4
melon | cantaloupe       | 2017-01-06   | 2
Run Code Online (Sandbox Code Playgroud)

所以那是 7 个黄金美味,2 个史密斯奶奶,1 个蜜露,6 个哈密瓜,还有 9 个苹果和 7 个甜瓜

您不能将其作为一个查询*来执行,因为您需要两个不同级别的分组。您必须将其作为两个查询来执行,然后(关键理解点)您必须将不太精确的(苹果/甜瓜)结果连接回更精确的(史密斯奶奶/黄金美味/蜜露/哈密瓜):

SELECT * FROM
(
  SELECT fruit, type, sum(count) as fruittypecount
  FROM fruit
  GROUP BY fruit, type
) fruittypesum
INNER JOIN
(
  SELECT fruit, sum(count) as fruitcount
  FROM fruit
  GROUP BY fruit
) fruitsum
ON
  fruittypesum.fruit = fruitsum.fruit
Run Code Online (Sandbox Code Playgroud)

你会得到这个:

fruit | type             | fruittypecount | fruit | fruitcount
apple | golden delicious | 7              | apple | 9
apple | granny smith     | 2              | apple | 9
melon | honeydew         | 1              | melon | 7
melon | cantaloupe       | 6              | melon | 7
Run Code Online (Sandbox Code Playgroud)

因此,对于您的查询、不同的组、详细信息和摘要:

SELECT
    detail.order || '-' || detail.ordinal_number as order,
    detail.company,
    summary.order_total,
    detail.order_shipment_total,
    detail.order_type
FROM (
    SELECT   o.order,
             osh.ordinal_number, 
             o.company,
             SUM(osh.items) AS order_shipment_total,
             o.order_type
    FROM     orders o
             JOIN order_shipments osh ON o.order_id = osh.order_id
    WHERE    o.order = [some order number]
    GROUP BY o.order,
             o.company,
             o.order_type
) detail
INNER JOIN
(
    SELECT   o.order,
             SUM(osh.items) AS order_total
    FROM     orders o
             JOIN order_shipments osh ON o.order_id = osh.order_id
    --don't need the where clause; we'll join on order number
    GROUP BY o.order,
             o.company,
             o.order_type
) summary
ON
summary.order = detail.order
Run Code Online (Sandbox Code Playgroud)

Gordon 的查询使用一个窗口函数达到同样的效果;窗口函数在分组完成后运行,它建立了另一个级别的分组(PARTITION BY ordernumber),它是GROUP BY ordernumber摘要中 my 的有效等价物。窗函数汇总数据通过订单号与明细数据有内在的联系;查询说:

SELECT
  ordernumber,
  lineitemnumber,
  SUM(amount) linetotal
  sum(SUM(amount)) over(PARTITION BY ordernumber) ordertotal
GROUP BY
  ordernumber,
  lineitemnumber
Run Code Online (Sandbox Code Playgroud)

.. 将有一个ordertotal是linetotal订单中所有的总数:GROUP BY 将数据准备到行级别详细信息,窗口函数将数据准备到订单级别,并根据需要重复总计次数填写每个订单项。我用大写写了SUM属于 GROUP BY 操作的 ..sum小写属于分区操作。它必须sum(SUM())也不能简单地说,sum(amount)因为数量作为一列本身是不允许的 - 它不在 group by 中。因为数量本身是不允许的,必须对 group by 求和才能工作,所以我们必须让sum(SUM())分区运行(它在 group by 完成后运行)

它的行为与分组到两个不同的级别并连接在一起完全相同,实际上我选择了这种方式来解释它,因为它使您更清楚地了解它如何与您已经了解的组和连接相关联

请记住:JOINS 使数据集横向增长,UNIONS 使它们向下增长。当您有一些详细数据并且想要通过更多数据(摘要)横向增长时,请加入它。(如果您希望总数位于每列的底部,则将其合并)


*您可以将其作为一个查询来执行(没有窗口函数),但它会变得非常混乱,因为它需要各种最终不值得的技巧,因为它太难维护了