Oracle 中的递归查询
Recursive Query in Oracle
您好,我有以下问题...
我想要一个如下的数据集,只有4列,查询得到项目。项目由前一行分隔,前一行 end_date 与当前行 start_date 不同。所以前三行将在同一个项目中。第二个项目将是接下来的两行,项目 3 和 4 将是倒数第二和最后一行。
DAYS,TASK_ID,START_DATE,END_DATE
NULL,1,10/1/2015,10/2/2015
0,2,10/2/2015,10/3/2015
0,3,10/3/2015,10/4/2015
9,4,10/13/2015,10/14/2015
0,5,10/14/2015,10/15/2015
13,6,10/28/2015,10/29/2015
1,7,10/30/2015,10/31/2015
所以输出看起来像
PROJECT,DAYS,TASK_ID,START_DATE,END_DATE
1,NULL,1,10/1/2015,10/2/2015
1,0,2,10/2/2015,10/3/2015
1,0,3,10/3/2015,10/4/2015
2,9,4,10/13/2015,10/14/2015
2,0,5,10/14/2015,10/15/2015
3,13,6,10/28/2015,10/29/2015
4,1,7,10/30/2015,10/31/2015
我是一名 sql 服务器架构师,我已经执行了 sql 服务器查询,但我在 oracle 查询中遇到错误。这是我到目前为止在 Oracle 中的内容。
WITH projectsNumbered (Project, Task_Id, Start_Date, End_Date, Days) AS
(SELECT 1 As "Project"
, Task_Id
, Start_Date
, End_Date
, Days
FROM daysBetweenTasks
WHERE Task_Id = 1
UNION ALL
SELECT
CASE WHEN COALESCE(pN.Days,0) = 0 THEN pN.Project
ELSE pN.Project + 1
END AS "Project"
, pN.Task_Id
, pN.Start_Date
, pN.End_Date
, pN.Days
FROM projectsNumbered pN
JOIN daysBetweenTasks d on p.task_Id = pN.task_Id + 1
)
--SEARCH DEPTH FIRST BY Task_Id SET order1
CYCLE Task_Id SET cycle TO '1' DEFAULT 0
SELECT *
FROM projectsNumbered
但我只返回两行。我在遇到错误后添加了 Cycle Clause。我认为 oracle dba 可能会处理这个问题?顺便说一句,我正在使用 11g Express。
Oracle 设置:
CREATE TABLE daysBetweenTasks ( DAYS,TASK_ID,START_DATE,END_DATE ) AS
SELECT NULL, 1, DATE '2015-10-01', DATE '2015-10-02' FROM DUAL UNION ALL
SELECT 0, 2, DATE '2015-10-02', DATE '2015-10-03' FROM DUAL UNION ALL
SELECT 0, 3, DATE '2015-10-03', DATE '2015-10-04' FROM DUAL UNION ALL
SELECT 9, 4, DATE '2015-10-13', DATE '2015-10-14' FROM DUAL UNION ALL
SELECT 0, 5, DATE '2015-10-14', DATE '2015-10-15' FROM DUAL UNION ALL
SELECT 13, 6, DATE '2015-10-28', DATE '2015-10-29' FROM DUAL UNION ALL
SELECT 1, 7, DATE '2015-10-30', DATE '2015-10-31' FROM DUAL;
查询:
SELECT DENSE_RANK() OVER ( ORDER BY CONNECT_BY_ROOT( task_id ) ) AS project,
days,
task_id,
start_date,
end_date
FROM (
SELECT d.*,
LAG( end_date ) OVER ( ORDER BY task_id ) AS prev_end_date
FROM daysBetweenTasks d
) d
START WITH prev_end_date IS NULL
OR prev_end_date <> start_date
CONNECT BY PRIOR end_date = start_date;
输出:
PROJECT DAYS TASK_ID START_DATE END_DATE
---------- ---------- ---------- ------------------- -------------------
1 1 2015-10-01 00:00:00 2015-10-02 00:00:00
1 0 2 2015-10-02 00:00:00 2015-10-03 00:00:00
1 0 3 2015-10-03 00:00:00 2015-10-04 00:00:00
2 9 4 2015-10-13 00:00:00 2015-10-14 00:00:00
2 0 5 2015-10-14 00:00:00 2015-10-15 00:00:00
3 13 6 2015-10-28 00:00:00 2015-10-29 00:00:00
4 1 7 2015-10-30 00:00:00 2015-10-31 00:00:00
您在查询时遇到错误,因为在联合的下端,您使用的是 pN.task_id 而不是 d.task_id,这是一个无限循环。
它应该是这样的:
WITH projectsNumbered (Project, Task_Id, Start_Date, End_Date, Days) AS
(SELECT 1 As "Project"
, Task_Id
, Start_Date
, End_Date
, Days
FROM daysBetweenTasks
WHERE Task_Id = 1
UNION ALL
SELECT
CASE WHEN COALESCE(d.Days,0) = 0 THEN pN.Project
ELSE pN.Project + 1
END AS "Project"
, d.Task_Id
, d.Start_Date
, d.End_Date
, d.Days
FROM projectsNumbered pN
JOIN daysBetweenTasks d on d.task_Id = pN.task_Id + 1
)
SELECT *
FROM projectsNumbered;
输出
PROJECT TASK_ID START_DATE END_DATE DAYS
1 1 01-OCT-15 02-OCT-15
1 2 02-OCT-15 03-OCT-15 0
1 3 03-OCT-15 04-OCT-15 0
2 4 13-OCT-15 14-OCT-15 9
2 5 14-OCT-15 15-OCT-15 0
3 6 28-OCT-15 29-OCT-15 13
4 7 30-OCT-15 31-OCT-15 1
您好,我有以下问题...
我想要一个如下的数据集,只有4列,查询得到项目。项目由前一行分隔,前一行 end_date 与当前行 start_date 不同。所以前三行将在同一个项目中。第二个项目将是接下来的两行,项目 3 和 4 将是倒数第二和最后一行。
DAYS,TASK_ID,START_DATE,END_DATE
NULL,1,10/1/2015,10/2/2015
0,2,10/2/2015,10/3/2015
0,3,10/3/2015,10/4/2015
9,4,10/13/2015,10/14/2015
0,5,10/14/2015,10/15/2015
13,6,10/28/2015,10/29/2015
1,7,10/30/2015,10/31/2015
所以输出看起来像
PROJECT,DAYS,TASK_ID,START_DATE,END_DATE
1,NULL,1,10/1/2015,10/2/2015
1,0,2,10/2/2015,10/3/2015
1,0,3,10/3/2015,10/4/2015
2,9,4,10/13/2015,10/14/2015
2,0,5,10/14/2015,10/15/2015
3,13,6,10/28/2015,10/29/2015
4,1,7,10/30/2015,10/31/2015
我是一名 sql 服务器架构师,我已经执行了 sql 服务器查询,但我在 oracle 查询中遇到错误。这是我到目前为止在 Oracle 中的内容。
WITH projectsNumbered (Project, Task_Id, Start_Date, End_Date, Days) AS
(SELECT 1 As "Project"
, Task_Id
, Start_Date
, End_Date
, Days
FROM daysBetweenTasks
WHERE Task_Id = 1
UNION ALL
SELECT
CASE WHEN COALESCE(pN.Days,0) = 0 THEN pN.Project
ELSE pN.Project + 1
END AS "Project"
, pN.Task_Id
, pN.Start_Date
, pN.End_Date
, pN.Days
FROM projectsNumbered pN
JOIN daysBetweenTasks d on p.task_Id = pN.task_Id + 1
)
--SEARCH DEPTH FIRST BY Task_Id SET order1
CYCLE Task_Id SET cycle TO '1' DEFAULT 0
SELECT *
FROM projectsNumbered
但我只返回两行。我在遇到错误后添加了 Cycle Clause。我认为 oracle dba 可能会处理这个问题?顺便说一句,我正在使用 11g Express。
Oracle 设置:
CREATE TABLE daysBetweenTasks ( DAYS,TASK_ID,START_DATE,END_DATE ) AS
SELECT NULL, 1, DATE '2015-10-01', DATE '2015-10-02' FROM DUAL UNION ALL
SELECT 0, 2, DATE '2015-10-02', DATE '2015-10-03' FROM DUAL UNION ALL
SELECT 0, 3, DATE '2015-10-03', DATE '2015-10-04' FROM DUAL UNION ALL
SELECT 9, 4, DATE '2015-10-13', DATE '2015-10-14' FROM DUAL UNION ALL
SELECT 0, 5, DATE '2015-10-14', DATE '2015-10-15' FROM DUAL UNION ALL
SELECT 13, 6, DATE '2015-10-28', DATE '2015-10-29' FROM DUAL UNION ALL
SELECT 1, 7, DATE '2015-10-30', DATE '2015-10-31' FROM DUAL;
查询:
SELECT DENSE_RANK() OVER ( ORDER BY CONNECT_BY_ROOT( task_id ) ) AS project,
days,
task_id,
start_date,
end_date
FROM (
SELECT d.*,
LAG( end_date ) OVER ( ORDER BY task_id ) AS prev_end_date
FROM daysBetweenTasks d
) d
START WITH prev_end_date IS NULL
OR prev_end_date <> start_date
CONNECT BY PRIOR end_date = start_date;
输出:
PROJECT DAYS TASK_ID START_DATE END_DATE
---------- ---------- ---------- ------------------- -------------------
1 1 2015-10-01 00:00:00 2015-10-02 00:00:00
1 0 2 2015-10-02 00:00:00 2015-10-03 00:00:00
1 0 3 2015-10-03 00:00:00 2015-10-04 00:00:00
2 9 4 2015-10-13 00:00:00 2015-10-14 00:00:00
2 0 5 2015-10-14 00:00:00 2015-10-15 00:00:00
3 13 6 2015-10-28 00:00:00 2015-10-29 00:00:00
4 1 7 2015-10-30 00:00:00 2015-10-31 00:00:00
您在查询时遇到错误,因为在联合的下端,您使用的是 pN.task_id 而不是 d.task_id,这是一个无限循环。 它应该是这样的:
WITH projectsNumbered (Project, Task_Id, Start_Date, End_Date, Days) AS
(SELECT 1 As "Project"
, Task_Id
, Start_Date
, End_Date
, Days
FROM daysBetweenTasks
WHERE Task_Id = 1
UNION ALL
SELECT
CASE WHEN COALESCE(d.Days,0) = 0 THEN pN.Project
ELSE pN.Project + 1
END AS "Project"
, d.Task_Id
, d.Start_Date
, d.End_Date
, d.Days
FROM projectsNumbered pN
JOIN daysBetweenTasks d on d.task_Id = pN.task_Id + 1
)
SELECT *
FROM projectsNumbered;
输出
PROJECT TASK_ID START_DATE END_DATE DAYS
1 1 01-OCT-15 02-OCT-15
1 2 02-OCT-15 03-OCT-15 0
1 3 03-OCT-15 04-OCT-15 0
2 4 13-OCT-15 14-OCT-15 9
2 5 14-OCT-15 15-OCT-15 0
3 6 28-OCT-15 29-OCT-15 13
4 7 30-OCT-15 31-OCT-15 1