您可以通过将版本控制和值组合到单个二进制列中来实现此目的,然后选择最大值。最短形式的查询是:
SELECT t.ID,
Value1 = CONVERT(VARCHAR(50), SUBSTRING(MAX(CONVERT(BINARY(4), t.Versioning)
+ CONVERT(VARBINARY(50), t.Value1)), 5, 50)),
Value2 = CONVERT(VARCHAR(50), SUBSTRING(MAX(CONVERT(BINARY(4), t.Versioning)
+ CONVERT(VARBINARY(50), t.Value2)), 5, 50)),
Value3 = CONVERT(INT, SUBSTRING(MAX(CONVERT(BINARY(4), t.Versioning)
+ CONVERT(VARBINARY(50), t.Value3)), 5, 50))
FROM YourTable AS t
GROUP BY ID;
为了解释发生了什么,我将只关注值 3,以及缩减样本数据。
该过程的第一步只是将排序列和值列组合成一个二进制值:
SELECT *,
BinaryValue3 = CONVERT(BINARY(2), t.Versioning) + CONVERT(BINARY(2), t.Value3)
FROM (VALUES (1, 2, 1), (1, 4, 2), (1, NULL, 3)) AS t (ID, Value3, Versioning)
这给出了:
ID Value3 Versioning BinaryValue3
--------------------------------------
1 2 1 0x00010002
1 4 2 0x00020004
1 NULL 3 NULL
然后我们取二进制值的最大值。这取决于两件事:
- 连接 NULL 将产生 NULL 的事实,因此非空记录只有一个二进制值
- 由于二进制值会从左到右排序,
MAX 函数将始终选择版本号最高的二进制值
然后,一旦我们有了最大的二进制值 (0x00020004),这只是提取右手边并将其转换回原始数据类型的情况。
完整的工作演示
DECLARE @T TABLE
(
ID INT NOT NULL,
Value1 VARCHAR(50),
Value2 VARCHAR(50),
value3 INT,
Versioning INT NOT NULL,
PRIMARY KEY (ID, Versioning)
);
INSERT @T (ID, Value1, Value2, Value3, Versioning)
VALUES
(1, 'sport', 'tennis', 2, 1),
(1, NULL, NULL, 4, 2),
(1, NULL, 'football', NULL, 3),
(1, 'game', NULL, NULL, 4);
SELECT t.ID,
Value1 = CONVERT(VARCHAR(50), SUBSTRING(MAX(CONVERT(BINARY(4), t.Versioning)
+ CONVERT(VARBINARY(50), t.Value1)), 5, 50)),
Value2 = CONVERT(VARCHAR(50), SUBSTRING(MAX(CONVERT(BINARY(4), t.Versioning)
+ CONVERT(VARBINARY(50), t.Value2)), 5, 50)),
Value3 = CONVERT(INT, SUBSTRING(MAX(CONVERT(BINARY(4), t.Versioning)
+ CONVERT(VARBINARY(50), t.Value3)), 5, 50))
FROM @T AS t
GROUP BY ID;
你也可以使用这个方法和窗口函数来为每一行添加最后一个非空值,所以如果你想填充所有空值,你可以使用最后一个非空值:
DECLARE @T TABLE
(
ID INT NOT NULL,
Value1 VARCHAR(50),
Value2 VARCHAR(50),
value3 INT,
Versioning INT NOT NULL,
PRIMARY KEY (ID, Versioning)
);
INSERT @T (ID, Value1, Value2, Value3, Versioning)
VALUES
(1, 'sport', 'tennis', 2, 1),
(1, NULL, NULL, 4, 2),
(1, NULL, 'football', NULL, 3),
(1, 'game', NULL, NULL, 4);
SELECT t.ID,
ActualValue1 = t.Value1,
ActualValue2 = t.Value2,
ActualValue3 = t.Value3,
LastNonNUllValue1 = CONVERT(VARCHAR(50), SUBSTRING(MAX(Value1Bin) OVER(PARTITION BY t.ID ORDER BY t.Versioning), 5, 50)),
LastNonNUllValue2 = CONVERT(VARCHAR(50), SUBSTRING(MAX(Value2Bin) OVER(PARTITION BY t.ID ORDER BY t.Versioning), 5, 50)),
LastNonNUllValue3 = CONVERT(INT, SUBSTRING(MAX(Value3Bin) OVER(PARTITION BY t.ID ORDER BY t.Versioning), 5, 50)),
t.Versioning
FROM @T AS t
CROSS APPLY
( SELECT Value1Bin = CONVERT(BINARY(4), t.Versioning) + CONVERT(VARBINARY(50), t.Value1),
Value2Bin = CONVERT(BINARY(4), t.Versioning) + CONVERT(VARBINARY(50), t.Value2),
Value3Bin = CONVERT(BINARY(4), t.Versioning) + CONVERT(VARBINARY(50), t.Value3)
) AS b
ORDER BY t.Versioning;
这给出了:
ID ActualValue1 ActualValue2 ActualValue3 LastNonNUllValue1 LastNonNUllValue2 LastNonNUllValue3 Versioning
------------------------------------------------------------------------------------------------------------------------------
1 sport tennis 2 sport tennis 2 1
1 NULL NULL 4 sport tennis 4 2
1 NULL football NULL sport football 4 3
1 game NULL NULL game football 4 4
更多阅读请看Itzik Ben-Gan's The Last non NULL Puzzle