【问题标题】:What's the best way to select data only appearing in one of two tables?选择仅出现在两个表之一中的数据的最佳方法是什么?
【发布时间】:2011-10-20 21:16:18
【问题描述】:

如果我有两个这样的表:

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

在这种情况下,这将返回 Alan、Fred 和 Steve。

但是这些感觉真的很笨重 - 有没有更有效的方法来解决这个问题?

【问题讨论】:

    标签: sql sql-server sql-server-2005 select


    【解决方案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
    

    【讨论】:

      【解决方案2】:
      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)为您提供了这种灵活性。

      顺便说一句,由于您提供了 CREATEINSERT 代码,我实际运行了它们,上面的代码是一个完整的工作代码。

      【讨论】:

      • 感谢您的解释 - 这会在更大的表/多列上产生可伸缩性问题吗?我的实际表将有大约 2 到 1000 万行,每行最多大约 15 列左右,我认为交叉连接会使事情变得有点慢?
      • 如果您在join 字段上有索引,那么与使用EXCEPTINTERSECT 相比,您的性能会好很多
      • 如果我要连接多个列,我需要一个覆盖所有连接列的索引,还是可以有很多单列索引?
      • @Adrian:难怪。连接通常在几个列上完成,而 UNION 等人。检查所有列。
      • 好吧,我不是“索引专家”,但我相信您应该从所有连接字段的索引开始。既然您手头有数据,那么您这里就有一位出色的老师:反复试验。实验。在那里学到了很多东西!
      【解决方案3】:

      您可以使用EXCEPTINTERSECT

      -- All rows
      SELECT * FROM #table1 
      UNION
      SELECT * FROM #table2
      EXCEPT -- except
      (
        -- those in both tables
        SELECT * FROM #table1 
        INTERSECT
        SELECT * FROM #table2
      )
      

      不确定这是否比您的 EXCEPTUNION 示例更好...

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-02-08
        • 1970-01-01
        • 1970-01-01
        • 2017-02-04
        • 1970-01-01
        • 2020-06-12
        • 1970-01-01
        相关资源
        最近更新 更多