4

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

现在这里的技巧是每个父子关系都有一个有效的持续时间。

以此数据集为参考:

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

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

孩子只是最底层的人

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

现在,今天对孩子有效的父母名单应该只包括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.

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

更新

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

4

2 回答 2

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;

这是一个 sqlfiddle 演示


更新感谢@user1395示例

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

真正发生的是该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;

另一个 sqlfiddle 演示

于 2013-02-14T11:08:26.387 回答
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);

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

于 2013-02-14T09:11:33.613 回答