带分区的 SQL 分组

标签 sql sql-server grouping

我有下表:

ID  Rating  Rating_from Rating_to
1   2       2010-01-01  2011-01-01
1   2       2011-01-02  2012-02-01
1   3       2012-02-02  2013-03-01
1   2       2013-03-02  2013-04-01
1   2       2013-04-02  9999-12-31

它包含每个 ID 的评级,该评级是临时检查的。每次检查评级时,最后一行都会给出一个 Rating_to 日期,通常是新评级的前一天,并输入一个新行,其中包含新评级的 Rating_from 日期。实际评级日。 Rating_to 设置为 9999-12-31 而不是 NULL。 🤷‍♀️ 通常,评级保持不变。有时,评级会发生变化。随着时间的推移,ID 也可能会收到以前的评级。

如何获取每个 ID、每个评级的最早 Rating_from 日期和最新 Rating_to 日期,而不将具有相同评级值但散布的评级分组其他评级?

我正在尝试获取下表:

ID  Rating  Rating_from Rating_to
1   2       2010-01-01  2012-02-01
1   3       2012-02-02  2013-03-01
1   2       2013-03-02  NULL

使用上面的数据,我尝试按 IDRating 进行分组(并设置 MIN()MAX( ) 在 from 和 to 字段上),但是我只会得到两行,一行用于评级 2,另一行用于评级 3,即使有两个评级 2 的时段。

我问了一位同事,他建议使用 LAG()LEAD(),但我不确定这在这里有什么帮助。数据位于SQL Server 2017中,大约有100万个ID。非常欢迎任何建议。

我添加了以下带有真实表数据的TABLE CREATE脚本,希望有所帮助:

CREATE TABLE tbl(
  id INT,
  rating int,
  rating_from DATE,
  rating_to DATE
);

INSERT INTO tbl VALUES
  (1, 2, '2014-05-23', '2015-04-13'),
  (1, 2, '2015-04-14', '2015-06-02'),
  (1, 2, '2015-06-03', '2016-05-31'),
  (1, 2, '2016-06-01', '2018-03-22'),
  (2, 1, '2016-06-01', '9999-12-31'),
  (3, 3, '2016-06-01', '9999-12-31'),
  (1, 2, '2018-03-23', '2018-08-06'),
  (1, 3, '2018-08-07', '2018-08-21'),
  (1, 2, '2018-08-22', '2018-09-19'),
  (1, 2, '2018-09-20', '9999-12-31');

最佳答案

这是基于 Itzik Ben-Gan's Islands approach 的解决方案。它首先查找发生更改的行。生成这些的运行总计,以获得每个更改的唯一 ID,然后对更改进行分组。这是一种快速而优雅的方法。

With LagAndLead AS
(
SELECT 
ID,Rating,Rating_from,Rating_to
, CASE WHEN     LAG(Rating) OVER (PARTITION BY ID ORDER BY Rating_from) <> Rating 
    THEN 1 
    ELSE 0 
END AS IsStart
FROM tbl
),
Islands AS 
(
SELECT ID,Rating,Rating_from, rating_to
, SUM(IsStart) OVER (PARTITION BY ID ORDER BY Rating_from ROWS UNBOUNDED PRECEDING) AS IslandID
FROM LagAndLead
)
SELECT S.ID,MIN(S.Rating) AS Rating ,min(S.Rating_from) AS Rating_from, max(S.rating_to) AS rating_to
FROM Islands AS S
GROUP BY S.ID,S.IslandID

示例:dbfiddle.uk

关于带分区的 SQL 分组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58337565/

相关文章:

mysql - SQL JOIN PARENT AND CHILD 并获取父项和子项记录的计数

ruby-on-rails - Rails has_many 关联计数子行

mysql - 如何将 MySQL 的 `group by` 与已在 select 调用中用 `replace` 修改的列一起使用?

mysql - 如何在 where 子句中使用临时列

mysql - 如果我使用带有关键字 group by 的聚合函数,如何获得更多列?

mysql - 在关系数据库中存储和引用不可变的有序列表

mysql - Sql server 中日期时间的 UTC 时间戳

java - 编程决定 java 或 .Net,db 或无 db

ruby - 按相似性对字符串进行分组

java - HQL 日期差异(以分钟为单位)