Как лучше всего выбирать данные, отображаемые только в одной из двух таблиц? - PullRequest
5 голосов
/ 05 августа 2011

Если у меня есть две таблицы, такие как эта:

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

Что вернет Алану, Фреду и Стиву в этом случае.

Но они кажутся действительно неуклюжими - есть ли более эффективный способ приблизиться к этому?

Ответы [ 3 ]

6 голосов
/ 05 августа 2011
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.

Наконец, стоит подчеркнуть, что повторы обнаруживаются по идентификатору.Если вы хотите, чтобы оно также было по имени, вы должны добавить name к JOIN.Если вы хотите быть только по имени, присоединяйтесь только к name.Это решение (с использованием JOIN) дает вам такую ​​гибкость.

Кстати, поскольку вы предоставили код CREATE и INSERT, я фактически выполнил их, а код выше - полностью рабочий код.*

2 голосов
/ 05 августа 2011

Вы можете использовать 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 пример ...

1 голос
/ 05 августа 2011
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
...