我有2列
date number
---- ------
1 3
2 NULL
3 5
4 NULL
5 NULL
6 2
.......
Run Code Online (Sandbox Code Playgroud)
我需要用新值取代NULL值,取值来自日期列中上一个日期的最后一个已知值的值,例如:date = 2 number = 3,date 4和5 number = 5和5.出现NULL值随机.
Adr*_*der 21
如果您使用的是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
Run Code Online (Sandbox Code Playgroud)
Bil*_*win 16
这是一个MySQL解决方案:
UPDATE mytable
SET number = (@n := COALESCE(number, @n))
ORDER BY date;
Run Code Online (Sandbox Code Playgroud)
这是简洁的,但在其他品牌的RDBMS中不一定有效.对于其他品牌,可能会有一个更具相关性的品牌特定解决方案.这就是为什么告诉我们你正在使用的品牌很重要的原因.
正如@Pax评论的那样,独立于供应商是件好事,但如果不这样做,那么使用您所选择的数据库品牌也是一件好事.
以上查询的说明:
@n是一个MySQL用户变量.它从NULL开始,并在UPDATE遍历行时为每一行分配一个值.哪里number是非NULL,@n则赋值为number.其中number是NULL,COALESCE()默认为前一个值@n.在任何一种情况下,这都成为number列的新值,UPDATE进入下一行.的@n变量从行保留其值排,所以后续行获取来自现有行(一个或多个)值.UPDATE的顺序是可预测的,因为MySQL特别使用ORDER BY和UPDATE(这不是标准SQL).
小智 9
最好的解决方案是Bill Karwin提供的解决方案.我最近不得不在一个相对较大的结果集中解决这个问题(1000行,每列12列,如果此值在当前行为null,则显示最后一个非空值)并使用更高方法选择上一个已知值(或带有前1个的子查询)运行速度超慢.
我使用的是SQL 2005,变量替换的语法与mysql略有不同:
UPDATE mytable
SET
@n = COALESCE(number, @n),
number = COALESCE(number, @n)
ORDER BY date
Run Code Online (Sandbox Code Playgroud)
如果'number'不为null,则第一个set语句将变量@n的值更新为当前行的'number'值(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>
Run Code Online (Sandbox Code Playgroud)
以下脚本解决了此问题,仅使用普通的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;
Run Code Online (Sandbox Code Playgroud)
小智 5
我知道这是一个非常古老的论坛,但我在解决问题时遇到了这个:)刚刚意识到其他人已经为上述问题提供了一些复杂的解决方案。请看下面我的解决方案:
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
Run Code Online (Sandbox Code Playgroud)
希望这可以帮助某人:)
小智 5
如果您正在寻找 Redshift 的解决方案,这将适用于 frame 子句:
SELECT date,
last_value(columnName ignore nulls)
over (order by date
rows between unbounded preceding and current row) as columnName
from tbl
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
92175 次 |
| 最近记录: |