SQL QUERY使用先前已知值中的值替换行中的NULL值

24 sql null

我有2列

date   number       
----   ------
1      3           
2      NULL        
3      5           
4      NULL        
5      NULL        
6      2          
.......
Run Code Online (Sandbox Code Playgroud)

我需要用新值取代NULL值,取值来自日期列中上一个日期的最后一个已知值的值,例如:date = 2 number = 3,date 4和5 number = 5和5.出现NULL值随机.

Adr*_*der 21

如果您使用的是Sql Server,这应该可行

DECLARE @Table TABLE(
        ID INT,
        Val INT
)

INSERT INTO @Table (ID,Val) SELECT 1, 3
INSERT INTO @Table (ID,Val) SELECT 2, NULL
INSERT INTO @Table (ID,Val) SELECT 3, 5
INSERT INTO @Table (ID,Val) SELECT 4, NULL
INSERT INTO @Table (ID,Val) SELECT 5, NULL
INSERT INTO @Table (ID,Val) SELECT 6, 2


SELECT  *,
        ISNULL(Val, (SELECT TOP 1 Val FROM @Table WHERE ID < t.ID AND Val IS NOT NULL ORDER BY ID DESC))
FROM    @Table t
Run Code Online (Sandbox Code Playgroud)


Bil*_*win 16

这是一个MySQL解决方案:

UPDATE mytable
SET number = (@n := COALESCE(number, @n))
ORDER BY date;
Run Code Online (Sandbox Code Playgroud)

这是简洁的,但在其他品牌的RDBMS中不一定有效.对于其他品牌,可能会有一个更具相关性的品牌特定解决方案.这就是为什么告诉我们你正在使用的品牌很重要的原因.

正如@Pax评论的那样,独立于供应商是件好事,但如果不这样做,那么使用您所选择的数据库品牌也是一件好事.


以上查询的说明:

@n是一个MySQL用户变量.它从NULL开始,并在UPDATE遍历行时为每一行分配一个值.哪里number是非NULL,@n则赋值为number.其中number是NULL,COALESCE()默认为前一个值@n.在任何一种情况下,这都成为number列的新值,UPDATE进入下一行.的@n变量从行保留其值排,所以后续行获取来自现有行(一个或多个)值.UPDATE的顺序是可预测的,因为MySQL特别使用ORDER BY和UPDATE(这不是标准SQL).


小智 9

最好的解决方案是Bill Karwin提供的解决方案.我最近不得不在一个相对较大的结果集中解决这个问题(1000行,每列12列,如果此值在当前行为null,则显示最后一个非空值)并使用更高方法选择上一个已知值(或带有前1个的子查询)运行速度超慢.

我使用的是SQL 2005,变量替换的语法与mysql略有不同:

UPDATE mytable 
SET 
    @n = COALESCE(number, @n),
    number = COALESCE(number, @n)
ORDER BY date
Run Code Online (Sandbox Code Playgroud)

如果'number'不为null,则第一个set语句将变量@n的值更新为当前行的'number'值(COALESCE返回你传入的第一个非null参数)第二个set语句更新实际'number'到它自身的列值(如果不是null)或变量@n(它总是包含遇到的最后一个非NULL值).

这种方法的优点在于,一遍又一遍地扫描临时表没有额外的资源... @n的行内更新负责跟踪最后一个非空值.

我没有足够的代表投票给他答案,但有人应该.它是最优雅,最高性能的.


APC*_*APC 8

这是Oracle解决方案(10g或更高).它使用last_value()带有ignore nulls选项的分析函数,该函数替换列的最后一个非空值.

SQL> select *
  2  from mytable
  3  order by id
  4  /

        ID    SOMECOL
---------- ----------
         1          3
         2
         3          5
         4
         5
         6          2

6 rows selected.

SQL> select id
  2         , last_value(somecol ignore nulls) over (order by id) somecol
  3  from mytable
  4  /

        ID    SOMECOL
---------- ----------
         1          3
         2          3
         3          5
         4          5
         5          5
         6          2

6 rows selected.

SQL>
Run Code Online (Sandbox Code Playgroud)

  • 我走了很长的路才找到这个答案!IGNORE NULLS 发挥了所有魔力! (3认同)

Ger*_*ima 6

以下脚本解决了此问题,仅使用普通的ANSI SQL.我在SQL2008,SQLite3和Oracle11g上测试了这个解决方案.

CREATE TABLE test(mysequence INT, mynumber INT);

INSERT INTO test VALUES(1, 3);
INSERT INTO test VALUES(2, NULL);
INSERT INTO test VALUES(3, 5);
INSERT INTO test VALUES(4, NULL);
INSERT INTO test VALUES(5, NULL);
INSERT INTO test VALUES(6, 2);

SELECT t1.mysequence, t1.mynumber AS ORIGINAL
, (
    SELECT t2.mynumber
    FROM test t2
    WHERE t2.mysequence = (
        SELECT MAX(t3.mysequence)
        FROM test t3
        WHERE t3.mysequence <= t1.mysequence
        AND mynumber IS NOT NULL
       )
) AS CALCULATED
FROM test t1;
Run Code Online (Sandbox Code Playgroud)


小智 5

我知道这是一个非常古老的论坛,但我在解决问题时遇到了这个:)刚刚意识到其他人已经为上述问题提供了一些复杂的解决方案。请看下面我的解决方案:

DECLARE @A TABLE(ID INT, Val INT)

INSERT INTO @A(ID,Val) SELECT 1, 3
INSERT INTO @A(ID,Val) SELECT 2, NULL
INSERT INTO @A(ID,Val) SELECT 3, 5
INSERT INTO @A(ID,Val) SELECT 4, NULL
INSERT INTO @A(ID,Val) SELECT 5, NULL
INSERT INTO @A(ID,Val) SELECT 6, 2

UPDATE D
    SET D.VAL = E.VAL
    FROM (SELECT A.ID C_ID, MAX(B.ID) P_ID
          FROM  @A AS A
           JOIN @A AS B ON A.ID > B.ID
          WHERE A.Val IS NULL
            AND B.Val IS NOT NULL
          GROUP BY A.ID) AS C
    JOIN @A AS D ON C.C_ID = D.ID
    JOIN @A AS E ON C.P_ID = E.ID

SELECT * FROM @A
Run Code Online (Sandbox Code Playgroud)

希望这可以帮助某人:)


小智 5

如果您正在寻找 Redshift 的解决方案,这将适用于 frame 子句:

SELECT date, 
       last_value(columnName ignore nulls) 
                   over (order by date
                         rows between unbounded preceding and current row) as columnName 
 from tbl
Run Code Online (Sandbox Code Playgroud)