SQL OUTER JOIN: "частично NULL" - хотел - PullRequest
1 голос
/ 13 сентября 2011

Я ищу способ архивировать следующее:

Представьте себе таблицы A, B:

A:

aID, aID2, avalue
=================
1  , 10  , 'abc'
2  , 20  , 'def'
3  , 30  , 'ghi'
4  , 40  , 'jkl'

B

bID, bID2, bvalue
=================
1  , 10  , 'mno'
20 , 20  , 'pqr'
3  , 1   , 'stu'

Теперь посмотрите на следующий оператор SQL и результаты (я нахожусь на Oracle 11, но должен быть таким же для MSSQL):

ВЫБРАТЬ A. *, B. * ИЗ ЛЕВОГО ВНЕШНЕГО СОЕДИНЕНИЯ B ВКЛЮЧЕНО (A.aID = B.bID)

aID, aID2, avalue, bID , bID2, bvalue
=====================================
1  , 10  , 'abc' , 1   , 10  , 'mno'  
2  , 20  , 'def' , NULL, NULL, NULL
3  , 30  , 'ghi' , 3   , 1   , 'stu'  
4  , 40  , 'jkl' , NULL, NULL, NULL

ВЫБРАТЬ A. *, B. * ИЗ ЛЕВОГО ВНЕШНЕГО СОЕДИНЕНИЯ B ВКЛЮЧИТЬ (A.aID = B.bID И A.aID2 = B.bID2)

aID, aID2, avalue, bID , bID2, bvalue
=====================================
1  , 10  , 'abc' , 1   , 10  , 'mno'  
2  , 20  , 'def' , NULL, NULL, NULL
3  , 30  , 'ghi' , NULL, NULL, NULL
4  , 40  , 'jkl' , NULL, NULL, NULL

Пока хорошо.

Я ищу заявление (настолько простое, насколько это возможно), которое дает мне следующее:

MADE-UP-CODE: ВЫБЕРИТЕ A. *, B. * ИЗ ЛЕВОГО ВНЕШНЕГО СОЕДИНЕНИЯ B ВКЛЮЧЕНО (A.aID = B.bID И A.aID2 = B.bID2 ПРОДОЛЖАЙТЕ СОХРАНИТЬ СОБЫТИЯ COLS)

aID, aID2, avalue, bID , bID2, bvalue
=====================================
1  , 10  , 'abc' , 1   , 10  , 'mno'  
2  , 20  , 'def' , NULL, 20  , NULL    (note 20)
3  , 30  , 'ghi' , 3   , NULL, NULL    (note 3)
4  , 40  , 'jkl' , NULL, NULL, NULL

Есть ли способ получить такое поведение (сохранить совпадающие части, NULL не совпадают части предложения "ON" и все столбцы значений), используя только объединения, не используя самосоединения снова и снова?

Как бы вы предложили, если бы не существовал ключевой мир, такой как "KEEP MATCHING COLS"? Подзапрос? Selfjoins

Спасибо, Blama

Ответы [ 4 ]

2 голосов
/ 13 сентября 2011

Присоединиться к Id или Id2, а затем выборочно обнулять результаты в предложении select.

Настройка тестовых таблиц и данных:

set null 'NULL'
create table a (aId number
    , aId2 number
    , aValue varchar2(4));
insert into a values (1, 10, 'abc');
insert into a values (2, 20, 'def');
insert into a values (3, 30, 'ghi');
insert into a values (4, 40, 'jkl');
create table b (bId number
    , bId2 number
    , bValue varchar2(4));
insert into b values (1, 10, 'mno');
insert into b values (20, 20, 'pqr');
insert into b values (3, 1, 'stu');
commit;

Запрос:

select A.*
    , case when A.aId = B.bId then B.bId end as bId
    , case when A.aId2 = B.bID2 then B.bId2 end as bId2
    , case when A.aId = B.bId 
        and A.aId2 = B.bId2 then bValue end as bValue
from A
left outer join B on A.aID = B.bId or A.aId2 = B.bId2;

Результаты:

       AID       AID2 AVAL        BID       BID2 BVAL
---------- ---------- ---- ---------- ---------- ----
         1         10 abc           1         10 mno
         2         20 def  NULL               20 NULL
         3         30 ghi           3 NULL       NULL
         4         40 jkl  NULL       NULL       NULL
0 голосов
/ 14 сентября 2011

Чтобы было легче писать (и, следовательно, поддерживать), я предлагаю вам избегать внешнего объединения и вместо этого объединять четыре требуемых подмножества, например,

SELECT A.*, B.* FROM A INNER JOIN B ON (A.aID = B.bID AND A.aID2 = B.bID2)
UNION
SELECT A.*, NULL, NULL, NULL
  FROM A 
 WHERE NOT EXISTS (
                   SELECT * 
                     FROM B 
                    WHERE (A.aID = B.bID)
                  )
       AND NOT EXISTS (
                       SELECT * 
                         FROM B 
                        WHERE (A.aID2 = B.bID2)
                      )
UNION
SELECT A.*, B.bID, NULL, NULL
  FROM A INNER JOIN B ON (A.aID = B.bID)
       AND NOT EXISTS (
                       SELECT * 
                         FROM B 
                        WHERE (A.aID2 = B.bID2)
                      )
UNION
SELECT A.*, NULL, B.bID2, NULL
  FROM A INNER JOIN B ON (A.aID2 = B.bID2)
 WHERE NOT EXISTS (
                   SELECT * 
                     FROM B 
                    WHERE (A.aID = B.bID)
                  );

Преимущество этого подхода заключается в том, что используются реляционные операторыобъединение, полуразница и объединение, позволяющее легко заменить эти нереляционные значения NULL (которые специально сгенерировано для внешнего объединения) фактическими значениями по умолчанию.

0 голосов
/ 13 сентября 2011

Не знаю, почему вы не можете использовать / не хотите самостоятельных объединений, но вот версия:

SELECT  a.aID,
    a.aID2,
    a.avalue,
    b1.bID,
    b2.bID2,
    CASE WHEN b1.bID = b2.bID AND b1.bID2 = b2.bID2 THEN b1.bvalue ELSE NULL END as bvalue
FROM A  a
LEFT OUTER JOIN B b1
    ON (a.aID = b1.bID) 
LEFT OUTER JOIN B b2
    ON (a.aID2 = b2.bID2)

Результаты:

aID aID2    avalue    bID     bID2     bvalue
1   10      abc         1      10       mno       
2   20      def         NULL   20       NULL
3   30      ghi         3      NULL     NULL
4   40      jkl         NULL   NULL     NULL
0 голосов
/ 13 сентября 2011

Я не думаю, что вы найдете простое решение для этого, вот что работает с вашим набором данных, но не красиво и не эффективно!

create table A ( aID int, aID2 int, avalue char(3) )
create table B ( bID int, bID2 int, bvalue char(3) )

insert into A VALUES (1  , 10  , 'abc')
insert into A VALUES (2  , 20  , 'def')
insert into A VALUES (3  , 30  , 'ghi')
insert into A VALUES (4  , 40  , 'jkl')


insert into B VALUES (1  , 10  , 'mno')
insert into B VALUES (20 , 20  , 'pqr')
insert into B VALUES (3  , 1   , 'stu')

select distinct
    A.*,
    COALESCE(B1.bID,B2.bID) as bID,
    COALESCE(B1.bID2,B3.bID2) as BID2,
    B1.bvalue
from A
left outer join 
    B B1
on 
    A.aID = B1.bID 
AND 
    A.aID2 = B1.bID2
left outer join 
    B B2
on 
    A.aID = B2.bID 
left outer join 
    B B3
on 
    A.aID2 = B3.bID2


aID, aID2, avalue, bID , bID2, bvalue
=====================================
1  , 10  , 'abc' , 1   , 10  , 'mno'  
2  , 20  , 'def' , NULL, 20  , NULL
3  , 30  , 'ghi' , 3   , NULL, NULL
4  , 40  , 'jkl' , NULL, NULL, NULL

Не совсем сам присоединяется, но не лучше, мне было бы интересно найти лучшее решение и понять требования.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...