更新值而不是 NULL
Updating values instead of NULLs
我正在使用 Oracle SQL。
我有以下 table:
Timestamp | A | B
13-11-14 06.49.54.004 | 50 | 70
13-11-14 06.49.54.005 | NULL| 80
13-11-14 06.49.54.006 | NULL| NULL
13-11-14 06.49.54.007 | 40 | 70
13-11-14 06.49.54.008 | 20 | 90
13-11-14 06.49.54.009 | 30 | NULL
如何用每列的最后一个值替换 NULL
值?这是预期的输出 table:
Timestamp | A | B
13-11-14 06.49.54.004 | 50 | 70
13-11-14 06.49.54.005 | 50 | 80
13-11-14 06.49.54.006 | 50 | 80
13-11-14 06.49.54.007 | 40 | 70
13-11-14 06.49.54.008 | 20 | 90
13-11-14 06.49.54.009 | 30 | 90
请指教
使用合并仅更新 NULL,使用子选择查找最新值。
update tablename t1 set a = coalesce(a,(select max(a) from tablename
where Timestamp < t1.Timestamp)),
b = coalesce(b,(select max(b) from tablename
where Timestamp < t1.Timestamp))
where a is null or b is null
现已编辑! (Max 可能不是最近的...)
update tablename t1 set a = coalesce(a,(select a from tablename
where Timestamp < t1.Timestamp
order by Timestamp desc
fetch first 1 row only)),
b = coalesce(b,(select b from tablename
where Timestamp < t1.Timestamp
order by Timestamp desc
fetch first 1 row only))
where a is null or b is null
FETCH FIRST 需要较新的 Oracle 版本。
我建议两种方法。首先,您可以很容易地使用 PL/SQL 块来完成此操作,例如:
DECLARE
v_prev_timestamp TIMESTAMP;
v_prev_A INT;
v_prev_B INT;
BEGIN
FOR x IN (SELECT * FROM table_name) LOOP
IF (x.A IS NULL) THEN
UPDATE table_name SET A = v_prev_A where timestamp = v_prev_timestamp;
END IF;
... same for B ...
END LOOP;
END;
或者,您可以使用 Oracle 的 LAG 函数创建一个视图,用前几行的值覆盖空值。
CREATE VIEW table_name_no_nulls AS
SELECT timestamp,
nvl(A, lag(A) OVER (ORDER BY timestamp)) AS A,
nvl(B, lag(B) OVER (ORDER BY timestamp)) AS B
FROM table_name;
在第二个版本中,NVL 在第一个参数中查找空值,如果为空值,则用第二个参数的输入结果(即同一列的滞后值)替换该值。您必须告诉 Oracle table 是如何排序的,以便它知道要使用哪个先前的值。
我建议使用第二个版本,因为您不需要 运行 任何一次性批处理来更新数据。只需查询一个转换空值的新视图。
有 LEAD() 和 LAG() 分析函数。在您的情况下,您需要 LAG()。它非常易于使用,有很多示例 - 查看文档并搜索 Whosebug...
您可以将 the first_value()
analytic function 与 windowing 子句一起使用,使用时间戳列进行排序并忽略空值:
select timestamp, a, b,
first_value(a) ignore nulls over (order by timestamp desc
rows between current row and unbounded following) as new_a,
first_value(b) ignore nulls over (order by timestamp desc
rows between current row and unbounded following) as new_b
from table_name
order by timestamp;
TIMESTAMP A B NEW_A NEW_B
---------------------------- ---------- ---------- ---------- ----------
13-NOV-14 06.49.54.004000000 50 70 50 70
13-NOV-14 06.49.54.005000000 80 50 80
13-NOV-14 06.49.54.006000000 50 80
13-NOV-14 06.49.54.007000000 40 70 40 70
13-NOV-14 06.49.54.008000000 20 90 20 90
13-NOV-14 06.49.54.009000000 30 30 90
或者用 last_value() 代替:
select timestamp, a, b,
last_value(a) ignore nulls over (order by timestamp
rows between unbounded preceding and current row) as new_a,
last_value(b) ignore nulls over (order by timestamp
rows between unbounded preceding and current row) as new_b
from table_name
order by timestamp;
window 包含当前行,因此如果该值不为空,则将使用它;否则它将使用 first/last 非空值(因为 ignore null
子句),因为它以指定的顺序遍历 window。
我正在使用 Oracle SQL。
我有以下 table:
Timestamp | A | B
13-11-14 06.49.54.004 | 50 | 70
13-11-14 06.49.54.005 | NULL| 80
13-11-14 06.49.54.006 | NULL| NULL
13-11-14 06.49.54.007 | 40 | 70
13-11-14 06.49.54.008 | 20 | 90
13-11-14 06.49.54.009 | 30 | NULL
如何用每列的最后一个值替换 NULL
值?这是预期的输出 table:
Timestamp | A | B
13-11-14 06.49.54.004 | 50 | 70
13-11-14 06.49.54.005 | 50 | 80
13-11-14 06.49.54.006 | 50 | 80
13-11-14 06.49.54.007 | 40 | 70
13-11-14 06.49.54.008 | 20 | 90
13-11-14 06.49.54.009 | 30 | 90
请指教
使用合并仅更新 NULL,使用子选择查找最新值。
update tablename t1 set a = coalesce(a,(select max(a) from tablename
where Timestamp < t1.Timestamp)),
b = coalesce(b,(select max(b) from tablename
where Timestamp < t1.Timestamp))
where a is null or b is null
现已编辑! (Max 可能不是最近的...)
update tablename t1 set a = coalesce(a,(select a from tablename
where Timestamp < t1.Timestamp
order by Timestamp desc
fetch first 1 row only)),
b = coalesce(b,(select b from tablename
where Timestamp < t1.Timestamp
order by Timestamp desc
fetch first 1 row only))
where a is null or b is null
FETCH FIRST 需要较新的 Oracle 版本。
我建议两种方法。首先,您可以很容易地使用 PL/SQL 块来完成此操作,例如:
DECLARE
v_prev_timestamp TIMESTAMP;
v_prev_A INT;
v_prev_B INT;
BEGIN
FOR x IN (SELECT * FROM table_name) LOOP
IF (x.A IS NULL) THEN
UPDATE table_name SET A = v_prev_A where timestamp = v_prev_timestamp;
END IF;
... same for B ...
END LOOP;
END;
或者,您可以使用 Oracle 的 LAG 函数创建一个视图,用前几行的值覆盖空值。
CREATE VIEW table_name_no_nulls AS
SELECT timestamp,
nvl(A, lag(A) OVER (ORDER BY timestamp)) AS A,
nvl(B, lag(B) OVER (ORDER BY timestamp)) AS B
FROM table_name;
在第二个版本中,NVL 在第一个参数中查找空值,如果为空值,则用第二个参数的输入结果(即同一列的滞后值)替换该值。您必须告诉 Oracle table 是如何排序的,以便它知道要使用哪个先前的值。
我建议使用第二个版本,因为您不需要 运行 任何一次性批处理来更新数据。只需查询一个转换空值的新视图。
有 LEAD() 和 LAG() 分析函数。在您的情况下,您需要 LAG()。它非常易于使用,有很多示例 - 查看文档并搜索 Whosebug...
您可以将 the first_value()
analytic function 与 windowing 子句一起使用,使用时间戳列进行排序并忽略空值:
select timestamp, a, b,
first_value(a) ignore nulls over (order by timestamp desc
rows between current row and unbounded following) as new_a,
first_value(b) ignore nulls over (order by timestamp desc
rows between current row and unbounded following) as new_b
from table_name
order by timestamp;
TIMESTAMP A B NEW_A NEW_B
---------------------------- ---------- ---------- ---------- ----------
13-NOV-14 06.49.54.004000000 50 70 50 70
13-NOV-14 06.49.54.005000000 80 50 80
13-NOV-14 06.49.54.006000000 50 80
13-NOV-14 06.49.54.007000000 40 70 40 70
13-NOV-14 06.49.54.008000000 20 90 20 90
13-NOV-14 06.49.54.009000000 30 30 90
或者用 last_value() 代替:
select timestamp, a, b,
last_value(a) ignore nulls over (order by timestamp
rows between unbounded preceding and current row) as new_a,
last_value(b) ignore nulls over (order by timestamp
rows between unbounded preceding and current row) as new_b
from table_name
order by timestamp;
window 包含当前行,因此如果该值不为空,则将使用它;否则它将使用 first/last 非空值(因为 ignore null
子句),因为它以指定的顺序遍历 window。