我有2列
date number
---- ------
1 3
2 NULL
3 5
4 NULL
5 NULL
6 2
.......
我需要用新值替换NULL值,并采用日期列中前一个日期的最后一个已知值中的值例如:date = 2数字= 3,日期4和5数字= 5和5。NULL值随机出现。
如果您使用的是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
这是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;
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
如果您具有一个标识(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 Projects
set KickOffStatus=2
where KickOffStatus is null
这是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)。
最好的解决方案是Bill Karwin提供的解决方案。我最近不得不在一个相对较大的结果集中解决此问题(1000行包含12列,每个列都需要这种类型的“如果当前行上的该值为null,请向我显示最后一个非null值”),并使用前1个值的update方法选择先前的已知值(或前1个子查询)运行起来非常慢。
我正在使用SQL 2005,并且变量替换的语法与mysql略有不同:
UPDATE mytable
SET
@n = COALESCE(number, @n),
number = COALESCE(number, @n)
ORDER BY date
第一个set语句将'@n'的值更新为当前行的'number'的值,如果'number'不为null(COALESCE返回您传递给它的第一个非null参数)第二条set语句将'number'的实际列值更新为自身(如果不为null)或变量@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;
我知道这是一个非常古老的论坛,但是在解决我的问题时遇到了这个问题:)才意识到,其他人已经为上述问题提供了一些复杂的解决方案。请在下面查看我的解决方案:
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
希望这可以帮助某人:)
如果您正在寻找Redshift的解决方案,它将与frame子句一起使用:
SELECT date,
last_value(columnName ignore nulls)
over (order by date
rows between unbounded preceding and current row) as columnName
from tbl
从一般意义上讲:
UPDATE MyTable
SET MyNullValue = MyDate
WHERE MyNullValue IS NULL
首先,您真的需要存储值吗?您可以只使用执行任务的视图:
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”上运行。