SQL QUERY заменяет значение NULL в строке значением из предыдущего известного значения
У меня 2 колонки
date number
---- ------
1 3
2 NULL
3 5
4 NULL
5 NULL
6 2
.......
Мне нужно заменить значения NULL новыми значениями, которые принимают значение из последнего известного значения в предыдущей дате в столбце даты, например: date=2 число = 3, дата 4 и 5 число = 5 и 5. Появляются значения NULL случайным образом.
12 ответов
Если вы используете 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
Вот решение MySQL:
UPDATE mytable
SET number = (@n := COALESCE(number, @n))
ORDER BY date;
Это сжато, но не обязательно будет работать в других марках СУБД. Для других брендов может существовать конкретное решение, которое является более актуальным. Вот почему важно сообщить нам марку, которую вы используете.
Как прокомментировал @Pax, приятно быть независимым от производителя, но в противном случае также полезно использовать выбранную марку базы данных в полной мере.
Объяснение вышеуказанного запроса:
@n
пользовательская переменная MySQL Он начинается с NULL, и ему присваивается значение в каждой строке, поскольку UPDATE проходит через строки. куда number
ненулевой, @n
присваивается значение number
, куда number
NULL, COALESCE()
по умолчанию предыдущее значение @n
, В любом случае это становится новым значением number
столбец и ОБНОВЛЕНИЕ переходит к следующей строке. @n
переменная сохраняет свое значение от строки к строке, поэтому последующие строки получают значения, полученные из предыдущей строки. Порядок UPDATE предсказуем, потому что MySQL использует ORDER BY с UPDATE (это не стандартный SQL).
Лучшее решение - это предложение Билла Карвина. Недавно мне пришлось решить эту проблему в относительно большом наборе результатов (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 обновляет фактический значение столбца для "число" для себя (если не ноль) или переменной @n (которая всегда содержит последнее значение, отличное от NULL).
Прелесть этого подхода в том, что нет дополнительных затрат ресурсов на сканирование временной таблицы снова и снова... Обновление @n в строке обеспечивает отслеживание последнего ненулевого значения.
У меня недостаточно представителей, чтобы проголосовать за него, но кто-то должен. Это самый элегантный и лучший исполнитель.
Вот решение 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>
Следующий скрипт решает эту проблему и использует только обычный 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;
Если вы ищете решение для Redshift, это будет работать с предложением frame:
SELECT date,
last_value(columnName ignore nulls)
over (order by date
rows between unbounded preceding and current row) as columnName
from tbl
Я знаю, что это очень старый форум, но я столкнулся с этим во время устранения неполадок моей проблемы:) только что понял, что другие парни дали немного сложное решение вышеупомянутой проблемы. Пожалуйста, посмотрите мое решение ниже:
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
Надеюсь, что это может помочь кому-то:)
Прежде всего, вам действительно нужно хранить значения? Вы можете просто использовать вид, который делает работу:
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".
В очень общем смысле:
UPDATE MyTable
SET MyNullValue = MyDate
WHERE MyNullValue IS NULL
Это решение для 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;
Это будет работать на Snowflake (кредит Даррена Гарднера):
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;
Если у вас одна личность (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
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
Попробуй это:
update Projects
set KickOffStatus=2
where KickOffStatus is null