SQL查询将行中的NULL值替换为先前已知值中的值

问题描述 投票:24回答:13

我有2列

date   number       
----   ------
1      3           
2      NULL        
3      5           
4      NULL        
5      NULL        
6      2          
.......

我需要用新值替换NULL值,并采用日期列中前一个日期的最后一个已知值中的值例如:date = 2数字= 3,日期4和5数字= 5和5。NULL值随机出现。

sql null
13个回答
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

1
投票

这是MS Access的解决方案。

示例表称为tab,具有字段idval

SELECT (SELECT last(val)
          FROM tab AS temp
          WHERE tab.id >= temp.id AND temp.val IS NOT NULL) AS val2, *
  FROM tab;

0
投票
UPDATE TABLE
   SET number = (SELECT MAX(t.number)
                  FROM TABLE t
                 WHERE t.number IS NOT NULL
                   AND t.date < date)
 WHERE number IS NULL

0
投票

如果您具有一个标识(Id)和一个公共(Type)列:

UPDATE #Table1 
SET [Type] = (SELECT TOP 1 [Type]
              FROM #Table1 t              
              WHERE t.[Type] IS NOT NULL AND 
              b.[Id] > t.[Id]
              ORDER BY t.[Id] DESC)
FROM #Table1 b
WHERE b.[Type] IS NULL

-3
投票

尝试一下:

update Projects
set KickOffStatus=2 
where KickOffStatus is null

16
投票

这是MySQL解决方案:

UPDATE mytable
SET number = (@n := COALESCE(number, @n))
ORDER BY date;

这很简洁,但是在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,请向我显示最后一个非null值”),并使用前1个值的update方法选择先前的已知值(或前1个子查询)运行起来非常慢。

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

UPDATE mytable 
SET 
    @n = COALESCE(number, @n),
    number = COALESCE(number, @n)
ORDER BY date

第一个set语句将'@n'的值更新为当前行的'number'的值,如果'number'不为null(COALESCE返回您传递给它的第一个非null参数)第二条set语句将'number'的实际列值更新为自身(如果不为null)或变量@n(始终包含遇到的最后一个非NULL值)。

此方法的优点在于,一次又一次地扫描临时表没有额外的资源。@ n的行内更新将跟踪最后一个非空值。

我没有足够的代表投票赞成他的答案,但是应该有人投票。它是最优雅,性能最好的。


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>

6
投票

以下脚本解决了此问题,仅使用普通的ANSI SQL。我在SQL2008SQLite3Oracle11g上测试了此解决方案。

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;

4
投票

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

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

希望这可以帮助某人:)


2
投票

如果您正在寻找Redshift的解决方案,它将与frame子句一起使用:

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

1
投票

从一般意义上讲:

UPDATE MyTable
SET MyNullValue = MyDate
WHERE MyNullValue IS NULL

1
投票

首先,您真的需要存储值吗?您可以只使用执行任务的视图:

SELECT  t."date",
        x."number" AS "number"
FROM    @Table t
JOIN    @Table x
    ON  x."date" = (SELECT  TOP 1 z."date"
                    FROM    @Table z
                    WHERE   z."date" <= t."date"
                        AND z."number" IS NOT NULL
                    ORDER BY z."date" DESC)

如果您确实有ID ("date")列并且它是主键(群集),那么此查询应该非常快。但是,请检查查询计划:最好还有一个包含Val列的覆盖索引。

此外,如果您不喜欢可以避免使用的程序,也可以对UPDATE使用类似的查询:

UPDATE  t
SET     t."number" = x."number"
FROM    @Table t
JOIN    @Table x
    ON  x."date" = (SELECT  TOP 1 z."date"
                    FROM    @Table z
                    WHERE   z."date" < t."date" --//@note: < and not <= here, as = not required
                        AND z."number" IS NOT NULL
                    ORDER BY z."date" DESC)
WHERE   t."number" IS NULL

注意:该代码必须在“ SQL Server”上运行。

© www.soinside.com 2019 - 2024. All rights reserved.