(Presto)SQL:按列“A”和“B”以及计数列“C”分组,但也包括仅按“A”分组的“C”计数

Posted

技术标签:

【中文标题】(Presto)SQL:按列“A”和“B”以及计数列“C”分组,但也包括仅按“A”分组的“C”计数【英文标题】:(Presto) SQL: Group by on columns "A" and "B" and count column "C", but also include count of "C" grouped only by "A" 【发布时间】:2021-11-24 23:39:38 【问题描述】:

这个问题的标题感觉有点奇怪,所以如果你能想象出更好的标题,请随时提供帮助。

你好,

想象一下这样的情况 - 有一个包含 3 列的“Sales”表:datestoresale_price,每一行表示单个项目销售:


date           |  store  |  sale_price
---------------+---------+------------
2021-09-01     |   foo   |    15
2021-09-01     |   foo   |    10
2021-09-01     |   foo   |    10
2021-09-01     |   bar   |     5
2021-09-02     |   foo   |    30
2021-09-02     |   bar   |    40
2021-09-02     |   bar   |    20
etc...

我要做的是创建一个按datestore 分组的查询,并计算每家商店每天售出的商品数量(因此,不考虑价格)。到目前为止,这很容易,但出于可视化目的,我还尝试添加一个额外的行,该行每天还包括销售总数。

这是我正在寻找的最终结果:


date           |    store    |  sales_count
---------------+-------------+------------
2021-09-01     |     foo     |     3
2021-09-01     |     bar     |     1
2021-09-01     |  aggregate  |     4
2021-09-02     |     foo     |     1
2021-09-02     |     bar     |     2
2021-09-02     |  aggregate  |     3
etc...

我知道我可以通过 UNION ALL 来创建它,但它不是超级高效,因为它会扫描原始表两次:

SELECT date,
       store,
       count(sale_price) AS sales_count
  FROM sales
 GROUP BY 1, 2

 UNION ALL

SELECT date,
       'aggregate' AS store,
       count(sale_price) AS sales_count
  FROM sales
 GROUP BY 1

我也知道我可以使用over() 子句创建一个额外的列,并避免两次扫描“销售”,但是我会有两个不同的列,而不是像我正在寻找的那样:

SELECT date,
       store,
       count(sale_price) AS sales_count,
       sum(count(sale_price)) over(PARTITION BY date) AS sales_per_day
  FROM sales
 GROUP BY 1, 2

--->


date           |    store    |  sales_count |  sales_per_day
---------------+-------------+--------------+-----------------
2021-09-01     |     foo     |      3       |        4
2021-09-01     |     bar     |      1       |        4
2021-09-02     |     foo     |      1       |        3
2021-09-02     |     bar     |      2       |        3
etc...

是否有可能在不扫描两次的情况下实现我想要做的事情?最后两列(sales_countsales_per_day)可以以某种方式合并吗? 提前感谢您的帮助。

【问题讨论】:

我认为您需要 GROUP BY GROUPING SETS 语法。这是一个很好的例子,一目了然,我认为:sqlservertutorial.net/sql-server-basics/… @JS 有意思,我去看看谢谢 @JS 天哪,太棒了!我已经在 presto 中写了大约 2 年的查询,但我不知道这个东西存在。您应该将其设为答案,以便我将其标记为已接受。 【参考方案1】:

您可以使用GROUPING SETSCUBEROLLUP 在同一查询中的不同级别进行聚合。您还可以使用GROUPING 操作来确定给定输出行的组中考虑了哪些列:

WITH data(day, store, sale_price) AS (
    VALUES
        (DATE '2021-09-01', 'foo', 15),
        (DATE '2021-09-01', 'foo', 10),
        (DATE '2021-09-01', 'foo', 10),
        (DATE '2021-09-01', 'bar',  5),
        (DATE '2021-09-02', 'foo', 30),
        (DATE '2021-09-02', 'bar', 40),
        (DATE '2021-09-02', 'bar', 20)
)
SELECT day,
    if(grouping(store) = 1, '<aggregate>', store),
    count(sale_price) as sales_count
FROM data
GROUP BY GROUPING SETS ((day), (day, store))
ORDER BY day, grouping(store)

【讨论】:

以上是关于(Presto)SQL:按列“A”和“B”以及计数列“C”分组,但也包括仅按“A”分组的“C”计数的主要内容,如果未能解决你的问题,请参考以下文章

Presto on Spark:通过 Spark 来扩展 Presto

在 SQL 中按列计算 NULL 值

SQL数据分析概览——HiveImpalaSpark SQLDrillHAWQ 以及Presto+druid

Vertica SQL 用于按列获取数据

每天按列和行、计数和百分比分组

Hive sql和Presto sql的一些对比