2013-02-14 4 views
4

Я столкнулся с вопросом во время работы и не смог понять, как именно он работает. То, что делает запрос, - это поиск всех родителей для лица, которое является его родителем сегодня.Иерархические запросы «НАЧАТЬ С», где поведение предложения

Теперь уловка в том, что каждое родительское дочернее отношение имеет продолжительность, для которой она действительна.

Возьмите этот набор данных в качестве ссылки:

GrandParent является родителем Отца от 01.01.2012 до 02-02-2015

Отец родитель ребенка от 01.01.2012 до 02.02.2011

Ребенок просто самый низкий уровень человек

NewFather является родителем ребенка от 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; 

Теперь то, что я не понимаю:

как можно где условие в начале с п влияют на поведение запрос таким образом?

Другое дело, что я не люблю об этом запросе в том, что он использует совершенно не связанную таблицу с именем ci_acct_per, который просто имеет столбец per_id в нем для каждого человека в ci_per_per.

Можем ли мы сделать лучше? Доступен ли более чистый подход для исправления исходного запроса?

UPDATE

Этот запрос работает только при путешествии выше в иерархии и нет, если мы ищем ребенок. Однако этот запрос никогда не ищет детей и не должен.

+0

пожалуйста, помогите ребятам! – MozenRath

ответ

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


Update Благодаря @ user1395 example:

Это трудно объясните, почему этот странный запрос работает, потому что он не ...

Что действительно происходит, так это то, что в предложении START WITH используется per_id2, который является столбцом «отец», поэтому, если их более одного (одно не относится к sysdate), вам все равно нужно , а не, чтобы начать с него.
Другими словами, это не начинается с «ребенка», а от «детей» отцов - «отец» и «новорожденный».

Таким образом, либо использовать @ user1395 предложению, имеющая дату логики как в пункте connect by, чтобы остановить, когда отец не имеет отношения, и 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

+1

Я понятия не имею ... это даже странно - это также работает http://www.sqlfiddle.com/#!4/e9e68/10 –

+0

hahahaha! Я предполагаю, что реализация where clause для основного sql является ошибкой! Или подождите, как указано выше, это может быть способ «НАЧАТЬ С»! – MozenRath

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

+0

, кроме того, ваш подход работает только для 1 уровня иерархии, а не для многоуровневого! – MozenRath

+0

Вот что я думал, что ты хочешь. Я был смущен вашими утверждениями: «То, что делает запрос, - это поиск всех родителей для человека, который является его родителем сегодня». «Теперь список родителей, действительных сегодня для Ребенка, должен состоять только из NewFather», «Это приводит к это дает список родителей как NewFather, GrandParent, который полностью НЕПРАВИЛЬНО! " - Я как-то думал, что GrandParent был родителем NewFather, извините. – user1395