【问题标题】:Oracle recursive CTE for date with interval带有间隔的日期的Oracle递归CTE
【发布时间】:2021-10-19 14:22:34
【问题描述】:

我正在尝试创建一个递归 CTE,它每 10 分钟生成一个日期/时间并在午夜停止,但我在语法上苦苦挣扎,似乎无法让它工作。

我会非常感谢任何可以帮助我的人。提前感谢所有回答的人。


ALTER SESSION SET NLS_DATE_FORMAT = 'MMDDYYYY HH24:MI:SS';


with date_rows 
(( SELECT TO_DATE('2021/08/20 18:30:25', 'YYYY/MM/DD HH24:MI:SS') as start_date  FROM DUAL ) 
union all
select start_date+interval '10' minute
 from date_rows
where start_date < 
TRUNC(start_date) + INTERVAL '1' DAY)
select * from date_rows;

【问题讨论】:

  • 什么版本的Oracle?
  • @MTO 我总是在实时 SQL 上进行测试,因此响应的人可以使用与我测试的 Oracle 版本相同
  • Oracle 的早期版本中存在一个错误,这意味着递归子查询因式分解子句无法处理 DATE 数据类型,因此如果您使用的是 Oracle 11g(可能是 12c ) 和更早版本,或者如果您使用的是更高版本。

标签: sql oracle datetime recursion common-table-expression


【解决方案1】:
Something like this, I presume.

SQL> with date_rows (datum) as
  2    (select cast (to_date('2021/08/20 18:30:25', 'YYYY/MM/DD HH24:MI:SS') as date)
  3     from dual
  4     union all
  5     select cast (datum + interval '10' minute as date)
  6     from date_rows
  7     where datum < trunc(to_date('2021/08/20 18:30:25', 'YYYY/MM/DD HH24:MI:SS')) + interval '1' day
  8    )
  9  select * from date_rows;

DATUM
-----------------
08202021 18:30:25
08202021 18:40:25
08202021 18:50:25
08202021 19:00:25
08202021 19:10:25
08202021 19:20:25
08202021 19:30:25
08202021 19:40:25
08202021 19:50:25
08202021 20:00:25
08202021 20:10:25
<snip>    
08202021 23:20:25
08202021 23:30:25
08202021 23:40:25
08202021 23:50:25
08212021 00:00:25

34 rows selected.

SQL>

【讨论】:

  • 感谢您的回复,但我相信 MTO 解决方案更容易,因为日期值仅使用一次。话虽如此,我的最终意图是将其包装在一个过程中,这样用户就不必编辑任何代码
  • 谢谢。我无法告诉您修复 QA 通过编辑自己破坏的代码浪费了多少时间,因此我总是尝试将代码包装在函数或过程中,以便它们可以传入 VALUES,这不会破坏代码。我学到了很好的一课!
【解决方案2】:

你可以使用:

WITH inputs ( value ) AS (
  SELECT TO_DATE('2021/08/20 18:30:25', 'YYYY/MM/DD HH24:MI:SS')
  FROM   DUAL
),
date_rows ( start_date, end_date ) AS (
  SELECT value,
         TRUNC(value) + INTERVAL '1' DAY
  FROM   inputs
UNION ALL
  SELECT start_date + INTERVAL '10' MINUTE,
         end_date
  FROM   date_rows
  WHERE  start_date + INTERVAL '10' MINUTE < end_date
)
SELECT start_date
FROM   date_rows;

或者,如果你想复制输入值,那么你可以使用:

WITH date_rows ( start_date ) AS (
  SELECT TO_DATE('2021/08/20 18:30:25', 'YYYY/MM/DD HH24:MI:SS')
  FROM   DUAL
UNION ALL
  SELECT start_date + INTERVAL '10' MINUTE
  FROM   date_rows
  WHERE  start_date + INTERVAL '10' MINUTE
           < TRUNC(TO_DATE('2021/08/20 18:30:25', 'YYYY/MM/DD HH24:MI:SS'))
             + INTERVAL '1' DAY
)
SELECT *
FROM   date_rows;

注意:在递归子查询的每次迭代中,start_date 是前一个值,因此您需要检查 start_date + INTERVAL '10' MINUTE 是否在午夜之前(而不仅仅是 start_date),否则您将获得午夜后的最后一行。

db小提琴here


你的代码出了什么问题:

  1. 对于非递归子查询因式分解子句,语法为:

    WITH query_alias AS (
    

    WITH query_alias (column_alias) AS (
    

    您缺少 AS 关键字。

    但是,对于递归子查询分解子句,您需要使用第二个版本并指定列别名。

  2. 第一个 SELECT 周围的 () 括号是多余的(但不会导致错误)。

  3. start_date &lt; TRUNC(start_date) + INTERVAL '1' DAY) 总是正确的,即使日期超过了午夜边界;因此查询将无限递归。

db小提琴here

【讨论】:

  • @MTO 效果很好,但如果您有时间,您能否简要解释一下为什么我的尝试没有奏效,以便我可以更好地自我介绍。我是完全走错了路还是我关闭了
  • @Beefstu 更新
猜你喜欢
  • 1970-01-01
  • 2021-09-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-06-21
  • 2018-07-07
  • 1970-01-01
相关资源
最近更新 更多