SQL:如何根据条件用前一行值填充空单元格?
Posted
技术标签:
【中文标题】SQL:如何根据条件用前一行值填充空单元格?【英文标题】:SQL: How to fill empty cells with previous row value on basis of condition? 【发布时间】:2017-07-30 07:34:16 【问题描述】:SQL:根据条件用前一行值填充空单元格?
请将此视为高优先级请求..需要帮助
请求高级用户链接它(http://i.imgur.com/P4UOiMz.jpg)
我需要使用 SQL 生成下表中的“OXY_ID_NEW”列。这在 SQL 2008R2 或 SQL 2012 或 Amazon REDSHIFT 中是否可行?
SQL 表图片(http://i.imgur.com/P4UOiMz.jpg)
基本上,我想用该 ID 的最后一个已知 Oxy_id 向前填充空的“OXY_ID”单元格,如“OXY_ID_NEW”列中所示。
【问题讨论】:
【参考方案1】:可能是这样的
coalesce(lag(oxy_id) over (partition by id order by number), oxy_id)
..假设 id,列数实际上增加了.. 在屏幕截图中看起来它们重复了,在这种情况下您需要提供整个表定义。
【讨论】:
请记住,从 SQL 2012 开始提供 Lag @gordy 谢谢,我会检查代码并告诉你。【参考方案2】:gordy 给出的 LAG 示例是最简单的,只要您有它。请注意,您不能直接使用它来更新您的表格。窗口函数只能出现在 SELECT 或 ORDER BY 子句中,因此您需要一个临时表。
对于旧版本,您需要一个光标。比如:
declare @demo table
(
id varchar (10),
number int,
oxy_id varchar(2)
)
INSERT INTO @demo VALUES ('308_2123', 36, 'ZY')
INSERT INTO @demo VALUES ('308_2123', 36, NULL)
INSERT INTO @demo VALUES ('308_2123', 37, NULL)
INSERT INTO @demo VALUES ('308_2123', 37, NULL)
INSERT INTO @demo VALUES ('308_2123', 38, 'WY')
INSERT INTO @demo VALUES ('308_2123', 38, 'WY')
INSERT INTO @demo VALUES ('308_2123', 38, NULL)
INSERT INTO @demo VALUES ('308_2123', 39, NULL)
INSERT INTO @demo VALUES ('309_5647', 30, 'AB')
INSERT INTO @demo VALUES ('309_5647', 30, NULL)
INSERT INTO @demo VALUES ('309_5647', 31, NULL)
INSERT INTO @demo VALUES ('309_5647', 32, 'BC')
INSERT INTO @demo VALUES ('310_8897', 20, 'CD')
INSERT INTO @demo VALUES ('310_8897', 21, 'DC')
INSERT INTO @demo VALUES ('310_8897', 22, NULL)
INSERT INTO @demo VALUES ('310_8897', 23, NULL)
INSERT INTO @demo VALUES ('310_8897', 23, NULL)
INSERT INTO @demo VALUES ('311_6789', 1, NULL)
INSERT INTO @demo VALUES ('311_6789', 1, NULL)
INSERT INTO @demo VALUES ('311_6789', 2, 'EF')
INSERT INTO @demo VALUES ('311_6789', 3, 'GH')
INSERT INTO @demo VALUES ('311_6789', 3, NULL)
INSERT INTO @demo VALUES ('312_9874', 1, 'HK')
INSERT INTO @demo VALUES ('312_9874', 1, 'KY')
INSERT INTO @demo VALUES ('312_9874', 1, NULL)
INSERT INTO @demo VALUES ('312_9874', 1, 'YY')
DECLARE @id varchar(10)
DECLARE @oxy_ID varchar(2)
declare @prevOxyID varchar(10) = NULL
declare @number int
DECLARE @previd varchar(10) = NULL
DECLARE cur CURSOR FOR
(SELECT d.id, d.number, d.oxy_id FROM @demo d)
OPEN cur
FETCH NEXT FROM cur into
@id, @number, @oxy_id
WHILE @@FETCH_STATUS = 0
BEGIN
IF @oxy_id IS NULL
BEGIN
if @prevOxyID IS NOT NULL
BEGIN
IF @id = @previd
BEGIN
UPDATE @demo SET oxy_id = @prevOxyID
WHERE id = @id AND number = @number AND oxy_id IS NULL
END
ELSE
BEGIN
SET @prevOxyID = NULL
END
END
SET @previd = @id
END
ELSE
BEGIN
SET @previd = @id
SET @prevOxyID = @oxy_ID
END
FETCH NEXT FROM cur into
@id, @number, @oxy_id
END
close cur
deallocate cur
SELECT * FROM @demo
请注意,您不能在光标中使用 order by。数据必须已经按照您的图像上显示的顺序排列。如果表中的数据又不是这个顺序,就需要使用一个临时表,将记录按正确的顺序插入,然后在临时表上进行游标,最后从临时表中更新原表一个。
编辑
OK 所以没有光标版本。正如gordy 所说,LAG 的问题在于重复的数字。同样的问题限制了 UPDATE 的使用,因为行没有唯一标识符。相反,我必须将结果插入临时表,删除原件,然后从 temp 重新插入。如果你确实有一个唯一的键,那么请用更新替换这个删除和插入。以下解决方案虽然有点啰嗦,但确实解决了这些问题,根据我的研究,它应该适用于 Amazon Redshift,但我无法进行测试。插入内容我就不重复了,请从上面复制。
declare @demo table
(
id varchar (10),
number int,
oxy_id varchar(2)
)
create table allrownums
(
id varchar (10),
number int,
oxy_id varchar(2),
rownum int
)
INSERT INTO allrownums
SELECT id, number, oxy_id, ROW_NUMBER() OVER (ORDER BY id, number) AS rownum
FROM @demo;
create table allnotnullrows
(
id varchar (10),
number int,
oxy_id varchar(2),
rownum int
)
INSERT INTO allnotnullrows
SELECT * FROM allrownums
WHERE oxy_id IS NOT NULL
create table maxrownums
(
id varchar (10),
rownum int,
maxrownum int
)
INSERT INTO maxrownums
SELECT a.id, a.rownum, Max(n.rownum)
FROM allrownums a INNER JOIN allnotnullrows n
ON n.id = a.id WHERE a.rownum >= n.rownum
GROUP BY a.id, a.rownum
create table tempresults
(
id varchar (10),
number int,
oxy_id varchar(2)
)
INSERT INTO tempresults
SELECT a.id, a.number, coalesce(a.oxy_id, n.oxy_id) as oxy_id
FROM allrownums a
LEFT JOIN maxrownums m
ON m.rownum = a.rownum
LEFT JOIN allnotnullrows n
ON a.id = n.id
and n.rownum = m.maxrownum
DELETE FROM @demo;
INSERT INTO @demo SELECT * FROM tempresults;
DROP TABLE tempresults;
DROP TABLE allrownums;
DROP TABLE allnotnullrows;
DROP TABLE maxrownums;
SELECT * FROM @demo;
【讨论】:
Willcock 非常感谢,您给了我同样的结果,但 ID = '312_9874' Oxy_id_new 数据与我在图片中显示的数据不同。 这正是我想要的,非常感谢。非常感谢您的帮助。 你能用join或CTE函数写同样的查询吗?我问的原因是因为当我在 SQL 服务器中测试你的查询时它工作正常但是当我在 Amazon Redshift 中复制它时它不支持变量和游标。 抱歉耽搁了——我昨天大部分时间都在外面。请参阅我的答案编辑 对于延迟回复我深表歉意,我昨天才收到您的查询,并检查了 Amazon Redshift 中的示例数据,它为我提供了我正在寻找的完美结果,没有任何问题,任何今天我如何开始用实际数据复制查询。让你同样的更新。再次感谢您的友好帮助。它就在我们最需要的时候出现了!谢谢。【参考方案3】:应该在 SQL 2008 中工作(我无法测试,但 CROSS APPLY 在 SQL 2008 中工作)
CREATE TABLE #table1( ID INT, Number INT, OXY_ID VARCHAR( 2 ))
INSERT INTO #table1
VALUES
( 1, 23, 'AD' ),
( 2, 23, 'XY' ),
( 3, 23, '' ),
( 4, 23, '' ),
( 5, 23, 'MY' ),
( 6, 23, '' ),
( 7, 23, 'ZY' )
CREATE INDEX IX_table1__ID ON #table1( ID, OXY_ID )
SELECT a.*, c.OXY_ID AS OXY_ID_New
FROM #table1 AS a
CROSS APPLY
( SELECT TOP 1 ID, OXY_ID
FROM #table1 AS b
WHERE OXY_ID <> '' AND a.ID >= b.ID
ORDER BY ID DESC ) AS c
评论:
应该比光标快很多。
LAG
解决方案比这个更优雅。
【讨论】:
我会检查让知道的代码。感谢您清除 LAG 功能。以上是关于SQL:如何根据条件用前一行值填充空单元格?的主要内容,如果未能解决你的问题,请参考以下文章