【问题标题】:Oracle "connect by prior" along with "max() over partition by" to remove duplicate subtreeOracle "connect by prior" 和 "max() over partition by" 删除重复的子树
【发布时间】:2019-01-31 14:29:35
【问题描述】:

我正在尝试删除由“先连接”子句返回的重复子树。我希望检查树层次结构的顶级节点,用户可以在其中输入已经是子树一部分的子 ID。看看下面的例子:

SELECT * FROM (
With test_hierarchy as(
       SELECT 'a' parent, 'b' child FROM dual UNION ALL
       SELECT 'b','c' FROM dual UNION ALL
       SELECT 'd','e' FROM dual UNION ALL
       SELECT 'e','f' FROM dual UNION ALL
       SELECT 'f','g' FROM dual UNION ALL
       SELECT 'f','h' FROM dual)
SELECT
    parent,
    child,
    CONNECT_BY_ROOT child AS init_child,
    LEVEL,
    CONNECT_BY_ISLEAF,
    MAX(LEVEL) OVER(
        PARTITION BY parent
    ) AS max_level
FROM
    test_hierarchy
WHERE
    CONNECT_BY_ISLEAF = 1
START WITH
    child IN (
        'c', 'b', 'e', 'f', 'h', 'g'
    )
CONNECT BY
    PRIOR parent = child);

此查询返回结果为:

P C I      LEVEL CONNECT_BY_ISLEAF  MAX_LEVEL
- - - ---------- ----------------- ----------
a b b          1                 1          2
a b c          2                 1          2
d e g          3                 1          3
d e f          2                 1          3
d e h          3                 1          3
d e e          1                 1          3

我希望只返回那些 level = max_level 的顶级节点。 IE。我的查询应返回结果为:

P C I      LEVEL CONNECT_BY_ISLEAF  MAX_LEVEL
- - - ---------- ----------------- ----------
a b c          2                 1          2
d e g          3                 1          3
d e h          3                 1          3

如果我尝试使用 WHERE 子句作为“WHERE level = max_level”过滤掉结果,Oracle 会抱怨:

ORA-01788: CONNECT BY clause required in this query block
01788. 00000 -  "CONNECT BY clause required in this query block"

如果您对如何操作有任何想法,请告诉我。 谢谢,

【问题讨论】:

    标签: sql database oracle connect-by partition-by


    【解决方案1】:

    将您的递归查询包装在另一个 CTE 中并对其进行过滤:

    WITH 
      test_hierarchy AS (
        SELECT 'a' parent, 'b' child FROM dual UNION ALL
        SELECT 'b','c' FROM dual UNION ALL
        SELECT 'd','e' FROM dual UNION ALL
        SELECT 'e','f' FROM dual UNION ALL
        SELECT 'f','g' FROM dual UNION ALL
        SELECT 'f','h' FROM dual
      ),
      recursion AS (
        SELECT
          parent,
          child,
          CONNECT_BY_ROOT child AS init_child,
          LEVEL AS lvl,
          CONNECT_BY_ISLEAF AS isleaf,
          MAX(LEVEL) OVER(
            PARTITION BY parent
          ) AS max_level
        FROM
          test_hierarchy
        START WITH child IN ('c', 'b', 'e', 'f', 'h', 'g')
        CONNECT BY PRIOR parent = child
      )
    SELECT *
    FROM recursion
    WHERE isleaf = 1 AND lvl = max_level
    

    【讨论】:

      【解决方案2】:

      您的逻辑有效,但有点蛮力方法,即检查所有可能性并仅选择有效的。

      一种替代方法很简单,将您的START WITH 限制为仅考虑 leave 节点。

      这可以通过排除所有作为节点的节点来完成:

      START WITH
          child IN ( 'c', 'b', 'e', 'f', 'h', 'g') and
          child not in (select parent from test_hierarchy)
      

      由于 START 列表有限,最终查询具有更好的性能,并且您无需 WINDOWS SORT 即可获得最高级别:

      With test_hierarchy as(
             SELECT 'a' parent, 'b' child FROM dual UNION ALL
             SELECT 'b','c' FROM dual UNION ALL
             SELECT 'd','e' FROM dual UNION ALL
             SELECT 'e','f' FROM dual UNION ALL
             SELECT 'f','g' FROM dual UNION ALL
             SELECT 'f','h' FROM dual)
      SELECT
          parent,
          child,
          CONNECT_BY_ROOT child AS init_child,
          LEVEL,
          CONNECT_BY_ISLEAF
      FROM
          test_hierarchy
      WHERE
          CONNECT_BY_ISLEAF = 1 
      START WITH
          child IN ( 'c', 'b', 'e', 'f', 'h', 'g') and
          child not in (select parent from test_hierarchy)
      CONNECT BY
          PRIOR parent = child;
      
      P C I      LEVEL CONNECT_BY_ISLEAF
      - - - ---------- -----------------
      a b c          2                 1
      d e g          3                 1
      d e h          3                 1
      

      【讨论】:

      • Marmite,感谢您的高效查询。与我的分析函数方法相比,它的性能稍好一些。因此,我将在我的解决方案中使用您的查询。干杯!我仍然会将@Lukas Eder 的查询保留为已接受的答案,因为他准确地回答了问题中的问题:)
      • @user613114 谢谢,我回答的问题与你所问的有点不同;)
      猜你喜欢
      • 2018-09-01
      • 1970-01-01
      • 1970-01-01
      • 2019-03-21
      • 2013-02-24
      • 1970-01-01
      • 1970-01-01
      • 2011-04-25
      • 1970-01-01
      相关资源
      最近更新 更多