【问题标题】:Select distinct customer_id选择不同的 customer_id
【发布时间】:2020-10-04 07:18:25
【问题描述】:

我想为article_id 级别上的每个store_id计数

  • 有多少共享article_id's 分别首先到达 store_A 和 store_B。

  • 如果 arrival_timestamp 为例如article_id=2 for store_A

请看下面的例子:

主表


arrival_timestamp           article_id   store_id

2019-04-01 11:04             2            A
2019-04-01 13:12             2            B
2019-04-01 08:24             4            A
2019-04-01 10:24             4            B
2019-04-10 07:00             7            A
2019-04-10 10:14             7            B
2019-04-23 07:34             9            A
2019-04-23 05:52             9            B

输出表


storeA_count_first_articles     storeB_count_first_articles
3                                1

【问题讨论】:

  • SQL 不允许基于结果的列 - 您需要将数据作为行返回(即使 PIVOTUNPIVOT 需要命名列)。
  • 不能添加case语句吗:例如对于 article_id =2 当到达_timestamp storeA

标签: sql date count presto


【解决方案1】:

您可以使用两个级别的聚合:

select
    sum(case when arrival_timestamp_a < arrival_timestamp_b then 1 else 0 end) storeA_count_first_articles,
    sum(case when arrival_timestamp_b < arrival_timestamp_a then 1 else 0 end) storeB_count_first_articles
from (
    select 
        article_id,
        min(case when store_id = 'A' then arrival_timestamp end) arrival_timestamp_a,
        min(case when store_id = 'B' then arrival_timestamp end) arrival_timestamp_b
    from mytable
    group by article_id
) t

子查询使用条件聚合来计算每篇文章在 eacn 商店中的首次到达日期。然后,外层查询比较每篇文章的初到时间戳并产生最终结果。

另一个选项使用row_number(),它避免了子查询中的条件逻辑和聚合:

select 
    sum(case when store_id = 'A' then 1 else 0 end) storeA_count_first_articles,
    sum(case when store_id = 'B' then 1 else 0 end) storeB_count_first_articles
from (
    select 
        t.*, 
        row_number() over(partition by article_id order by arrival_timestamp) rn
    from mytable t
) t
where rn = 1

【讨论】:

    【解决方案2】:

    我不熟悉 Presto,但我认为这应该基于他们的文档。此答案是通用解决方案,无需在查询中具体命名 Store A 和 Store B。

    SELECT
        q.first_store_id AS store_id,
        COUNT(*) AS count_first_articles
    FROM
        (
            SELECT
                article_id,
                first_value( store_id ) OVER ( ORDER BY arrival_timestamp ) AS first_store_id
            FROM
                table
            GROUP BY
                article_id
        ) AS q
    GROUP BY
        first_store_id
    
    

    这适用于任意数量的store_id 值,无需手动定义每一列 - 因为结果是面向行而不是面向列的,所以它们更容易在应用程序代码中处理。如果您仍然想要命名列,您可以在外部查询中执行此操作或使用PIVOT/UNPIVOT(嗯,apparently Presto doesn't support PIVOT yet - 但您仍然可以在应用程序代码中执行此操作)

    你会得到这样的结果:

    store_id        count_first_articles
          A                            3
          B                            1
    

    The magic is in the first_value which is a Window Function,Presto 内置了一组不错的窗口函数。

    要将基于行的结果转换为基于列的原始示例输出,请执行以下操作:

    SELECT
        SUM( CASE WHEN q2.store_id = 'A' THEN q2.count_first_articles END ) AS storeA_count_first_articles,
        SUM( CASE WHEN q2.store_id = 'B' THEN q2.count_first_articles END ) AS storeB_count_first_articles
    FROM
        (
            SELECT
                q.first_store_id AS store_id,
                COUNT(*) AS count_first_articles
            FROM
                (
                    SELECT
                        article_id,
                        first_value( store_id ) OVER ( ORDER BY arrival_timestamp ) AS first_store_id
                    FROM
                        table
                    GROUP BY
                        article_id
                ) AS q
            GROUP BY
                first_store_id
        ) AS q2
    

    给予:

    storeA_count_first_articles     storeB_count_first_articles
    3                                1
    

    虽然这个答案表面上比其他答案更复杂(嗯,更多嵌套),但它是一个通用解决方案,当您想查看除@987654331 之外的更多商店时,不需要修改@和'B'

    【讨论】:

    • 第二个查询出现以下错误:ORDER BY 表达式 'arrival_timestamp' 必须是聚合表达式或出现在 GROUP BY 子句中
    • @user12625679 第一个查询得到什么输出或结果?
    【解决方案3】:

    您可以使用两个级别的聚合。一种方法是:

    select sum(case when first_store_id = 'A' then 1 else 0 end) as first_a,
           sum(case when first_store_id = 'B' then 1 else 0 end) as first_b       
    from (select distinct article_id,
                 first_value(store_id) over (partition by article_id order by arrival_timestamp) as first_store_id
          from t
         ) t;
    

    注意:内部聚合使用select distinct 是为了方便。外部聚合不使用group by,因为您只需要结果集中的一行。

    这也可以使用 min_by() 和显式聚合在 Presto 中编写:

    select sum(case when first_store_id = 'A' then 1 else 0 end) as first_a,
           sum(case when first_store_id = 'B' then 1 else 0 end) as first_b       
    from (select article_id, min_by(store_id, arrival_timestamp) as first_store_id
          from t
          group by article_id
         ) t;
    

    注意:这两个查询都假设您没有其他商店。如果您这样做并且您只关心这两个,请在查询中添加 where store_id in ('A', 'B')

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-11-15
      • 1970-01-01
      • 2019-04-10
      • 2023-04-05
      • 2011-09-28
      • 2011-11-07
      • 2011-04-11
      相关资源
      最近更新 更多