B中的不匹配行与A中的某些行连接

时间:2017-12-06 17:42:02

标签: sql sql-server join

示例数据:

Table1     Table2
x | y      x | y  
-----      -----
1 | A      1 | A
1 | B      1 | D
1 | C      2 | M
1 | null   2 | N
2 | M      2 | M
2 | N      1 | A

我想计算Table2中x的数量并将其与Table1连接。但在表2中,我可以得到与表1中的y不匹配的y。在这种情况下,我想在Table1的y列中使用null与row连接。 在示例中,Table2.1-D应与Tabl1.1-null

连接

对于给定的例子,我期待结果:

x | y    | count
-----------------     
1 | A    | 2
1 | B    | 0
1 | C    | 0
1 | null | 1 (because D doesn't match to anything else in Table1)
2 | M    | 2
2 | N    | 1

7 个答案:

答案 0 :(得分:2)

我无法想到一种非常优雅的方法来实现这一目标。以下是一些蛮力,但它完成了工作:

select t1.x, t1.y,
       (case when t1.y is null then unmatched.cnt else matched.cnt end) as cnt
from table1 t1 outer apply
     (select count(*) as cnt
      from table2 t2
      where t2.y = t1.y
     ) matched cross join
     (select count(*) as cnt
      from table2 t2
      where not exists (select 1 from table1 t1 where t1.y = t2.y)
    ) unmatched;

答案 1 :(得分:0)

有多种方法可以解决这个问题,最好的方法取决于数据,行数和最终的性能。

其中一种方法是对null以外的所有内容进行操作,然后Table1使用UNION行进行操作。

对于您的示例,我假设nullxyTable1值的组合始终是唯一的。如果不是,你需要额外的分组。

Table2

答案 2 :(得分:0)

这是我提出的最好的:

SELECT t1.x
, t1.y 
, [count] = ISNULL(t2.cnt,0)
FROM #Table1 t1
LEFT JOIN (SELECT x
            , y
            , cnt = COUNT(*) 
            FROM #Table2
            GROUP BY x, y
        ) t2 
ON (t1.x = t2.x 
        AND t1.y = t2.y 
        AND t1.y IS NOT NULL
    ) 
    OR (t1.x = t2.x 
        AND ISNULL(t1.y,t2.y) = t2.y 
        AND NOT EXISTS (SELECT 1 FROM #Table1 WHERE y = t2.y)
    )

答案 3 :(得分:0)

这是一个解决方案。看看这是否有帮助!

WITH v_t2 AS
(SELECT y, COUNT(*) AS cnt
   FROM table2 t2 
  GROUP BY t2.y
),
v_t2_null AS
(SELECT COUNT(*) AS cnt
   FROM table2 t2
  WHERE NOT EXISTS (SELECT 1 FROM table1 t1 WHERE t1.y= t2.y)
)
SELECT t1.x. t1.y, COALESCE(t2.cnt, v_t2_null.cnt)
  FROM table1 t1 LEFT JOIN v_t2 t2
                   ON (t1.y = t2.y)
                 JOIN v_t2_null;

输出

    x   y   cnt
    1   A   2
    1   B   0
    1   V   0
    1   NULL    1
    2   M   2
    2   N   1

答案 4 :(得分:0)

如果我的理解是正确的,那么它不仅仅是一个左连接问题,它是这样的:

-- Creating example
CREATE TABLE #T1 (x int, y char(1) null)
CREATE TABLE #T2 (x int, y char(1) null)

-- Loading tables
INSERT INTO #T1
SELECT 1,'A'  
UNION ALL SELECT 1,'B'  
UNION ALL SELECT 1,'C'  
UNION ALL SELECT 1,null 
UNION ALL SELECT 2,'M'  
UNION ALL SELECT 2,'N'  

INSERT INTO #T2
SELECT 1 ,'A'
UNION ALL SELECT 1 ,'D'
UNION ALL SELECT 2 ,'M'
UNION ALL SELECT 2 ,'N'
UNION ALL SELECT 2 ,'M'
UNION ALL SELECT 1 ,'A'


SELECT t1.x, t1.y, count(t2.y) FROM #T1 t1
LEFT JOIN #T2 t2 on t1.y = t2.y 
LEFT JOIN #T2 t22 on t1.Y IS NULL AND  t22.y IS NULL
WHERE t1.y is not null
GROUP BY t1.x, t1.y
UNION ALL
SELECT t.x, t.y, (SELECT count(1) 'count' FROM #T2 t2 
LEFT JOIN #T1 t1 on t1.y = t2.y  
WHERE t1.y IS NULL
) FROM #T1 t WHERE t.y IS NULL

我不得不将问题分成两部分。

答案 5 :(得分:0)

另一种方法我们可以通过自联接和分组连接两个表来实现这一点,如下所示

select t1.*,t2.c from Table1 t1 join
(
    select 
        a.y,
        sum(case when b.y is null then 0 else 1 end)  as c
    from Table1 a 
        full outer join 
        Table2 b 
    on 
        a.y=b.y
    group by a.y
)t2
on ISNULL(t1.y,-1)=ISNULL(t2.y,-1)

Working demo

答案 6 :(得分:0)

感谢您的所有答案。 他们非常有帮助。 这是我的解决方案,以正确的方式计算Table2中的所有行,并将其与x匹配到table1中的行,y == null。 我知道我可以在左连接中更好地使用子查询。

当然,真正的问题很复杂,问题是它的简化版本。

我发现如何改进查询,请告诉我:)

;with T2Count as
(
    select x, y, count(*) cnt from @t2 group by x, y
)
/*
select t1.x, t1.y, isnull(t2.cnt, 0)
from @T1 t1
left join T2Count t2 on t1.x = t2.x and t1.y = t2.y
where t1.y is not null
*/
select t1.x, t1.y, isnull(AllCounts.cnt, 0) cnt from @t1 t1
left join (
    select * from T2Count -- I know that here I'will take too much data 1-D, 2-Q, but those data will not natch in last join (AllCounts...)
union all
    select t2.x, null, sum(cnt) cnt
    from T2Count t2
    left join @t1 t1 on t1.x = t2.x and t1.y = t2.y
    where t1.x is null
    group by t2.x
) as AllCounts on t1.x = AllCounts.x and (t1.y = AllCounts.y or (t1.y is null and AllCounts.y is null))