使用带有年和月(时间段)的 SQL Server 窗口函数

问题描述 投票:0回答:2

请考虑这个脚本:

Declare @tbl Table
(
    F1    int,
    F2    int,
    Year  int,
    Month tinyint
)

Insert into @tbl 
values
(10, 1, 2020, 1),
(10, 1, 2020, 2),
(10, 1, 2020, 3),
(10, 1, 2020, 4),
(10, 2, 2020, 5),
(10, 1, 2020, 6),
(10, 1, 2020, 7),
(11, 1, 2020, 1),
(11, 1, 2020, 2),
(11, 2, 2020, 3),
(11, 2, 2020, 4),
(11, 1, 2020, 5),
(12, 1, 2020, 1),
(12, 1, 2020, 2),
(12, 1, 2020, 3),
(12, 1, 2020, 4)

我写了这个查询:

Select 
    F1, F2, year, month,
    Row_number() over (partition by F1, F2 order by year, month) as rownumber,
    Rank() over (partition by F1, F2 order by year, month) as rnk
From 
    @tbl
Order by 
    f1, f2, year, month

此查询返回此结果集:

F1 F2 年份 行号 Rnk
10 1 2020 1 1 1
10 1 2020 2 2 2
10 1 2020 3 3 3
10 1 2020 4 4 4
10 1 2020 6 5 5
10 1 2020 7 6 6
10 2 2020 5 1 1
11 1 2020 1 1 1
11 1 2020 2 2 2
11 1 2020 5 3 3
11 2 2020 3 1 1
11 2 2020 4 2 2
12 1 2020 1 1 1
12 1 2020 2 2 2
12 1 2020 3 3 3

但我想要这个结果:

F1 F2 年份 顺序
10 1 2020 1 1
10 1 2020 2 2
10 1 2020 3 3
10 1 2020 4 4
10 2 2020 5 1
10 1 2020 6 1
10 1 2020 7 2
11 1 2020 1 1
11 1 2020 2 2
11 2 2020 3 1
11 2 2020 4 2
11 1 2020 5 1
12 1 2020 1 1
12 1 2020 2 2
12 1 2020 3 3
12 1 2020 4 4

即在每组相同的F1值中,
按年然后月顺序,
在每次 F2 更改时重置年月序列号

怎样才能达到我想要的结果?谢谢

sql sql-server window-functions
2个回答
3
投票

这基本上是变相的差距和孤岛问题。 这里,每个“岛”是属于同一个

F1
块的一组记录,其中
F2
值在年份和月份中没有按顺序变化。 我们可以生成一个伪组来跟踪这些记录,然后使用
ROW_NUMBER()
来生成最终的序列值。 请注意,当
F1
块结束或
F2
值发生变化时,此伪组值重置为 1。

WITH cte AS (
    SELECT *, CASE WHEN LAG(F2) OVER (PARTITION BY F1 ORDER BY Year, Month) != F2 THEN 1 ELSE 0 END AS flag
    FROM @tbl
),
cte2 AS (
    SELECT *, SUM(flag) OVER (PARTITION BY F1 ORDER BY Year, Month) grp
    FROM cte
)

SELECT F1, F2, Year, Month,
       ROW_NUMBER() OVER (PARTITION BY F1, grp ORDER BY Year, Month) AS seq
FROM cte2
ORDER BY F1, Year, Month;

演示


1
投票

确定边界后(F1分组序列中F2发生变化的时刻),您可以从所有后续位置中减去最后一个边界的位置:

With bordered as
(
    Select 
        *,
        Row_number() over win as pos,
        case when F2 = lag(F2) over win then null else Count(1) over win - 1 end as ignore_before
    From 
        @tbl
    Window win As (partition by F1 order by year, month)
)
Select F1, F2, year, month, pos - max(ignore_before) over win Sequence
from bordered
Window win As (partition by F1 order by year, month)
Order by
    f1, year, month;

这里它运行于SQLFiddle

因为您需要窗口位置的窗口最大值,
每个窗口函数至少需要一次通过,
因此必须使用 CTE。

请注意,我使用了窗口定义以避免重复使用(因此,避免复制错误)。

© www.soinside.com 2019 - 2024. All rights reserved.