【问题标题】:Merge lines over timespan in SCD2 table在 SCD2 表中合并跨时间跨度的行
【发布时间】:2021-03-30 08:54:48
【问题描述】:

我的下表来自 SCD2 表。从这个源表中,我只选择了几列,这导致几行看起来完全相似。我想删除不必要的行,那些包含相同数据的行,并让 ValidFrom 列显示第一个值,而 ValidTo 列显示“时间跨度组”中的最后一个值。

来源数据:

| Item     | Color      | ValidFrom     | ValidTo    |
| -------- | ---------- | ------------- | ---------- |
| Ball     | Red        | 2020-01-01    | 2020-03-24 |
| Ball     | Blue       | 2020-03-25    | 2020-04-12 |
| Ball     | Blue       | 2020-04-13    | 2020-05-07 |
| Ball     | Blue       | 2020-05-08    | 2020-11-14 |
| Ball     | Red        | 2020-11-15    | 9999-12-31 |
| Doll     | Yellow     | 2020-01-01    | 2020-03-24 |
| Doll     | Green      | 2020-03-25    | 2020-04-12 |
| Doll     | Green      | 2020-04-13    | 2020-05-07 |
| Doll     | Green      | 2020-05-08    | 2020-11-14 |
| Doll     | Pink       | 2020-11-15    | 9999-12-31 | 

我想要完成的是:

| Item     | Color      | ValidFrom     | ValidTo    |
| -------- | ---------- | ------------- | ---------- |
| Ball     | Red        | 2020-01-01    | 2020-03-24 |
| Ball     | Blue       | 2020-03-25    | 2020-11-14 |
| Ball     | Red        | 2020-11-15    | 9999-12-31 |
| Doll     | Yellow     | 2020-01-01    | 2020-03-24 |
| Doll     | Green      | 2020-03-25    | 2020-11-14 |
| Doll     | Pink       | 2020-11-15    | 9999-12-31 | 

请注意,物品球最初的颜色是红色,然后是蓝色,然后又变回红色。根据我所学到的,这让事情变得更复杂了。

感谢您的帮助。

【问题讨论】:

  • “完全相似”是矛盾的。这些行只是相似的——共享两个相同的值。不要使用令人困惑的术语使事情变得更加困难。
  • @SM我很抱歉让您感到困惑。由于英语不是我的母语,我有时会弄错一些词。

标签: sql sql-server tsql gaps-and-islands scd2


【解决方案1】:

您的数据非常有规律。您似乎只想合并没有重叠或间隙的相邻平铺记录。然而,以下处理差距和更普遍的重叠:

select item, color, min(validfrom), max(validto)
from (select t.*,
             sum(case when prev_validto >= dateadd(day, -1, validfrom)
                      then 0 else 1
                 end) over (partition by item order by validfrom) as grp
      from (select t.*,
                   lag(validto) over (partition by item, color order by validfrom) as prev_validto
            from t
            ) t
     ) t
group by item, color, grp;

您正在寻找原始数据中的行岛,其中“岛”具有相同的项目、颜色和相邻的日期。这通过查看相同项目和颜色的前一行来确定岛屿的开始位置。如果没有这样的行或者该行在当前行开始之前结束,则当前行是一个岛的开始。

grp 是“岛屿起点”的累积总和,可用于汇总并获得最终结果。

您的特定数据非常有限 - 完美地平铺了一行在下一行开始的前一天结束。你可以使用left join 做一些非常相似的事情:

select item, color, min(validfrom), max(validto)
from (select t.*,
             sum(case when tprev.color is null then 1 else 0
                 end) over (partition by t.item order by t.validfrom) as grp
      from t left join
           t tprev
           on tprev.item = t.item and
              tprev.color = t.color and
              tprev.validto = dateadd(day, -1, t.validfrom)
     ) t
group by item, color, grp
order by item, min(validfrom);

Here 是一个 db说明这两种方法的小提琴

【讨论】:

  • 这似乎成功了。非常感谢,戈登!
【解决方案2】:

这是孤岛和间隙问题。

您可以按如下方式使用解析函数:

Select item, color,
       min(validfrom) as validfrom,
       Max(validto) as validto
  From
(Select t.*,
       Sum(case when lged between validfrom and validto then 0 else 1 end) 
           over (partition by item, color order by validfrom) as sm
  From
(Select t.*,
       Lag(validto) over (partition by item, color order by validfrom) as lged
  From t) t) t
Group by item, color, sm

【讨论】:

  • 运行此脚本会生成两行 Ball - Blue,ValidTo 和 ValidFrom 分别为 2020-03-25 - 2020-05-07 和 2020-05-08 - 2020-11-14。这些应该在同一行。
【解决方案3】:

这确实是一个间隙和岛屿问题,其中岛屿是具有相同项目和颜色的相邻记录。

在这里,我建议使用行号之间的差异来定义组。这仅涉及一层嵌套,而不是使用lag() 时的两层,因此它应该是最有效的选择:

select item, color, min(validfrom) as validfrom, max(validto) as validto
from (
    select t.*,
        row_number() over(order by validfrom) as rn1,
        row_number() over(partition by item, color order by validfrom) as rn2
    from mytable t
) t
group by item, color, rn1 - rn2

【讨论】:

  • 运行此脚本会生成两行 Ball - Blue,ValidTo 和 ValidFrom 分别为 2020-03-25 - 2020-05-07 和 2020-05-08 - 2020-11-14。这些应该在同一行。
【解决方案4】:

由于行之间没有间隙或重叠,因此此查询就足够了

select item, color, 
       min(validfrom) as ValidFrom,
       max(validto) as ValidTo
from tTable
group by item, color
order by item, ValidFrom;

【讨论】:

  • 恐怕这对我没有帮助。第 1 行和第 5 行将组合在一起,ValidFrom 和 ValidTo 为 2020-01-01 - 9999-12-31,这是不正确的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-07-23
  • 1970-01-01
  • 1970-01-01
  • 2013-03-22
  • 1970-01-01
  • 2015-05-18
  • 1970-01-01
相关资源
最近更新 更多