SQL联接数据并获取与NULL不匹配的行

时间:2019-09-04 08:42:40

标签: sql sql-server tsql

我有两个要联接的表,如下所示:

表1

 Code1 | Code2 |  Date(1)   | Amount(1)     
   A   |   AA  | 201802     |  100         
   A   |   AA  | 201803     |   50     
   A   |   AA  | 201804     |   30   

表2

 Code1 | Code2 |  Date(2)  | Amount(2)          
   A   |   AA  | 201801    |   20      
   A   |   AA  | 201802    |   10      
   A   |   AA  | 201803    |   10 

我希望结果表看起来像这样:

结果

 Code1 |  Code2 |  Date(1)   |   Date(2) |  Amount(1) | Amount(2)       
   A   |   AA   |    NULL    |    201801 |    NULL    |   20      
   A   |   AA   |   201802   |    201802 |    100     |   10      
   A   |   AA   |   201803   |    201803 |    50      |   10      
   A   |   AA   |   201804   |    NULL   |    30      |   NULL      

所以我需要加入这两个表 on table1.Code1 = table2.Code1 AND table1.Code2 = table2.Code2 AND table1.Date(1) = table2.Date(2)

但是我还希望日期与null不匹配的行是与不匹配表相关的列(例如,在我的示例中为Date(1) = 201804的行)。

我尝试用左,右和外联接来连接这两个表,但是我仍然无法成功获得带有空值的行(可能是因为该特定缺失行不存在Code1和Code2)

也许交叉申请可能有效,但是我不确定如何执行。

我想要一种最有效的性能方法,因为这是包含大量数据和大量计算的大型查询的一部分。

更新: 我使用的代码是:

Select table1.Code 1, table1.Code2, Table1.Date(1), table2.Date(2), table1.Amount(1), table2.amount(2)
FROM Table1
Full Outer Join 
table2 ON 
    table1.Code1 = table2.Code1 
    AND table1.Code2 = table2.Code2 
    AND table1.date(1) = table2.date(2) 

哪个给我以下结果:

 Code1 |  Code2 |  Date(1)   |   Date(2) |  Amount(1) | Amount(2)             
   A   |   AA   |   201802   |    201802 |    100     |   10      
   A   |   AA   |   201803   |    201803 |    50      |   10        

缺少这两行:
 A | AA | NULL | 201801 | NULL | 20
A | AA | 201804 | NULL | 30 | NULL

3 个答案:

答案 0 :(得分:1)

您可以尝试这个。

--sample dataset
DECLARE @tab1 as table (
    Code1 varchar(10),
    Code2 varchar(10),
    Date1 int,
    Amount1 int )
insert into @tab1
values
    ('A', 'AA', 201802, 100),
    ('A', 'AA', 201803, 50),
    ('A', 'AA', 201804, 30),
    ('B', 'AA', 201802, 100) --additional

DECLARE @tab2 as table (
    Code1 varchar(10),
    Code2 varchar(10),
    Date2 int,
    Amount2 int )
insert into @tab2
values
    ('A', 'AA', 201802, 100),
    ('A', 'AA', 201803, 50),
    ('A', 'AA', 201801, 30)

查询

SELECT *
FROM (
    select
        coalesce(table1.Code1,table2.Code1) as Code1,
        coalesce(table1.Code2,table2.Code2) as Code2,
        table1.Date1,
        table2.Date2,
        table1.Amount1,
        table2.amount2
    FROM @tab1 as Table1
        Full Outer Join @tab2 as table2 ON 
            table1.Code1 = table2.Code1
            AND table1.Code2 = table2.Code2
            AND table1.date1= table2.date2
    ) as t1    
     CROSS APPLY ( --to exclude records not matched by "Code 1 and Code 2"
                    SELECT top 1
                        Code1
                    FROM @tab2 as t
                    where t.Code1 = t1.Code1 
                    and t.Code2 = t1.Code2
                ) as c
ORDER BY   t1.Date1

或类似这样:

select
    coalesce(table1.Code1,table2.Code1) as Code1,
    coalesce(table1.Code2,table2.Code2) as Code2,
    table1.Date1,
    table2.Date2,
    table1.Amount1,
    table2.amount2
FROM @tab1 as Table1
    Full Outer Join @tab2 as table2 ON 
            table1.Code1 = table2.Code1
        AND table1.Code2 = table2.Code2
        AND table1.date1= table2.date2
where exists (select null --to exclude records not matched by "Code 1 and Code 2"
              from @tab2 as t2
              where coalesce(table1.Code1,table2.Code1) = t2.Code1
                    and coalesce(table1.Code2,table2.Code2) = t2.Code2)
ORDER BY   table1.Date1

enter image description here

答案 1 :(得分:0)

如果您将CodeX列设为ISNULL,则更新的查询应该可以使用。

declare @t1 table (Code1 varchar(4), Code2 varchar(4),  Date1 date, Amount1 int)
declare @t2 table (Code1 varchar(4), Code2 varchar(4),  Date2 date, Amount2 int)

insert into @t1
values
   ('A', 'AA', '2018-02-01', 100 ),     
   ('A', 'AA', '2018-03-01', 50 ),     
   ('A', 'AA', '2018-04-01', 30 )

insert into @t2
values
   ('A', 'AA', '2018-01-01', 20 ),     
   ('A', 'AA', '2018-02-01', 10 ),     
   ('A', 'AA', '2018-03-01', 10 )

SELECT 
    code1   
    ,code2  
    ,date1  
    ,date2  
    ,amount1    
    ,amount2
FROM (
    SELECT code1, code2 FROM @t1
    INTERSECT
    SELECT code1, code2 FROM @t2
) t0
CROSS APPLY (
    SELECT 
        date1, date2, amount1, amount2
    FROM @t1 t1  
    FULL OUTER JOIN @t2 t2 ON t1.Code1 = t2.Code1 and t1.Code2 = t2.Code2 and date1 = date2
    WHERE 
        t0.code1 = isnull(t1.Code1, t2.code1)
    and t0.code2 = isnull(t1.Code2, t2.code2)
) tt
ORDER BY 
    date1, date2

答案 2 :(得分:0)

我建议的解决方案涉及使用code2运算符进行完全联接和另一个联接到派生表,该派生表包含两个表中存在的intersectDECLARE @T1 AS TABLE ( Code1 char(1), Code2 char(2), Date1 char(6), Amount1 int ) DECLARE @T2 AS TABLE ( Code1 char(1), Code2 char(2), Date2 char(6), Amount2 int ) INSERT INTO @T1 (Code1, Code2, Date1, Amount1) VALUES ('A', 'AA', '201802', 100) ,('A', 'AA', '201803', 50) ,('A', 'AA', '201804', 30) ,('B', 'AA', '201802', 30); -- Note: Added to the original sample data INSERT INTO @T2 (Code1, Code2, Date2, Amount2) VALUES ('A', 'AA', '201801', 20) ,('A', 'AA', '201802', 10) ,('A', 'AA', '201803', 10) ,('A', 'AB', '201802', 10); -- Note: Added to the original sample data 的所有组合。 / p>

首先,创建并填充示例数据(在您将来的问题中为我们保存此步骤):

SELECT  ISNULL(T1.Code1, T2.Code1) As Code1,
        ISNULL(T1.Code2, T2.Code2) As Code2,
        Date1, Date2, Amount1, Amount2
FROM @T1 As T1
FULL JOIN @T2 As T2
    ON T1.Code1 = T2.Code1
    AND T1.Code2 = T2.Code2
    AND T1.Date1 = T2.Date2
-- Remove this next join if you want to get rows where codes don't match
JOIN (
    SELECT Code1, Code2
    FROM @T1
    INTERSECT 
    SELECT Code1, Code2
    FROM @T2
) As CommonCodes
    ON CommonCodes.Code1 = ISNULL(T1.Code1, T2.Code1)
    AND CommonCodes.Code2 = ISNULL(T1.Code2, T2.Code2)
ORDER BY Date1

查询:

Code1   Code2   Date1   Date2   Amount1     Amount2
A       AA      NULL    201801  NULL        20
A       AA      201802  201802  100         10
A       AA      201803  201803  50          10
A       AA      201804  NULL    30          NULL

结果:

{{1}}

You can see a live demo on rextester.