对 SQL 中的两个单独列使用 Group By 来计算新列

Posted

技术标签:

【中文标题】对 SQL 中的两个单独列使用 Group By 来计算新列【英文标题】:Use Group By against two separate columns in SQL to compute a new column 【发布时间】:2020-07-19 21:16:28 【问题描述】:

我有一个名为 transactionsmysql 表,如下所示:

|---------|--------------|--------------|--------------------------|
|order_id |customer_name |  brand_name  |   order_time_stamp       | 
|---------|--------------|--------------|--------------------------|
|   1     | Jack         |  Pepsi       | 2019-02-23 20:02:21.550. |
|---------|--------------|--------------|--------------------------|
|   2     | Dorothy      |  Fanta       | 2019-02-23 20:03:21.550. |
|---------|--------------|--------------|--------------------------|
|   3     | Dorothy      |  Fanta       | 2019-02-23 20:04:21.550. |
|---------|--------------|--------------|--------------------------|
|   4     | Jack         |  Fanta       | 2019-02-23 20:05:21.550. |
|---------|--------------|--------------|--------------------------| 

很明显,这是一个以 order_id 作为主键的表,它捕获在线商店的每个订单。我试图捕捉的是按品牌名称分组的额外订单数量,如下所示:

enter code here
|------------|--------------------|
| brand_name | additional orders  |
|------------|--------------------| 
| Pepsi      |         0          |
|------------|--------------------| 
| Fanta      |         1          | 
|------------|--------------------| 

但是,附加订单是在客户级别定义的,并且定义为客户第一次订购后所有订单的总和。

我的策略是使用 rank() 函数,如下所示:

select rank() over( partition by customer_name order by order_time_stamp) as rank
from transactions

这会创建一个额外的列,为每个客户创建一个排名。但是,我不确定现在如何将其分组到品牌级别并获得我所展示的输出

【问题讨论】:

为什么不是《芬达》2呢?杰克和多萝西都有芬达的第二笔订单。 额外的订单数量是品牌级别的..当杰克订购芬达时,这是第一次发生。 你们需要组队。 【参考方案1】:

您可以使用row_number() 对每个客户的订单进行排名,然后过滤“附加”订单(即排名大于 1 的每个订单),然后按brand_name 汇总:

select brand_name, count(*) no_additional_orders
from (
    select 
        t.*, 
        row_number() over(partition by customer_name order by order_time_stamp) rn
    from transactions t
) t
where rn > 1
group by brand_name

如果您还想考虑没有额外订单的品牌,则可以将过滤逻辑移至聚合函数:

select brand_name, sum(rn > 1) no_additional_orders
from (
    select t.*, row_number() over(partition by customer_name order by order_time_stamp) rn
    from transactions t
) t
group by brand_name

【讨论】:

【参考方案2】:

您的数据相当混乱。我认为您想要最早的时间戳之后的所有内容,而不是最早的顺序。这是一个微妙的区别,但很重要:

select brand_name,
       sum(order_time_stamp > min_ots)
from (select t.*, min(order_time_stamp) over (partition by customer_name) as min_ots
      from t
     ) t
group by brand_name;

您也可以使用rank() 做类似的事情:

select brand_name,
       sum(seqnum > 1)
from (select t.*, 
             rank() over (partition by customer_name order by order_time_stamp) as seqnum
      from t
     ) t
group by brand_name;

【讨论】:

【参考方案3】:

您希望将每个customer_name 的所有订单与brand_name 的所有订单相加,但 1 除外,因为您不希望每个客户的第一个订单相加。 您可以通过从订单总数中减去订购产品的不同客户的数量来做到这一点,该数量等于每个客户的第一个订单数量:

select brand_name, 
       count(*) - count(distinct customer_name) additional_orders
from transactions
group by brand_name

请参阅demo。 结果:

> brand_name | additional_orders
> :--------- | ----------------:
> Pepsi      |                 0
> Fanta      |                 1

【讨论】:

以上是关于对 SQL 中的两个单独列使用 Group By 来计算新列的主要内容,如果未能解决你的问题,请参考以下文章

SQL使用group by获取SUM,但有条件地对两列之一的内容求和

在 SQL 中使用 Group By 和 Aggregate - 获取错误“选择列表中的列无效,因为它不包含在聚合函数或 GROUP BY 中”

SQL选择列的总和最大的行(在GROUP BY中有两个字段)

sql语句中的group by啥意思

sql 您可以使用group by function连接postgres中的相同列,如下例所示

sql语句中的group by啥意思