计算每行之间最近的天数
Calculate closest days count, between each row
给定 table 这样的(dt
列是唯一的):
id | dt | days_count
------------------------------
1 | 2015-06-01 | NULL
2 | 2015-06-03 | NULL
4 | 2015-06-09 | NULL
我需要计算最近日期之间的间隔天数并更新 days_count
列中的检索结果,并且计算必须从最晚日期开始。也就是说,需要的结果是:
id | dt | days_count
------------------------------
1 | 2015-06-01 | NULL
2 | 2015-06-03 | 2
4 | 2015-06-09 | 6
我有 2 个变体:
- 变体 1
使用 PL/SQL FOR (SELECT ..) LOOP
,获取每一行,按 dt desc
排序并计算当前行日期和上一行日期之间的间隔天数。
变体 2
MERGE INTO mytable
USING(
WITH
t1 as (SELECT id, dt, row_number() over(order by dt) as rn from mytable),
t2 as (SELECT id, dt, row_number() over(order by dt) + 1 as rn from mytable)
SELECT t1.id, t1.rn, t1.dt - t2.dt as days_count from t1
left JOIN t2
on
t1.rn = t2.rn
) day_interval
ON (mytable.id = day_interval.id)
WHEN MATCHED THEN UPDATE SET
mytable.days_count= day_interval.days_count
这些变体有效,但问题是:也许有更有效的方法?
永远不要在 PL/SQL 中这样做,而在纯 SQL.
您可以简单地使用 LAG() OVER() 分析函数来完成。
例如,
设置
SQL> CREATE TABLE t
2 (id int, dt date, days_count varchar2(4));
Table created.
SQL>
SQL> INSERT ALL
2 INTO t (id, dt, days_count)
3 VALUES (1, to_date('2015-06-01','YYYY-MM-DD'), NULL)
4 INTO t (id, dt, days_count)
5 VALUES (2, to_date('2015-06-03','YYYY-MM-DD'), NULL)
6 INTO t (ID, dt, days_count)
7 VALUES (4, to_date('2015-06-09','YYYY-MM-DD'), NULL)
8 SELECT * FROM dual;
3 rows created.
SQL>
查询
SQL> SELECT id, dt, dt - lag(dt) over(order by dt) days_count FROM t;
ID DT DAYS_COUNT
---------- --------- ----------
1 01-JUN-15
2 03-JUN-15 2
4 09-JUN-15 6
SQL>
MERGE 语句
SQL> MERGE INTO t
2 USING(
3 SELECT id, dt, dt - lag(dt) over(order by dt) days_count FROM t
4 ) day_interval
5 ON (t.id = day_interval.id)
6 WHEN MATCHED THEN UPDATE SET
7 t.days_count= day_interval.days_count;
3 rows merged.
SQL> SELECT * FROM t;
ID DT DAYS
---------- --------- ----
1 01-JUN-15
2 03-JUN-15 2
4 09-JUN-15 6
SQL>
给定 table 这样的(dt
列是唯一的):
id | dt | days_count
------------------------------
1 | 2015-06-01 | NULL
2 | 2015-06-03 | NULL
4 | 2015-06-09 | NULL
我需要计算最近日期之间的间隔天数并更新 days_count
列中的检索结果,并且计算必须从最晚日期开始。也就是说,需要的结果是:
id | dt | days_count
------------------------------
1 | 2015-06-01 | NULL
2 | 2015-06-03 | 2
4 | 2015-06-09 | 6
我有 2 个变体:
- 变体 1
使用 PL/SQL FOR (SELECT ..) LOOP
,获取每一行,按 dt desc
排序并计算当前行日期和上一行日期之间的间隔天数。
变体 2
MERGE INTO mytable USING( WITH t1 as (SELECT id, dt, row_number() over(order by dt) as rn from mytable), t2 as (SELECT id, dt, row_number() over(order by dt) + 1 as rn from mytable) SELECT t1.id, t1.rn, t1.dt - t2.dt as days_count from t1 left JOIN t2 on t1.rn = t2.rn ) day_interval ON (mytable.id = day_interval.id) WHEN MATCHED THEN UPDATE SET mytable.days_count= day_interval.days_count
这些变体有效,但问题是:也许有更有效的方法?
永远不要在 PL/SQL 中这样做,而在纯 SQL.
您可以简单地使用 LAG() OVER() 分析函数来完成。
例如,
设置
SQL> CREATE TABLE t
2 (id int, dt date, days_count varchar2(4));
Table created.
SQL>
SQL> INSERT ALL
2 INTO t (id, dt, days_count)
3 VALUES (1, to_date('2015-06-01','YYYY-MM-DD'), NULL)
4 INTO t (id, dt, days_count)
5 VALUES (2, to_date('2015-06-03','YYYY-MM-DD'), NULL)
6 INTO t (ID, dt, days_count)
7 VALUES (4, to_date('2015-06-09','YYYY-MM-DD'), NULL)
8 SELECT * FROM dual;
3 rows created.
SQL>
查询
SQL> SELECT id, dt, dt - lag(dt) over(order by dt) days_count FROM t;
ID DT DAYS_COUNT
---------- --------- ----------
1 01-JUN-15
2 03-JUN-15 2
4 09-JUN-15 6
SQL>
MERGE 语句
SQL> MERGE INTO t
2 USING(
3 SELECT id, dt, dt - lag(dt) over(order by dt) days_count FROM t
4 ) day_interval
5 ON (t.id = day_interval.id)
6 WHEN MATCHED THEN UPDATE SET
7 t.days_count= day_interval.days_count;
3 rows merged.
SQL> SELECT * FROM t;
ID DT DAYS
---------- --------- ----
1 01-JUN-15
2 03-JUN-15 2
4 09-JUN-15 6
SQL>