Задачка по sql - xor join

Gleb Reutov
Дата: 11.11.2008 13:14:03
Задача: к таблице t2 нужно присоеденять (в идеале inner join-ом) таблицу t1 по полю s_id, если в обеих таблицах нет совпадающих s_id, то соединять по t1.s_id is null (поля t1.s_id=null тоже может не быть).
Я придумал два решения, но они оба кажутся мне неоптимальными. Есть ли вариант как сделать лучше

Первый вариант:
with t1 as (select null s_id, 'msg0' msg  from dual union all 
            select 1 s_id, 'msg1' msg  from dual union all 
            select 2 s_id, 'msg2' msg  from dual union all 
            select 3 s_id, 'msg3' msg  from dual ),
     t2 as (select 1 s_id, 'target' t from dual)
select t2.s_id, t2.t, case when t1a.s_id is  null then  t1b.msg else  t1a.msg end msg
      from t2
      left join    t1 t1b on t1b.s_id is null
      left join    t1 t1a on t1a.s_id = t2.s_id 
Этот вариант мне не нравится по двум причинам:
1. Нельзя сделать inner join - не полностью выполнены условия задачи
2. Одна и та-же таблица присоединяется два раза

Второй вариант:
with t1 as (select null s_id, 'msg0' msg  from dual union all 
            select 1 s_id, 'msg1' msg  from dual union all 
            select 2 s_id, 'msg2' msg  from dual union all 
            select 3 s_id, 'msg3' msg  from dual ),
     t2 as (select 1 s_id, 'target' t from dual)
select * from t2 inner join t1 on (not exists (select * from t1 where t1.s_id=t2.s_id) and t1.s_id is null) or t1.s_id=t2.s_id
В этом случае мне не нравится дополнительный подзапрос, хотя подозреваю что без него невозможно реализовать задачу

* Это условный пример, в действительности все сложенее. Именно поэтому не подходит вариант с подзапросами в селекте

Oracle 10gR2
orawish
Дата: 11.11.2008 13:21:50
я бы соединял таблицы по каждому из возможных кандитат-критериев (a.key=b.key or b.key is null) ну и keep - ом отсекал по каждой партиции менее приоритетное
Elic
Дата: 11.11.2008 13:26:44
with t1_inner_join_t2 as (...) 
select ... from t1_inner_join_t2
union all
<cartesian join (t1.s_id is null)*t2> where not exists(select null from t1_inner_join_t2)
Gleb Reutov
Дата: 11.11.2008 13:52:49
2orawish К своему стыду я не знал про keep.
Насколько я понял, в частном случае, когда в t2 только одна строка, keep эквивалентен такому запросу
with t1 as (select null s_id, 'msg0' msg  from dual union all 
            select 1 s_id, 'msg1' msg  from dual union all 
            select 2 s_id, 'msg2' msg  from dual union all 
            select 3 s_id, 'msg3' msg  from dual ),
     t2 as (select 1 s_id, 'target' t from dual)
select * from (
  select * from t2 inner join t1 on t1.s_id=t2.s_id or t1.s_id is null order by t1.s_id+t2.s_id)
where rownum=1;
Gleb Reutov
Дата: 11.11.2008 14:05:48
Elic - твоя идея состоит в том чтобы соеденить все со всем, а потом отфильтровать? Буду признателен если ты опишешь ее поподробнее.