如果我有两个这样的表:
CREATE TABLE #table1 (id INT, name VARCHAR(10))
INSERT INTO #table1 VALUES (1,'John')
INSERT INTO #table1 VALUES (2,'Alan')
INSERT INTO #table1 VALUES (3,'Dave')
INSERT INTO #table1 VALUES (4,'Fred')
CREATE TABLE #table2 (id INT, name VARCHAR(10))
INSERT INTO #table2 VALUES (1,'John')
INSERT INTO #table2 VALUES (3,'Dave')
INSERT INTO #table2 VALUES (5,'Steve')
我希望看到所有行只出现在其中一个表中,最好的方法是什么?
我能想到的就是:
SELECT * from #table1 except SELECT * FROM #table2
UNION
SELECT * from #table2 except SELECT * FROM #table1
或者类似的东西:
SELECT id,MAX(name) as name FROM
(
SELECT *,1 as count from #table1 UNION ALL
SELECT *,1 as count from #table2
) data
group by id
HAVING SUM(count) =1
在这种情况下会回归艾伦,弗雷德和史蒂夫。
但是这些感觉真的很笨重 - 有没有更有效的方法来解决这个问题?
答案 0 :(得分:6)
select coalesce(t1.id, t2.id) id,
coalesce(t1.name, t2.name) name
from #table1 t1
full outer join #table2 t2
on t1.id = t2.id
where t1.id is null
or t2.id is null
完整的外部联接保证了联接两侧的记录。无论双方都没有的记录(你正在寻找的记录)都会在一侧或其他方面有NULL
。这就是我们过滤NULL
的原因。
COALESCE
可以保证显示非NULL
值。
最后,值得强调的是ID会检测到重复。如果您希望它也是名称,则应将name
添加到JOIN
。如果您只想按名称加入,请仅按name
加入。此解决方案(使用JOIN
)为您提供了灵活性。
顺便说一句,既然您提供了CREATE
和INSERT
代码,我实际运行了它们,上面的代码就是一个完全正常运行的代码。
答案 1 :(得分:2)
您可以使用EXCEPT
和INTERSECT
:
-- All rows
SELECT * FROM #table1
UNION
SELECT * FROM #table2
EXCEPT -- except
(
-- those in both tables
SELECT * FROM #table1
INTERSECT
SELECT * FROM #table2
)
不确定这是否比您的EXCEPT
和UNION
示例...
答案 2 :(得分:1)
select id, name
from
(select *, count(*) over(partition by checksum(*)) as cc
from (select *
from #table1
union all
select *
from #table2
) as T
) as T
where cc = 1