SQL:如何根据条件用前一行值填充空单元格?

时间:2017-07-30 07:34:16

标签: sql-server amazon-redshift

SQL:根据条件填充前一行值的空单元格?

请将此视为高优先级请求。需要帮助

请求高代表用户链接(http://i.imgur.com/P4UOiMz.jpg

我需要制作专栏" OXY_ID_NEW"在下表中使用SQL。这在SQL 2008R2或SQL 2012或Amazon REDSHIFT中是否可行?

SQL TABLE图像(http://i.imgur.com/P4UOiMz.jpg

基本上,我想把空填充#34; OXY_ID"具有该ID的最后已知Oxy_id的单元格,如' OXY_ID_NEW'列。

3 个答案:

答案 0 :(得分:2)

可能像

coalesce(lag(oxy_id) over (partition by id order by number), oxy_id)

..假设id,number cols实际上增加..在截图中看起来它们重复,在这种情况下你需要提供整个表定义。

答案 1 :(得分:0)

只要你拥有它,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。数据必须已按照图像上显示的顺序排列。如果表中的数据不按此顺序再次,则需要使用临时表,其中记录以正确的顺序插入,然后在临时表上执行游标,最后从临时表更新原始表之一。

修改

确定没有游标版本。正如gordy所提到的,LAG的问题是重复的数字。同样的问题限制了UPDATE的使用,因为没有行的唯一标识符。相反,我必须将结果插入临时表,删除原件,然后从temp重新插入。如果你确实有一个唯一的密钥,那么请替换此删除并插入UPDATE。下面的解决方案,虽然有点啰嗦,但确实解决了问题,根据我的研究应该可以使用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;

答案 2 :(得分:0)

应该在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解决方案更加优雅。