SQL QUERY заменяет значение NULL в строке значением из предыдущего известного значения - PullRequest
23 голосов
/ 28 августа 2009

у меня 2 столбца

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

Мне нужно заменить значения NULL новыми значениями, которые принимают значение из последнего известного значения в предыдущую дату в столбце даты например: дата = 2 число = 3, дата 4 и 5 число = 5 и 5. Значения NULL отображаются случайным образом.

Ответы [ 12 ]

20 голосов
/ 28 августа 2009

Если вы используете 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
15 голосов
/ 28 августа 2009

Вот решение MySQL:

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

Это сжато, но не обязательно будет работать в других марках СУБД. Для других брендов может существовать конкретное решение, которое более актуально. Вот почему важно сообщить нам марку, которую вы используете.

Приятно быть независимым от поставщика, как прокомментировал @Pax, но в противном случае также неплохо использовать выбранную вами базу данных с максимальной пользой.


Объяснение вышеуказанного запроса:

@n - переменная пользователя MySQL. Он начинается со значения NULL, и ему присваивается значение в каждой строке, поскольку UPDATE проходит через строки. Если number не равен NULL, @n присваивается значение number. Где number равно NULL, COALESCE() по умолчанию равно предыдущему значению @n. В любом случае это становится новым значением столбца number, и ОБНОВЛЕНИЕ переходит к следующей строке. Переменная @n сохраняет свое значение от строки к строке, поэтому последующие строки получают значения, полученные из предыдущей строки. Порядок UPDATE предсказуем, поскольку MySQL использует ORDER BY с UPDATE (это не стандартный SQL).

9 голосов
/ 07 октября 2010

Лучшее решение - это предложение Билла Карвина. Недавно мне пришлось решить эту проблему в относительно большом наборе результатов (1000 строк с 12 столбцами, каждый из которых нуждается в этом типе «покажите мне последнее ненулевое значение, если это значение равно нулю в текущей строке») и используя метод обновления с верхним 1 выберите предыдущее известное значение (или подзапрос с топ-1), выполняемое очень медленно.

Я использую SQL 2005, и синтаксис для замены переменной немного отличается от mysql:

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

Первый оператор set обновляет значение переменной @n до значения текущей строки 'number', если 'number' не равно нулю (COALESCE возвращает первый ненулевой аргумент, который вы передаете ему) Второй оператор set обновляет фактическое значение столбца для 'number' для себя (если оно не равно NULL) или переменной @n (которая всегда содержит последнее значение, отличное от NULL).

Прелесть этого подхода в том, что нет дополнительных затрат ресурсов на сканирование временной таблицы снова и снова ... Обновление @n в строке обеспечивает отслеживание последнего ненулевого значения.

У меня недостаточно представителей, чтобы голосовать за него, но кто-то должен. Это самый элегантный и лучший исполнитель.

8 голосов
/ 28 августа 2009

Вот решение 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 голосов
/ 18 мая 2012

Следующий скрипт решает эту проблему и использует только простой 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;
4 голосов
/ 29 марта 2012

Я знаю, что это очень старый форум, но я сталкивался с этим при устранении неполадок в моей проблеме :) только что понял, что другие парни дали немного сложное решение вышеупомянутой проблемы. Пожалуйста, смотрите мое решение ниже:

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 голосов
/ 28 сентября 2016

Если вы ищете решение для Redshift, это будет работать с предложением frame:

SELECT date, 
       last_value(columnName ignore nulls) 
                   over (order by date
                         rows between unbounded preceding and current row) as columnName 
 from tbl
2 голосов
/ 28 августа 2009

В очень общем смысле:

UPDATE MyTable
SET MyNullValue = MyDate
WHERE MyNullValue IS NULL
1 голос
/ 09 мая 2012

Это решение для MS Access.

Пример таблицы называется tab, с полями id и val.

SELECT (SELECT last(val)
          FROM tab AS temp
          WHERE tab.id >= temp.id AND temp.val IS NOT NULL) AS val2, *
  FROM tab;
1 голос
/ 28 августа 2009

Прежде всего, вам действительно нужно хранить значения? Вы можете просто использовать вид, который делает работу:

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».

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...