【问题标题】:SQL QUERY replace NULL value in a row with a value from the previous known valueSQL QUERY 用前一个已知值中的值替换行中的 NULL 值
【发布时间】:2009-08-28 04:44:47
【问题描述】:

我有 2 列

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

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

【问题讨论】:

  • 您能否确定您正在使用的 SQL 数据库的品牌,例如MySQL、Oracle、SQL-Server,并编辑您的问题以添加该标签?
  • 你是指上一行的日期,还是上一个日期(第-1天)?或许可以举例说明您在列中拥有的数据以及您想要的输出示例。
  • @Bill,他可能想要一个通用的解决方案。我们中的一些人实际上喜欢当他们成为 PITA 时在 DBMS 之间轻松切换的能力:-) 不过,在标准 SQL 中做到这一点非常困难(可能是不可能的),@mike,所以如果你有一个特定的 DBMS,一定要告诉我们。
  • @Pax:我同意便携式解决方案的目标,但使用最清晰、最有效的供应商特定解决方案也有好处。
  • @paxdiablo 计算机科学中的所有问题都可以通过另一个级别的间接来解决。在这种情况下,我认为三重自连接应该可以工作......如果你真的想要,我可以尝试编写代码。

标签: sql null


【解决方案1】:

如果您使用的是 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

【讨论】:

  • 嗨.. @Adriaan - 当我在第 x 周部门级别有一些值并且我想用部门的最后一个已知值替换 NULL 时,它将如何工作?所以在这个例子中,添加一个第三维,比如 ID、ID1、Val。任何指针将不胜感激!谢谢
【解决方案2】:

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

【讨论】:

  • COALESCE 在 SQL Server(2000?)和 Oracle 9i+ 上受支持,但我不明白 @n 在做什么。
【解决方案3】:

最佳解决方案是 Bill Karwin 提供的解决方案。我最近不得不在一个相对较大的结果集中解决这个问题(1000 行有 12 列,每列都需要这种类型的“如果当前行上的值为空,则显示最后一个非空值”)并使用带有前 1 的更新方法select for the previous known value(或带有前 1 的子查询)运行速度非常慢。

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

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

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

这种方法的美妙之处在于,无需花费额外的资源来一遍又一遍地扫描临时表……@n 的行内更新负责跟踪最后一个非空值。

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

【讨论】:

  • SQLite 不支持变量。但是,如果条目具有自动增量 id 并且已将日期作为单调函数插入,则每个连续行都可以引用前一个。不过,这是一个非常具体的案例。 UPDATE mytable SET number = COALESCE(number, (SELECT t.number FROM mytable t WHERE mytable.id = t.id + 1));
  • SQL 2012 不支持排序依据。我不得不接受 Adriaan Stander 的回答。
【解决方案4】:

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

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>

【讨论】:

  • 你能用 LAG 代替 LAST_VALUE 吗?如果是这样,那将使它与 8i+ 兼容。
  • 没有。 LAG() 仅适用于固定偏移量。给定的测试数据有一个可变的偏移量。
  • 我走了很长一段路才找到这个答案! IGNORE NULLS 做了所有的魔法!
  • 忽略空值将我的查询缩短了 30% :)
【解决方案5】:

以下脚本解决了这个问题,并且只使用了普通的 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;

【讨论】:

    【解决方案6】:

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

    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
    

    希望这可以帮助某人:)

    【讨论】:

      【解决方案7】:

      如果您正在寻找 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】:

        首先,您真的需要存储这些值吗?您可以只使用完成这项工作的视图:

        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】:

          这是 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】:

            这将适用于 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】:
              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
              

              【讨论】:

              • max(t.value) 不起作用 - 您想要最大 id
              • @SquareCog:重新阅读 OP:...用日期列中上一个日期的最后一个已知 [数字列] 值中的新值替换 NULL [数字列] 值,例如: date=2 number = 3, date 4 and 5 number = 5 and 5.
              【解决方案12】:

              如果您有一个身份 (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】:

                一般意义上的:

                UPDATE MyTable
                SET MyNullValue = MyDate
                WHERE MyNullValue IS NULL
                

                【讨论】:

                • 投反对票。不正确,因为第 2 行的值为 3,第 4 行,第 5 行的值为 5。MyNullValue = MyDate 将值 2 赋予第 2 行等。
                【解决方案14】:

                试试这个:

                update Projects
                set KickOffStatus=2 
                where KickOffStatus is null
                

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 2016-07-19
                  • 2016-07-20
                  • 1970-01-01
                  • 1970-01-01
                  • 2020-08-03
                  • 2021-08-16
                  • 1970-01-01
                  相关资源
                  最近更新 更多