带排序的递归子查询
Recursive Subquerying with sorting
我看了 Tim Hall 的优秀文章 here,它允许您使用自引用实体并显示分层数据(从顶级节点开始并递归地返回),在 Oracle 中使用类似 CTE 的语法.
所以我的代码看起来像这样:
WITH J1(JOBMST_ID, JOBMST_NAME, JOBMST_PRNTID, JOBMST_TYPE, LVL) AS (
SELECT JOBMST_ID, JOBMST_NAME, JOBMST_PRNTID, JOBMST_TYPE, 1
FROM TIDAL.JOBMST
WHERE JOBMST_PRNTID IS NULL
UNION ALL
SELECT J2.JOBMST_ID,J2.JOBMST_NAME,J2.JOBMST_PRNTID, J2.JOBMST_TYPE, J1.LVL+1
FROM TIDAL.JOBMST J2
INNER JOIN J1 ON J2.JOBMST_PRNTID = J1.JOBMST_ID
WHERE J2.JOBMST_PRNTID IS NOT NULL)
SEARCH DEPTH FIRST BY JOBMST_ID SET DISP_SEQ
SELECT *
FROM J1
ORDER BY DISP_SEQ
对于锚行(我的 SQL 中的顶级层次结构 J1 条目,父级为 NULL),我想:
ORDER BY J1.JOBMST_NAME
对于递归连接:
ORDER BY J2.JOBMST_PRNTID, J2.JOBMST_NAME
- 如果我尝试在 UNION ALL 语句上方添加 ORDER BY 语句,我会得到某种无效的 SQL 语法。
您如何解决这个问题,以便最终数据在层次结构的每个深度级别按名称字母顺序排序?
(如果数据在连接点正确排序,搜索深度优先创建的 DISP_SEQ 应该正确整理数据)。
你最终得到这样的东西(省略名字):
JOBMST_ID JOBMST_NAME JOBMST_PRNTID JOBMST_TYPE LVL DISP_SEQ
746 1 1 1
1433 1 1 2
1328 1433 1 2 3
1329 1328 1 3 4
1330 1329 1 4 5
1331 1329 1 4 6
1332 1329 1 4 7
我的目标:
- 所有级别 1 均按 JOBMST_NAME
的字母顺序排序
- 级别 1 中的所有级别 2 按 JOBMST_NAME 每个父级
的字母顺序排序
- 2 级中的所有 3 级都按 JOBMST_NAME 的字母顺序排序,每位家长,
- 等等等等……
更新:
我设法稍微调整了代码,所以锚 select 被排序:
但我似乎无法将相同的语法糖应用于递归连接。
WITH J1(JOBMST_ID, JOBMST_NAME, JOBMST_PRNTID, JOBMST_TYPE, LVL) AS (
SELECT * FROM (
SELECT JOBMST_ID, JOBMST_NAME, JOBMST_PRNTID, JOBMST_TYPE, 1
FROM TIDAL.JOBMST
WHERE JOBMST_PRNTID IS NULL
ORDER BY JOBMST_NAME
)
UNION ALL
SELECT J2.JOBMST_ID, J2.JOBMST_NAME, J2.JOBMST_PRNTID, J2.JOBMST_TYPE, J1.LVL + 1
FROM TIDAL.JOBMST J2
INNER JOIN J1 ON J2.JOBMST_PRNTID = J1.JOBMST_ID
WHERE J2.JOBMST_PRNTID IS NOT NULL
)
SEARCH DEPTH FIRST BY JOBMST_ID SET DISP_SEQ
SELECT *
FROM J1
ORDER BY DISP_SEQ
最初,我找不到比创建临时文件更优雅的解决方案 table。
我在想,SQL Oracle 的方言是多么尴尬:
- 为什么没有 IF TABLE EXISTS DELETE TABLE?
- 为什么我必须对字符串执行 EXECUTE IMMEDIATE?为什么我不能单独执行 DROP TABLE TEMP?
- 为什么我不能在不嵌套在 ANCHOR 括号中的情况下使用 ORDER BY?
- 为什么我不能在 UNION ALL 之后对递归 SELECT 使用 ORDER BY?
- SQL WITH 需要标准化。其他数据库方言不需要在 WITH 语句中用括号括起列名。如果你不这样做,你会得到一些无意义的 ALIAS 错误,在 UNION ALL 之后的递归连接点。
- 分页:参见here无限制/偏移
DECLARE
v_c NUMBER;
BEGIN
SELECT COUNT(*) INTO v_c FROM user_tables WHERE TABLE_NAME = 'TEMP';
IF v_c = 1 THEN
EXECUTE IMMEDIATE 'DROP TABLE TEMP';
END IF;
END;
CREATE TABLE TEMP AS (
SELECT * FROM (
SELECT JOBMST_ID, JOBMST_NAME, JOBMST_PRNTID, JOBMST_TYPE
FROM TIDAL.JOBMST
WHERE JOBMST_PRNTID IS NOT NULL
ORDER BY JOBMST_PRNTID, JOBMST_NAME
)
);
WITH J1(JOBMST_ID, JOBMST_NAME, JOBMST_PRNTID, JOBMST_TYPE, LVL) AS (
SELECT * FROM (
SELECT JOBMST_ID, JOBMST_NAME, JOBMST_PRNTID, JOBMST_TYPE, 1
FROM TIDAL.JOBMST
WHERE JOBMST_PRNTID IS NULL
ORDER BY JOBMST_NAME
)
UNION ALL
SELECT J2.JOBMST_ID, J2.JOBMST_NAME, J2.JOBMST_PRNTID, J2.JOBMST_TYPE, J1.LVL + 1
FROM TEMP J2
INNER JOIN J1 ON J2.JOBMST_PRNTID = J1.JOBMST_ID
WHERE J2.JOBMST_PRNTID IS NOT NULL
)
SEARCH DEPTH FIRST BY JOBMST_ID SET DISP_SEQ
SELECT *
FROM J1
ORDER BY DISP_SEQ;
然后 (mathguy on the Oracle Community Forum) 向我指出我的搜索深度优先应该在 JOBMST_NAME 之前完成。
然后一切就绪:
WITH J1(JOBMST_ID, JOBMST_NAME, JOBMST_PRNTID, JOBMST_TYPE, LVL) AS (
SELECT JOBMST_ID, JOBMST_NAME, JOBMST_PRNTID, JOBMST_TYPE, 1
FROM TIDAL.JOBMST
WHERE JOBMST_PRNTID IS NULL
UNION ALL
SELECT J2.JOBMST_ID, J2.JOBMST_NAME, J2.JOBMST_PRNTID, J2.JOBMST_TYPE, J1.LVL + 1
FROM TIDAL.JOBMST J2
INNER JOIN J1 ON J2.JOBMST_PRNTID = J1.JOBMST_ID
WHERE J2.JOBMST_PRNTID IS NOT NULL
)
SEARCH DEPTH FIRST BY JOBMST_NAME SET DISP_SEQ
SELECT *
FROM J1
ORDER BY DISP_SEQ
我看了 Tim Hall 的优秀文章 here,它允许您使用自引用实体并显示分层数据(从顶级节点开始并递归地返回),在 Oracle 中使用类似 CTE 的语法.
所以我的代码看起来像这样:
WITH J1(JOBMST_ID, JOBMST_NAME, JOBMST_PRNTID, JOBMST_TYPE, LVL) AS (
SELECT JOBMST_ID, JOBMST_NAME, JOBMST_PRNTID, JOBMST_TYPE, 1
FROM TIDAL.JOBMST
WHERE JOBMST_PRNTID IS NULL
UNION ALL
SELECT J2.JOBMST_ID,J2.JOBMST_NAME,J2.JOBMST_PRNTID, J2.JOBMST_TYPE, J1.LVL+1
FROM TIDAL.JOBMST J2
INNER JOIN J1 ON J2.JOBMST_PRNTID = J1.JOBMST_ID
WHERE J2.JOBMST_PRNTID IS NOT NULL)
SEARCH DEPTH FIRST BY JOBMST_ID SET DISP_SEQ
SELECT *
FROM J1
ORDER BY DISP_SEQ
对于锚行(我的 SQL 中的顶级层次结构 J1 条目,父级为 NULL),我想:
ORDER BY J1.JOBMST_NAME
对于递归连接:
ORDER BY J2.JOBMST_PRNTID, J2.JOBMST_NAME
- 如果我尝试在 UNION ALL 语句上方添加 ORDER BY 语句,我会得到某种无效的 SQL 语法。
您如何解决这个问题,以便最终数据在层次结构的每个深度级别按名称字母顺序排序?
(如果数据在连接点正确排序,搜索深度优先创建的 DISP_SEQ 应该正确整理数据)。
你最终得到这样的东西(省略名字):
JOBMST_ID JOBMST_NAME JOBMST_PRNTID JOBMST_TYPE LVL DISP_SEQ
746 1 1 1
1433 1 1 2
1328 1433 1 2 3
1329 1328 1 3 4
1330 1329 1 4 5
1331 1329 1 4 6
1332 1329 1 4 7
我的目标:
- 所有级别 1 均按 JOBMST_NAME 的字母顺序排序
- 级别 1 中的所有级别 2 按 JOBMST_NAME 每个父级 的字母顺序排序
- 2 级中的所有 3 级都按 JOBMST_NAME 的字母顺序排序,每位家长,
- 等等等等……
更新: 我设法稍微调整了代码,所以锚 select 被排序:
但我似乎无法将相同的语法糖应用于递归连接。
WITH J1(JOBMST_ID, JOBMST_NAME, JOBMST_PRNTID, JOBMST_TYPE, LVL) AS (
SELECT * FROM (
SELECT JOBMST_ID, JOBMST_NAME, JOBMST_PRNTID, JOBMST_TYPE, 1
FROM TIDAL.JOBMST
WHERE JOBMST_PRNTID IS NULL
ORDER BY JOBMST_NAME
)
UNION ALL
SELECT J2.JOBMST_ID, J2.JOBMST_NAME, J2.JOBMST_PRNTID, J2.JOBMST_TYPE, J1.LVL + 1
FROM TIDAL.JOBMST J2
INNER JOIN J1 ON J2.JOBMST_PRNTID = J1.JOBMST_ID
WHERE J2.JOBMST_PRNTID IS NOT NULL
)
SEARCH DEPTH FIRST BY JOBMST_ID SET DISP_SEQ
SELECT *
FROM J1
ORDER BY DISP_SEQ
最初,我找不到比创建临时文件更优雅的解决方案 table。
我在想,SQL Oracle 的方言是多么尴尬:
- 为什么没有 IF TABLE EXISTS DELETE TABLE?
- 为什么我必须对字符串执行 EXECUTE IMMEDIATE?为什么我不能单独执行 DROP TABLE TEMP?
- 为什么我不能在不嵌套在 ANCHOR 括号中的情况下使用 ORDER BY?
- 为什么我不能在 UNION ALL 之后对递归 SELECT 使用 ORDER BY?
- SQL WITH 需要标准化。其他数据库方言不需要在 WITH 语句中用括号括起列名。如果你不这样做,你会得到一些无意义的 ALIAS 错误,在 UNION ALL 之后的递归连接点。
- 分页:参见here无限制/偏移
DECLARE
v_c NUMBER;
BEGIN
SELECT COUNT(*) INTO v_c FROM user_tables WHERE TABLE_NAME = 'TEMP';
IF v_c = 1 THEN
EXECUTE IMMEDIATE 'DROP TABLE TEMP';
END IF;
END;
CREATE TABLE TEMP AS (
SELECT * FROM (
SELECT JOBMST_ID, JOBMST_NAME, JOBMST_PRNTID, JOBMST_TYPE
FROM TIDAL.JOBMST
WHERE JOBMST_PRNTID IS NOT NULL
ORDER BY JOBMST_PRNTID, JOBMST_NAME
)
);
WITH J1(JOBMST_ID, JOBMST_NAME, JOBMST_PRNTID, JOBMST_TYPE, LVL) AS (
SELECT * FROM (
SELECT JOBMST_ID, JOBMST_NAME, JOBMST_PRNTID, JOBMST_TYPE, 1
FROM TIDAL.JOBMST
WHERE JOBMST_PRNTID IS NULL
ORDER BY JOBMST_NAME
)
UNION ALL
SELECT J2.JOBMST_ID, J2.JOBMST_NAME, J2.JOBMST_PRNTID, J2.JOBMST_TYPE, J1.LVL + 1
FROM TEMP J2
INNER JOIN J1 ON J2.JOBMST_PRNTID = J1.JOBMST_ID
WHERE J2.JOBMST_PRNTID IS NOT NULL
)
SEARCH DEPTH FIRST BY JOBMST_ID SET DISP_SEQ
SELECT *
FROM J1
ORDER BY DISP_SEQ;
然后 (mathguy on the Oracle Community Forum) 向我指出我的搜索深度优先应该在 JOBMST_NAME 之前完成。
然后一切就绪:
WITH J1(JOBMST_ID, JOBMST_NAME, JOBMST_PRNTID, JOBMST_TYPE, LVL) AS (
SELECT JOBMST_ID, JOBMST_NAME, JOBMST_PRNTID, JOBMST_TYPE, 1
FROM TIDAL.JOBMST
WHERE JOBMST_PRNTID IS NULL
UNION ALL
SELECT J2.JOBMST_ID, J2.JOBMST_NAME, J2.JOBMST_PRNTID, J2.JOBMST_TYPE, J1.LVL + 1
FROM TIDAL.JOBMST J2
INNER JOIN J1 ON J2.JOBMST_PRNTID = J1.JOBMST_ID
WHERE J2.JOBMST_PRNTID IS NOT NULL
)
SEARCH DEPTH FIRST BY JOBMST_NAME SET DISP_SEQ
SELECT *
FROM J1
ORDER BY DISP_SEQ