带有排序的递归子查询

时间:2018-04-20 10:42:13

标签: sql oracle sql-order-by depth-first-search recursive-query

我查看了Tim Hall的优秀文章here,它允许您使用自引用实体并显示分层数据(从顶级节点开始并递归连接),在Oracle中使用类似CTE的语法。 / p>

所以我的代码看起来像这样:

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语法。
  • 你如何解决这个问题,所以最后的数据按名称排序,按层次结构的每个深度级别排序?

  • (如果数据在连接点处正确排序,则SEARCH DEPTH FIRST创建的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的字母顺序排序,每位父母
  • 等等。

更新: 我设法稍微调整了代码,因此锚选择被排序:

但我似乎无法将相同的语法糖应用于递归连接。

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

1 个答案:

答案 0 :(得分:1)

最初,我看不到比创建临时表更优雅的解决方案。

我在想,SQL Oracle的尴尬方言是什么:

  1. 为什么没有IF TABLE EXISTS DELETE TABLE?
  2. 为什么我必须用字符串执行EXECUTE IMMEDIATE?为什么我不能自己做DROP TABLE TEMP?
  3. 为什么我没有在ANCHOR的括号中嵌套订购?
  4. 为什么在UNION ALL之后无法通过递归SELECT进行ORDER BY?
  5. SQL WITH需要标准化。其他数据库方言不需要在WITH语句上括号列名称。如果你不这样做,你会在UNION ALL之后的递归连接时得到一些毫无意义的ALIAS错误。
  6. 分页:见here无限制/偏移
  7. 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)向我指出我的SEARCH DEPTH FIRST刚应该是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