【问题标题】:Hierarchical Queries "START WITH" with where clause behavior带有 where 子句行为的分层查询“START WITH”
【发布时间】:2013-01-29 22:49:41
【问题描述】:

我在工作中遇到了一个查询,但无法弄清楚它是如何工作的。查询的作用是查找今天是其父级的人的所有父级。

这里的诀窍是每个父子关系都有一个有效的持续时间。

以此数据集作为参考:

从 01-01-2012 到 02-02-2015,GrandParent 是父亲的父母

从 01-01-2012 到 02-02-2011,父亲是孩子的父母

孩子只是最底层的人

NewFather 是 Child 从 01-01-2012 到 02-02-2014 的父母

现在对 Child 有效的父母列表应仅包含 NewFather

为了获取列表,之前我们使用了这个 SQL:

SELECT connect_by_root per_id2 AS per_id2,
       per_id1,
       LEVEL                   AS per_level,
       n.entity_name
FROM   ci_per_per pp,
       ci_per_name N
WHERE  N.per_id = per_id1
       AND start_dt <= SYSDATE
       AND ( end_dt IS NULL
              OR end_dt >= SYSDATE )
START WITH per_id2 = :personID
CONNECT BY NOCYCLE PRIOR per_id1 = per_id2;

personID 是一个绑定变量

此查询不起作用,因为 where 子句的行为是它首先获取所有记录,然后检查非连接条件(检查开始日期和结束日期)。这导致它给出的父母名单为NewFather, GrandParent,这是完全错误的!

因此,查询更改为以下内容:

SELECT connect_by_root per_id2 AS per_id2,
       per_id1,
       LEVEL                   AS per_level,
       n.entity_name
FROM   ci_per_per pp,
       ci_per_name N
WHERE  N.per_id = per_id1
       AND start_dt <= SYSDATE
       AND ( end_dt IS NULL
              OR end_dt >= SYSDATE )
START WITH per_id2 = (SELECT per_id
                      FROM   ci_acct_per
                      WHERE  per_id = :personID
                             AND pp.start_dt <= SYSDATE
                             AND ( pp.end_dt IS NULL
                                    OR pp.end_dt >= SYSDATE ))
CONNECT BY NOCYCLE PRIOR per_id1 = per_id2;

现在我不明白的是:

start with 子句中的 where 条件如何以这种方式影响查询的行为?

我不喜欢这个查询的另一件事是,它使用了一个名为 ci_acct_per 的完全不相关的表,其中只是为 ci_per_per 中的每个人提供了一个 per_id 列。

我们可以做得更好吗?是否有更简洁的方法可用于修复原始查询?

更新

此查询仅在层次结构中较高时有效,而在我们正在寻找孩子时无效。但是,此查询从不寻找孩子,也不应该这样做。

【问题讨论】:

    标签: database oracle hierarchy connect-by


    【解决方案1】:

    您可以在 connect by 子句中使用日期逻辑。

    SELECT connect_by_root per_id2 AS per_id2,
           per_id1,
           LEVEL                   AS per_level,
           n.entity_name
    FROM   ci_per_per pp,
           ci_per_name N
    WHERE  N.per_id = per_id1
    START WITH per_id2 = :personID AND 
                             SYSDATE BETWEEN start_dt AND NVL(end_dt,SYSDATE)
    CONNECT BY NOCYCLE PRIOR per_id1 = per_id2 AND 
                             SYSDATE BETWEEN start_dt AND NVL(end_dt,SYSDATE);
    

    当当前日期没有有效的父母时,这将停止爬升。

    【讨论】:

    • 实际上我需要我在查询中使用的所有列,只是错误只影响你建议的列
    • 此外,您的方法仅适用于 1 级层次结构,不适用于多级!!!
    • 这就是我认为你想要的。我对您的陈述感到困惑:“查询的作用是查找今天作为其父母的人的所有父母。”、“现在对 Child 有效的父母列表应该只包含 NewFather”、“这会导致它给出的父母名单是新父亲、祖父母,这是完全错误的!” - 我以某种方式认为 GrandParent 是 NewFather 的父母,对不起。
    • 别出汗兄弟!只是为了网站而适当地更改您的答案或删除您的答案:)
    • @user1395, mmmm...现在这解释了一切... OP 中的奇怪查询也不起作用!现在有道理了
    【解决方案2】:

    我不确定我是否理解正确,但为什么不:

    SELECT connect_by_root per_id2 AS per_id2,
           pp.per_id1,
           LEVEL                   AS per_level,
           n.entity_name
    FROM   (select * 
            from ci_per_per
           where start_dt <= SYSDATE
           AND ( end_dt IS NULL
                  OR end_dt >= SYSDATE )) pp join
           ci_per_name N on N.per_id = pp.per_id1         
    START WITH per_id2 = 1
    CONNECT BY NOCYCLE PRIOR pp.per_id1 = pp.per_id2;
    

    Here is a sqlfiddle demo


    更新感谢@user1395 example

    很难解释奇怪的查询是如何工作的,因为它没有......

    真正发生的是 START WITH 子句使用 per_id2 这是“父亲”列,所以如果有多个(一个与 sysdate 无关),您仍然需要 从它开始。
    换句话说,它不是从“孩子”开始,而是从“孩子”父亲开始——“父亲”和“新父亲”。

    因此,要么使用 @user1395 建议在 connect by 子句中包含日期逻辑以在父亲不相关时停止,并使用 start with 子句仅使相关父亲可用,或者删除所有不相关的父亲第一位(如我之前的建议)或“从”“孩子”而不是“父亲”开始:

    select * from (
    SELECT connect_by_root per_id1 AS per_id2,
           per_id1,
           LEVEL                   AS per_level,
           n.entity_name
    FROM   ci_per_per pp,
           ci_per_name N
    WHERE  N.per_id = per_id1       
    START WITH per_id1 = 1
    CONNECT BY NOCYCLE PRIOR per_id1 = per_id2 AND start_dt <= SYSDATE
           AND ( end_dt IS NULL
                  OR end_dt >= SYSDATE ))
    where per_id1 <> per_id2;
    

    Another sqlfiddle demo

    【讨论】:

    • 现在我记得有人在写新查询之前提出了一些类似的建议,然后他想出了那个奇怪的查询,但我忘记了这一切!但是,我仍然想知道他的查询是如何工作的!
    • 我不知道......它甚至更奇怪 - 这也适用sqlfiddle.com/#!4/e9e68/10
    • 哈哈哈!我猜主 sql 的 where 子句实现是错误的!或者按照上面的答案等待,可能是START WITH的实现方式!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-11
    • 2012-01-23
    • 1970-01-01
    • 1970-01-01
    • 2019-09-02
    相关资源
    最近更新 更多