如何根据条件对sql中的行进行分组

Posted

技术标签:

【中文标题】如何根据条件对sql中的行进行分组【英文标题】:How can i group rows on sql base on condition 【发布时间】:2021-07-16 08:00:49 【问题描述】:

我正在使用 redshift sql,并希望将凭证期重叠的用户分组到一行中(显示最短开始日期和最长结束日期)

例如,如果我有这些记录,

我想用 redshift 来实现这个结果

由于第 1 行和第 2 行有重叠的日期,所以解释很简单,我想将它们组合在一起并得到 min(Start_date) 和 max(End_Date)

我真的不知道从哪里开始。尝试使用 row_number 对它们进行分区,但似乎效果不佳。这是我尝试过的。

select 
    id, 
    start_date, 
    end_date, 
    lag(end_date, 1) over (partition by id order by start_date) as prev_end_date,
    row_number() over (partition by id, (case when prev_end_date >= start_date then 1 else 0) order by start_date) as rn
from users

有什么建议吗?谢谢各位好心人。

【问题讨论】:

Redshift 还是 Postgres?这是两种截然不同的数据库产品 哦,对不起,红移。编辑标签 【参考方案1】:

这是一种孤岛问题。因为日期是任意的,所以我建议以下方法:

使用累积最大值获取当前日期之前的最大 end_date。 使用逻辑来确定何时没有总体(即新时期开始)。 开始的累积总和提供了组的标识符。 然后聚合。

作为 SQL:

select id, min(start_date), max(end_date)
from (select u.*,
             sum(case when prev_end_date >= start_date then 0 else 1
                 end) over (partition by id
                            order by start_date, voucher_code
                            rows between unbounded preceding and current row
                           ) as grp
      from (select u.*,
                   max(end_date) over (partition by id
                                       order by start_date, voucher_code
                                       rows between unbounded preceding and 1 preceding
                                      ) as prev_end_date                            
            from users u
           ) u
      ) u
group by id, grp;

【讨论】:

【参考方案2】:

另一种方法是使用递归 CTE:

将所有行划分为按id 分组并按start_dateend_date 排序的编号分区 迭代它们,为每一行计算 group_start_date(必须在最终结果中合并的行将具有相同的 group_start_date) 最后,您需要通过idgroup_start_date 对CTE 进行分组,从每个组中获取最大end_date

这里是对应的sqlfiddle:http://sqlfiddle.com/#!18/7059b/2

还有 SQL,以防万一:

WITH cteSequencing AS (
  -- Get Values Order
  SELECT *, start_date AS group_start_date,
      ROW_NUMBER() OVER (PARTITION BY id ORDER BY start_date, end_date) AS iSequence
  FROM users),

Recursion AS (
  -- Anchor - the first value in groups
  SELECT *
  FROM cteSequencing
  WHERE iSequence = 1
  UNION ALL
  -- Remaining items
  SELECT b.id, b.start_date, b.end_date,
    CASE WHEN a.end_date > b.start_date THEN a.group_start_date 
    ELSE b.start_date
    END
    AS groupStartDate,
    b.iSequence
  FROM Recursion AS a
  INNER JOIN cteSequencing AS b ON a.iSequence + 1 = b.iSequence AND a.id = b.id) 
SELECT id, group_start_date as start_date, MAX(end_date) as end_date FROM Recursion group by id, group_start_date ORDER BY id, group_start_date

【讨论】:

以上是关于如何根据条件对sql中的行进行分组的主要内容,如果未能解决你的问题,请参考以下文章

当必须根据条件对记录进行分组时如何选择最多 x 行

SQL根据开始和结束时间对满足条件的时间序列进行分组

仅在某些条件下使用 Redshift 中的 SQL 对具有相同名称的行进行分组

如何根据数据框中的值有条件地对数据进行分组?

在sql中使用case语句根据某些条件对列进行分组

根据 pandas 中的字典对数据帧的行进行分组并对相应的分子求和