【问题标题】:SQL OUTER JOIN: "partial NULL"-rows wantedSQL OUTER JOIN:需要“部分 NULL”-行
【发布时间】:2011-09-13 16:54:26
【问题描述】:

我正在寻找一种方法来归档以下内容:

想象一下表 A、B:

答:

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

乙:

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

现在看看下面的 SQL 语句和结果(我在 Oracle 11 上,但对于 MSSQL 应该是一样的):

从 A LEFT OUTER JOIN B ON (A.aID = B.bID) 中选择 A.*、B.*

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 中选择 A.*、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 LEFT OUTER JOIN B ON 中选择 A.*、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, 20  , NULL    (note 20)
3  , 30  , 'ghi' , 3   , NULL, NULL    (note 3)
4  , 40  , 'jkl' , NULL, NULL, NULL

有没有办法让这种行为(保持匹配部分,NULL 不匹配“ON”子句和所有值列的部分)只使用连接而不是一遍又一遍地使用自连接?

如果没有像“KEEP MATCHING COLS”这样的密钥世界,你会建议什么方式? 子选择?自加入?

谢谢, 布拉玛

【问题讨论】:

  • 由于我对 Stackoverflow 比较陌生,我不知道更好的问题规范属于哪里。我把它放在我的帖子的答案中。谢谢布拉玛

标签: sql database oracle left-join outer-join


【解决方案1】:

在 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

【讨论】:

    【解决方案2】:

    我认为您不会找到一个简单的解决方案,这里有一些适用于您的数据集的方法,但效果不佳!

    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
    

    不是完全自我加入,但没有更好,我有兴趣看到更好的解决方案并了解需求。

    【讨论】:

    • 如果 Id 或 Id2 不匹配,则不将 bValue 返回为 null。请参阅预期结果的第 2 行和第 3 行。我同意,要求是不同的,如果有更多的上下文会很好。
    【解决方案3】:

    不知道为什么你不能使用/不想要自我加入,但这里有一个版本:

    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
    

    【讨论】:

      【解决方案4】:

      为了使这更容易编写(并因此维护),我建议您避免外部连接,而是联合您需要的四个子集,例如

      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 值(外连接明确设计为生成)轻松替换为实际的默认值。

      【讨论】:

        猜你喜欢
        • 2018-01-21
        • 2014-08-10
        • 2015-07-31
        • 1970-01-01
        • 1970-01-01
        • 2011-07-06
        • 2018-12-04
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多