具有where子句行为的分层查询“START WITH”

时间:2013-02-14 07:20:47

标签: database oracle hierarchy connect-by

我在工作期间遇到了一个查询,无法弄清楚它是如何工作的。查询的作用是为所有父母寻找今天的父母。

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

将此数据集作为参考:

  

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

     

父亲是儿童的父母,从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的完全不相关的表,其中per_id中的每个人都只有一列ci_per_per

  

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

更新

此查询仅适用于在层次结构中向上移动而不是在寻找子项时。但是,此查询从不会查找子项,也不应该查找。

2 个答案:

答案 0 :(得分: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无关),你还需要不< / strong>从头开始。
换句话说,它不是从“孩子”开始,而是从“孩子”父亲 - “父亲”和“新父”开始。

因此,使用@ 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

答案 1 :(得分: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);

如果当前日期没有有效的父母,这将停止攀升。