2013-02-14 64 views
4

我在工作中遇到了一個查詢,無法弄清楚它的工作原理。查詢所做的就是查找所有父母,以便今天是其父母的人員。分層查詢使用where子句行爲「START WITH」

現在這裏的訣竅是每個父母子女關係都有一個有效的持續時間。

取本數據設定爲基準:

祖父母是從2012年1月1日父的父2015年2月2日

父是從2012年1月1日兒童的父到2011年2月2日

孩子只是最低級的人

NewFather是兒童的父母從2012年1月1日至2014年2月2日

現在家長的名單至今依然有效的兒童應該只包含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; 

現在我不明白的是:

WHERE條件在開始與子句如何影響行爲以這種方式查詢?

,我不喜歡這個查詢的另一件事是,它使用一個名爲ci_acct_per它只是有它per_id中的每個人ci_per_per列一個完全不相關的表。

我們可以做得更好嗎?清理方法是否可用於修復原始查詢?

UPDATE

這個查詢只能當行駛在更高的層次,而不是如果我們正在尋找孩子。但是,這個查詢從不查找孩子,也不應該這樣做。

+0

請幫助傢伙! – MozenRath 2013-02-14 08:57:21

回答

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無關),您仍然需要而不是以此開頭。
換句話說,它不是從「孩子」開始,而是從「孩子」父親 - 「父親」和「新人」開始。

所以,要麼使用@同時在connect by條款有日期的邏輯user1395建議停止時,父親是不相關的,並且start with條款,使只有相關父親可用,或刪除放在首位所有unrelevant父親(在我以前的建議)或「開始」的「孩子」,而不是其父親:

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

+0

現在我記得建議類似於寫他之前寫新查詢的人,然後他提出了奇怪的查詢,但我忘了所有這一切!不過,我仍然想知道他的查詢是如何工作的! – MozenRath 2013-02-14 11:21:38

+1

我不知道...它更奇怪 - 這也可以http://www.sqlfiddle.com/#!4/e9e68/10 – 2013-02-14 12:03:36

+0

haha​​haha!我猜主sql的where子句實現是bug!或者按照上述的答案等待,這可能就是'START WITH'的實施方式! – MozenRath 2013-02-14 14:28:53

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); 

噹噹前沒有有效的父母時,這將停止爬升。

+0

實際上,我需要我在查詢中使用的所有列,它只是錯誤隻影響列,我建議使用 – MozenRath 2013-02-14 10:00:22

+0

此外,您的方法僅適用於1級的層次結構,而不適用於多級別! – MozenRath 2013-02-14 10:03:59

+0

這就是我以爲你想要的。我對你的陳述感到困惑:「查詢所做的是查找所有父母給今天是其父母的人。」,「現在,今天有效的孩子名單應該只包含NewFather」,「這導致它給父母的名單作爲NewFather,完全是錯誤的GrandParent!「 - 我不知何故認爲GrandParent是NewFather的父母,對不起。 – user1395 2013-02-14 11:39:39