Как заменить нулевое значение значением из следующей строки

Мне нужна поддержка в моем коде запроса SQL. Я должен заменить нулевое значение в столбце ненулевым значением из следующей строки.

В качестве примера мы можем использовать этот код:

declare   @value table (r# int, value varchar(15))
insert into @value ( r#, value ) values
 (1, NULL   ) ,
 (2, 'January'), 
 (3, 'February' ), 
 (4, NULL    ),
 (5, 'March'  ),
 (6, NULL    ),
(7, Null  ),
(8, 'December' ),
(9, Null ),
(10, Null  ),
(11, Null  ),
(12, 'November' ),
(13, Null )
select * from @value

Когда я использую функцию отведения, я получаю это значение, но оно не работает с NULL. Что мне нужно, это получить:

1 January
2 January
3 February
4 March
5 March
6 December
7 December
8 December
9 November
10 November
11 November
12 November
13 NULL

Бу из моего запроса:

SELECT r#, 
  value
 ,case when value is null  then Lead(value) OVER ( order by  r#  asc) else value end as RESULT 
FROM @value
order by r#

У меня есть: enter image description here

Ответ 1

Вы можете сделать это с помощью оконных функций. К сожалению, SQL Server не поддерживает параметр IGNORE NULL в LEAD(), поэтому это не вариант.

Однако вы можете использовать два уровня оконных функций:

select v.r#, v.value,
       coalesce(v.value, max(value) over (partition by next_r#)) as imputed_value
from (select v.*,
             min(case when value is not null then r# end) over (order by r# desc) as next_r#
      from @value v
     ) v
order by v.r#;

На 13 рядах разница в производительности, вероятно, не заметна. Тем не менее, это должно иметь гораздо лучшую производительность по мере увеличения числа строк.

Ответ 2

Следующий подход может помочь. Вам нужен дополнительный оператор APPLY чтобы найти первую запись со значением, NULL:

T-SQL:

SELECT 
    v1.[r#], 
    CASE 
        WHEN v1.[value] IS NULL THEN v2.[value]
        ELSE v1.[value]
    END AS [value]
 FROM @value v1
 OUTER APPLY (
    SELECT TOP 1 [Value]
    FROM @value 
    WHERE (v1.[r#] < [r#]) AND [value] IS NOT NULL
) v2

Выход:

r#  value
1   January
2   January
3   February
4   March
5   March
6   December
7   December
8   December
9   November
10  November
11  November
12  November
13  NULL

Ответ 3

Вы можете попробовать ниже SQL-кода, используя сопутствующий подзапрос

SELECT v1.r#,CASE WHEN value IS NOT NULL THEN v1.Value     
             ELSE ( SELECT TOP 1 value
                    FROM @value  v2
                    WHERE v2.r# > v1.r# AND v2.[value] IS NOT NULL
                  ) END  Value 
FROM @value v1

ИЛИ (без указания случая)

SELECT v1.r#,ISNULL(v1.VALUE,( SELECT TOP 1 value
                            FROM @value  v2
                            WHERE v2.r# > v1.r# 
                            AND v2.[value] IS NOT NULL
                           ) 
                      ) AS [MonthNames]
FROM @value v1

Результат

r#  Value
----------
1   January
2   January
3   February
4   March
5   March
6   December
7   December
8   December
9   November
10  November
11  November
12  November
13  NULL

Ответ 4

следующий запрос работает в SQL Server:

;WITH CTE_Value
AS (
    SELECT R#, Value
    FROM @value AS T
    WHERE Value IS NOT NULL

    UNION ALL

    SELECT t.r#, c.Value
    FROM @value AS t
    INNER JOIN CTE_Value AS c ON t.r# + 1 = c.r#
    WHERE t.Value IS NULL
    )
SELECT *
FROM CTE_Value

UNION ALL

SELECT v.*
FROM @value AS v
LEFT JOIN CTE_value AS c ON v.r# = c.r#
WHERE c.r# IS NULL
ORDER BY r#