首页 > 解决方案 > SQL Oracle 中的滚动 90 天日期窗口

问题描述

SELECT *
  FROM (SELECT date'2021-05-06' AS tran_dt FROM dual UNION
        SELECT date'2021-05-24' FROM dual UNION
        SELECT date'2021-06-25' FROM dual UNION
        SELECT date'2021-07-02' FROM dual UNION
        SELECT date'2021-07-27' FROM dual UNION
        SELECT date'2021-08-16' FROM dual UNION
        SELECT date'2021-08-23' FROM dual UNION
        SELECT date'2021-10-01' FROM dual UNION
        SELECT date'2021-12-31' FROM dual)

你好

我正在使用 SQL Oracle 中的日期字段。

我正在尝试创建滚动窗口,该窗口返回第一行并在自上次返回行以来的日期> 90 时返回另一行。

在下面的示例中,我想计算并返回警报字段 = 是的位置。

如果有人有任何想法,将不胜感激。

谢谢

席7

TRAN_DT    Alert    Days Since Last Alert
06/05/2021  Yes     0
24/05/2021  No      18
25/06/2021  No      50
02/07/2021  No      57
27/07/2021  No      82
16/08/2021  Yes     102
23/08/2021  No      7
01/10/2021  No      46
31/12/2021  Yes     137

标签: sqloracleoracle11g

解决方案


From Oracle 12, this is the sort of query that MATCH_RECOGNIZE is intended for:

SELECT tran_dt,
       alert,
       tran_dt
         - LAG(CASE alert WHEN 'Yes' THEN tran_dt END, 1, tran_dt)
             IGNORE NULLS OVER (ORDER BY tran_dt)
         AS days
FROM   table_name
MATCH_RECOGNIZE (
  ORDER BY tran_dt
  MEASURES
    CLASSIFIER() AS alert
  ALL ROWS PER MATCH
  PATTERN ( "Yes" "No"* )
  DEFINE
    "No" AS tran_dt <= "Yes".tran_dt + INTERVAL '90' DAY
)

Before Oracle 12, you can use a recursive query:

WITH dates (tran_dt, rn) AS (
  SELECT tran_dt,
         ROW_NUMBER() OVER (ORDER BY tran_dt) AS rn
  FROM   table_name
),
rolling_dates (tran_dt, alert, rn, days, last_alert) AS (
  SELECT tran_dt, 'Yes', rn, 0, tran_dt
  FROM   dates
  WHERE  rn = 1
UNION ALL
  SELECT d.tran_dt,
         CASE
         WHEN d.tran_dt <= r.last_alert + INTERVAL '90' DAY
         THEN 'No'
         ELSE 'Yes'
         END,
         d.rn,
         d.tran_dt - r.last_alert,
         CASE
         WHEN d.tran_dt <= r.last_alert + INTERVAL '90' DAY
         THEN r.last_alert
         ELSE d.tran_dt
         END
  FROM   rolling_dates r
         INNER JOIN dates d
         ON (r.rn + 1 = d.rn)
)
SELECT tran_dt,
       alert,
       days
FROM   rolling_dates;

Both of which outputs:

TRAN_DT ALERT DAYS
2021-05-06 00:00:00 Yes 0
2021-05-24 00:00:00 No 18
2021-06-25 00:00:00 No 50
2021-07-02 00:00:00 No 57
2021-07-27 00:00:00 No 82
2021-08-16 00:00:00 Yes 102
2021-08-23 00:00:00 No 7
2021-10-01 00:00:00 No 46
2021-12-31 00:00:00 Yes 137

db<>fiddle here


推荐阅读