Помогите,плиз, с хитрым селектом

voprosik20012
Дата: 08.12.2012 13:24:42
Добрый день.
Помогите пожалуйста написать нужный селект.
Есть 4 таблицы с такими данными:
create table INS(i NUMBER);
create table REL(s NUMBER,o NUMBER);
create table CON(ci NUMBER,cnum varCHAR2(10));
create table CNT(ci NUMBER,cnum varCHAR2(10));

insert into INS (i) values (1);
insert into INS (i) values (2);
insert into INS (i) values (3);
insert into INS (i) values (4);
insert into INS (i) values (5);
insert into INS (i) values (6);
insert into INS (i) values (7);
insert into INS (i) values (8);
insert into INS (i) values (9);
insert into INS (i) values (10);
insert into INS (i) values (11);
insert into INS (i) values (12);
insert into INS (i) values (13);
insert into INS (i) values (14);
insert into INS (i) values (15);
insert into INS (i) values (16);
insert into INS (i) values (17);
insert into INS (i) values (18);
insert into INS (i) values (19);
insert into INS (i) values (20);

insert into REL (s, o) values (1, 5);
insert into REL (s, o) values (7, 9);
insert into REL (s, o) values (11, 15);
insert into REL (s, o) values (17, 19);

insert into CON (ci, cnum) values (1, '111');
insert into CON (ci, cnum) values (5, '555');
insert into CON (ci, cnum) values (3, '333');
insert into CON (ci, cnum) values (9, '999');
insert into CON (ci, cnum) values (11, 'a11');
insert into CON (ci, cnum) values (18, '188');

insert into CNT (ci, cnum) values (2, '22222');
insert into CNT (ci, cnum) values (1, '11111');
insert into CNT (ci, cnum) values (17, '171717');
insert into CNT (ci, cnum) values (3, '33333);
insert into CNT (ci, cnum) values (15, '151515');
insert into CNT (ci, cnum) values (5, '55555');


В таблице REL содержатся отношения между элементами из INS, а таблицах CON, CNT данные для INS.
Нужно принести все строки из INS.
Для каждого элемента INS.i и REL.o где INS.i = REL.s(+) нужно принести данные из CON и CNT, где
INS.i = CON.ci(+) и INS.i = CNT.ci(+).
Т.е. если в CON есть данные и для INS.i и для REL.o принести все строки данных для них. Если нет для INS.i, но есть для REL.o, то принести только для REL.o но поместить их в строку INS.i. Если нет для обоих, то INS.i будет с пустыми данными.
Если для INS.i есть данные и в CON и в CNT, они должны быть в одной строке.
Для этих данных селект должен вернуть 21 строку:
Num	INS.I	CON.CI	CNUM_1	CNT.CI	CNUM_2
1	1	1	111	1	11111
2	1	5	555	5	55555
3	2			2	22222
4	3	3	333	3	33333
5	4				
6	5	5	555	5	55555
7	6				
8	7	9	999		
9	8				
10	9	9	999		
11	10				
12	11	11	a11	15	151515
13	12				
14	13				
15	14				
16	15			15	151515
17	16				
18	17			17	171717
19	18	18	188		
20	19				
21	20				

Данные только для примера, на самом деле таблицы гораздо больше и INS.i например - семизначный индекс.
voprosik20012
Дата: 09.12.2012 14:57:34
На данный момент я смог сделать нужное только с помощью pipe lined функции. Но хотелось бы обойтись протым селектом. И из-за того, что таблицы очень больщие использовать поменьше объединений и проверок exists/not exists.
Заранее спасибо.
AmKad
Дата: 09.12.2012 15:30:39
voprosik20012,

Твоя задача поставлена как-то мудрено. Ломать голову на выходных неохота. Будь проще, и люди к тебе потянутся.
SY
Дата: 09.12.2012 16:47:40
Не уверен что правильно понл условия:

select  a.i ins_i,
        c.ci con_ci,
        c.cnum cnum1,
        d.ci cnt_ci,
        d.cnum cnum2
  from      ins a
        left join
            rel b
          on a.i = b.s
        left join
            con c
          on nvl(b.s,a.i) = c.ci or nvl(b.o,a.i) = c.ci
        left join
            cnt d
          on d.ci = case c.ci
                      when b.o then nvl(b.s,a.i)
                      when b.s then nvl(b.o,a.i)
                      else a.i
                    end
  order by i,
           con_ci
/

     INS_I     CON_CI CNUM1          CNT_CI CNUM2
---------- ---------- ---------- ---------- ----------
         1          1 111                 5 55555
         1          5 555                 1 11111
         2                                2 22222
         3          3 333
         4
         5          5 555                 5 55555
         6
         7          9 999
         8
         9          9 999
        10

     INS_I     CON_CI CNUM1          CNT_CI CNUM2
---------- ---------- ---------- ---------- ----------
        11         11 a11                15 151515
        12
        13
        14
        15                               15 151515
        16
        17                               17 171717
        18         18 188
        19
        20

21 rows selected.

SQL> 


SY.
voprosik20012
Дата: 09.12.2012 17:37:23
Ок, попробую сформулировать по-другому. Нужно принести все строки из таблицы INS. Для каждой строки из INS нужно принести данные из CON и CNT. И в CON и в CNT может быть несколько соответствющих строк, может не быть вообще. Есть также таблица REL, задающая отношения между элементами из INS - сын - отец. И так же элемент из INS может быть в таблице REL как сын и может не быть. Если в REL есть отец для текущего элемента из INS, то нужно принести данные из CON и CNT также и для отца. Возможна ситуация когда в CON и CNT есть данные только для отца - тогда эти данные нужно принести 2 раза - один раз через отношение из REL и второй раз когда элемент из INS это сам отец. Отношений сын - отец - дед и т.п. быть не может. У сына может быть только один отец, но у отца может быть несколько сыновей.
В результате нужно получить строку вида элемент INS | для кого показаны данные (снова сам элемент или его отец)| данные из CON | данные из CNT.
Если например в INS 5 элементов 1,2,3,4,5, есть отношение (1,5) и в CON и CNT есть по одной строке для 1 и 5, и в CNT есть данные для 3, а в CON для 4, то получим:
1 | 1 | CON1 | CNT1
1 | 5 | CON5 | CNT5
2 |   |      |  
3 | 3 |      | CNT3
4 |   | CON4 |
5 | 5 | CON5 | CNT5

Если бы в CON и CNT не было данных для 1, то получили бы результат без первой строки.
Надеюсь так немного лучше.
voprosik20012
Дата: 09.12.2012 17:47:40
SY,
Спасибо, но немного не то. У 1 должно быть
1	1	111	1	11111
1	5	555	5	55555

а у Вас
1          1            111         5            55555
1          5            555         1            11111

и у 3 не хватает данных из CNT
SY
Дата: 09.12.2012 18:53:19
voprosik20012
Спасибо, но немного не то.


with t as (
           select  s,
                   o,
                   ci,
                   count(*) over(partition by s,o) c
             from  rel,
                   con
             where s = ci
                or o = ci
          )
select  a.i ins_i,
        c.ci con_ci,
        c.cnum cnum1,
        d.ci cnt_ci,
        d.cnum cnum2
  from      ins a
        left join
            t b
          on a.i = b.s
        left join
            con c
          on nvl(b.ci,a.i) = c.ci
        left join
            cnt d
          on d.ci = case b.c
                      when 2 then c.ci
                      else nvl(b.o,a.i)
                    end
  order by i,
           con_ci
/

     INS_I     CON_CI CNUM1          CNT_CI CNUM2
---------- ---------- ---------- ---------- ----------
         1          1 111                 1 11111
         1          5 555                 5 55555
         2                                2 22222
         3          3 333                 3 33333
         4
         5          5 555                 5 55555
         6
         7          9 999
         8
         9          9 999
        10

     INS_I     CON_CI CNUM1          CNT_CI CNUM2
---------- ---------- ---------- ---------- ----------
        11         11 a11                15 151515
        12
        13
        14
        15                               15 151515
        16
        17                               17 171717
        18         18 188
        19
        20

21 rows selected.

SQL> 


SY.
voprosik20012
Дата: 10.12.2012 01:14:36
SY,
Большое спасибо, очень похоже на правду.
Называется век живи - век учись. Я пытался написать селект используя Оракловские (+), к такому синтаксису не привык.
Можете подсказать, пожалуйста, хорошую книгу по SQL, PL/SQL, желательно на русском?

Еще раз большое спасибо.
SY
Дата: 10.12.2012 02:23:26
voprosik20012
Я пытался написать селект используя Оракловские (+)


with t1 as (
            select  s,
                    o,
                    ci,
                    count(*) over(partition by s,o) c
              from  rel,
                    con
              where s = ci
                 or o = ci
           ),
     t2 as (
            select  *
              from  ins a,
                    t1 b
              where a.i = b.s(+)
           )
select  a.i,
        b.ci con_ci,
        b.cnum cnum1,
        c.ci cnt_ci,
        c.cnum cnum2
  from  t2 a,
        con b,
        cnt c
  where nvl(a.ci,a.i) = b.ci(+)
    and c.ci(+) = case a.c
                    when 2 then a.ci
                    else nvl(a.o,a.i)
                  end
  order by i,
           con_ci
/

         I     CON_CI CNUM1          CNT_CI CNUM2
---------- ---------- ---------- ---------- ---------
         1          1 111                 1 11111
         1          5 555                 5 55555
         2                                2 22222
         3          3 333                 3 33333
         4
         5          5 555                 5 55555
         6
         7          9 999
         8
         9          9 999
        10

         I     CON_CI CNUM1          CNT_CI CNUM2
---------- ---------- ---------- ---------- ---------
        11         11 a11                15 151515
        12
        13
        14
        15                               15 151515
        16
        17                               17 171717
        18         18 188
        19
        20

21 rows selected.

SQL> 


SY.