创建“部分”窗口函数以更新 SQL Server 中的数据

Posted

技术标签:

【中文标题】创建“部分”窗口函数以更新 SQL Server 中的数据【英文标题】:Create a 'partial' window function to update data in SQL Server 【发布时间】:2020-09-14 23:08:54 【问题描述】:

我有一个表,我想使用窗口函数来填充一些 NULL,但我只希望数据根据表中的 Rank 列向下流动。使用窗口函数(PARTITION BY),所有行都分配相同的数据,这不是这里的要求。

初始表在 Rank=2 和 ID=1 的列 A 和 B 中具有 NULL 值,我想用 Rank=1 的值填充它们。 C 列在 Rank=1 时为 NULL,在 Rank=2 和 ID=1 时为 15,需要保持不变。

这是初始表的结构、所需的输出以及一些示例代码。我不确定如何通过语句将排名合并到分区中 初始表

ID    A       B      C      Rank
---------------------------------
1     10      10     NULL   1
1     NULL    NULL   15     2
2     10      NULL   NULL   1
2     NULL    NULL   15     2
2     NULL    NULL   15     3

目标表

ID    A       B      C      Rank
---------------------------------
1     10      10     NULL   1
1     10      10     15     2
2     10      NULL   NULL   1
2     10      NULL   15     2
2     10      NULL   15     3  

SQL 查询:

SELECT
    ID,
    MAX(A) OVER (PARTITION BY ID),
    MAX(B) OVER (PARTITION BY ID),
    MAX(C) OVER (PARTITION BY ID),
    Rank
FROM 
    TBL;

正如预期的那样,按 ID 和 Rank 进行分区不会导致初始表发生变化

【问题讨论】:

通过在 Partition by...(PARTITION BY Id ORDER BY Rank)的括号中包含“ORDER BY”语句似乎有希望的结果,如果这是正确的解决方案,将更新帖子 【参考方案1】:

你可以使用first_value():

select
    id,
    coalesce(a, first_value(a) over (partition by id order by rnk)) a,
    coalesce(b, first_value(b) over (partition by id order by rnk)) b,
    coalesce(c, first_value(c) over (partition by id order by rnk)) c,
    rnk
from tbl;

注意rank 是一个语言关键字(如在窗口函数rank() over() 中),因此不是列名的好选择。我在查询中将其重命名为rnk

Demo on DB Fiddle

编号 |一个 |乙 | c | rnk -: | -: | ---: | ---: | --: 1 | 10 | 10 | | 1 1 | 10 | 10 | 15 | 2 2 | 10 | | | 1 2 | 10 | | 15 | 2 2 | 10 | | 15 | 3

【讨论】:

谢谢。是的,我没有在实际数据中使用排名。有趣的是,我使用 coalesce/first_value 得到了相同的结果,而不是只使用 max()。似乎我的数据对于给定列dbfiddle.uk/… 只有一个潜在(非空)值【参考方案2】:

很遗憾,SQL Server 不支持任何窗口函数中的ignore nulls。这是最好的方法。在您的示例中,您只有一个值或NULL。如果是这种情况,您可以使用累积最大值:

select id,
       coalesce(a, max(a) over (partition by id order by rank)) as a,
       coalesce(b, max(b) over (partition by id order by rank)) as b,
       coalesce(c, max(c) over (partition by id order by rank)) as c,
       rank
from tbl;

如果不是这样,逻辑就比较复杂了。但实际上,如果是这样的话,我建议你问一个 new 问题。让这个与您所呈现的数据有关。

【讨论】:

谢谢,但这并不完全正确,因为这不考虑单独的 ID。 GMB 提出的解决方案正在奏效 @hamela777 。 . .我看到。我确定了答案。【参考方案3】:

你几乎就在分区旁边。您需要使用一种相对不常用的方法来限制分区大小 - 在这种情况下,仅限所有前面的行(以便在计算行的值时不使用任何“未来”)。

CREATE TABLE #Temp (ID int, Rnk int, A int, B int, C int)
INSERT INTO #Temp (ID, Rnk, A, B, C)
    VALUES  (1, 1, 10,   10,   NULL),  
            (1, 2, NULL, NULL, 15),      
            (2, 1, 10,   NULL, NULL),    
            (2, 2, NULL, NULL, 15),      
            (2, 3, NULL, NULL, 15)  

SELECT      ID, Rnk, 
            MAX(A) OVER (PARTITION BY ID ORDER BY Rnk ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS A_Fill,
            MAX(B) OVER (PARTITION BY ID ORDER BY Rnk ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS B_Fill,
            MAX(C) OVER (PARTITION BY ID ORDER BY Rnk ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS C_Fill
    FROM    #Temp

我相信这会输出与上述相同的结果,例如,

ID | Rnk | A_Fill | B_Fill | C_Fill
1  | 1   | 10     | 10     | NULL
1  | 2   | 10     | 10     | 15
2  | 1   | 10     | NULL   | NULL
2  | 2   | 10     | NULL   | 15
2  | 3   | 10     | NULL   | 15

请注意,如果您为 ID = 2(具有 rnks 4 和 5)添加另外两行,其余为 NULL,此方法会将 15 放入“C”列,而上述 (COALESCE/FIRST_VALUE) 没有,我相信。

ID | Rnk | A_Fill | B_Fill | C_Fill
1  | 1   | 10     | 10     | NULL
1  | 2   | 10     | 10     | 15
2  | 1   | 10     | NULL   | NULL
2  | 2   | 10     | NULL   | 15
2  | 3   | 10     | NULL   | 15
2  | 4   | 10     | NULL   | 15
2  | 5   | 10     | NULL   | 15

这是一个 DB_Fiddle 显示差异(基于上面之前的)

【讨论】:

以上是关于创建“部分”窗口函数以更新 SQL Server 中的数据的主要内容,如果未能解决你的问题,请参考以下文章

如何创建 SQL Server 函数以返回 int?

SQL Server:合并语句的更新部分不起作用

sql server 怎么彻底删除

Microsoft SQL Server 2022 新特性之 T-SQL 语言增强

Microsoft SQL Server 2022 新特性之 T-SQL 语言增强

创建函数以更新 SQL 中的两个表