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

时间:2009-08-28 04:44:47

标签: sql null

我有2列

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

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

14 个答案:

答案 0 :(得分:20)

如果您使用的是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 :(得分:15)

这是一个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)。

答案 2 :(得分:9)

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

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

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

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

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

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

答案 3 :(得分: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>

答案 4 :(得分: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;

答案 5 :(得分: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

希望这可以帮助某人:)

答案 6 :(得分:2)

在一般意义上说:

UPDATE MyTable
SET MyNullValue = MyDate
WHERE MyNullValue IS NULL

答案 7 :(得分: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

答案 8 :(得分: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”。

答案 9 :(得分: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;

答案 10 :(得分:1)

这将在Snowflake上起作用(归功于Darren Gardner):

create temp table ss (id int, val int);
insert into ss (id,val) select 1, 3;
insert into ss (id,val) select 2, null;
insert into ss (id,val) select 3, 5;
insert into ss (id,val) select 4, null;
insert into ss (id,val) select 5, null;
insert into ss (id,val) select 6, 2;

select *
      ,last_value(val ignore nulls) over 
       (order by id rows between unbounded preceding and current row) as val2
  from ss;

答案 11 :(得分: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

答案 12 :(得分: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

答案 13 :(得分:-3)

试试这个:

update Projects
set KickOffStatus=2 
where KickOffStatus is null