2017-02-10 3 views
1

я недавно задавал этот вопрос и был немного озадачен, поэтому я хочу спросить экспертов ...SQL заявление для возврата записей без пересечения

Даны два таблиц А & B, я хочу, чтобы вернуть все значения от A и B, которые не перекрываются. Подумайте о двух перекрывающихся кругах; как мы можем вернуть все данные, которые НЕ находятся в перекрывающейся центральной части? И мне пришлось использовать ANSI Standard SQL, а не синтаксис Oracle.

Предположим, что мы хотим все исключительно как А, так & B, мой ответ был

select * 
    from A 
    cross join B 
    minus 
    (select a.common_column from a 
    intersect 
    select b.common_column) 

ли это выглядеть правильно, или даже близко? Если это правильно, есть ли более эффективный способ сделать это?

BTW - мое решение было обоснованно отклонен ....

Спасибо!

+0

Идет близко. Я бы пошел с «union all» вместо перекрестного соединения для внешнего запроса. –

+0

Почему вы выполняете крест? Это создаст декартово произведение A и B.Предполагая, что A и B содержат более одного столбца, этот запрос не будет компилироваться (поскольку перекрестное соединение B возвращает столбцы NxM, тогда как вторая часть возвращает 1 столбец). –

+0

Да, вы не хотите «перекрестное присоединение» .... Либо выполните «union all», либо «except» на 'intersect', или просто сделайте' union all' on 'select col from A кроме select col from B' и 'select col from B, кроме select col from A' –

ответ

1

Может быть полное внешнее соединение?

select coalesce(A.col, B.col) 
from A full outer join B on A.col = B.col 
where A.col is null or B.col is null; 
0

Для вычисления разницы множество симметричную, вы можете использовать комбинацию MINUS и UNION ALL:

select * from (
    (select * from A 
    minus 
    select * from B) 
    union all 
    (select * from B 
    minus 
    select * from A) 
) 
+0

Логически это правильно. Но полное внешнее соединение более эффективно - т.е. меньше проходит через таблицу. – BobC

+0

@BobC Обычно я запускаю это как специальный запрос, поэтому я не слишком беспокоюсь о производительности. И я нахожу это решение более понятным и быстрым, чем полное внешнее соединение, но YMMV. –

+0

Я очень забочусь о производительности (это моя работа) :) Итак, я просто хочу указать на эти вещи :) – BobC

0

Вам нужно будет выбрать все данные из обеих таблиц, кроме случаев, когда они перекрывают друг друга, а затем объединить данные с объединением. Предоставленный код должен работать для вашего примера.

SELECT * 
FROM 
    (
    SELECT * FROM Table1 
    EXCEPT SELECT * FROM Table2 
) 
UNION 
SELECT * 
FROM 
    (
    SELECT * FROM Table2 
    EXCEPT SELECT * FROM Table1 
) 

Надеюсь, это поможет.

+1

Поскольку подзапросы будут создавать непересекающиеся наборы кортежей, 'union all' вернет одинаковый набор результатов и может привести к повышению производительности. –

+0

Вы, вероятно, захотите использовать 'select *' в своем втором запросе - иначе это вернет ошибку синтаксического анализа, если только таблица1 и таблица2 не содержат только один столбец 'item'. –

+0

Я даже не знал, что у Oracle есть функция EXCEPT. Что-то для меня, чтобы учиться. Благодаря! Упс - похоже, что MINUS работает в Oracle, а EXCEPT работает в других системах. – CarCrazyBen

0

Ваш запрос был отклонен, поскольку он синтаксически неверен: количество столбцов различается, и оно смущает cross join и union all. Однако, я думаю, у вас есть правильная идея для решения этого.

Вы можете легко исправить:

(select * 
from A 
union all 
select * 
from B 
) minus 
(select * 
from A 
intersect 
select * 
from B 
); 

То есть, объединить все, используя union all, а затем вычесть те строки, которые происходят в обеих таблицах.

Конечно, если есть один идентификатор, то вы можете использовать идентификатор с join и другими операциями.

+0

Это решение, к которому я стремился ... Я сделал это, используя Cross Join вместо Union All. Спасибо! – CarCrazyBen

0

Так же, как ответил в то же время Фрэнк Schmitt:

Вот она, включая пример данных:

WITH 
table_a(name) AS (
      SELECT 'From_A_1' 
UNION ALL SELECT 'From_A_2' 
UNION ALL SELECT 'From_A_3' 
UNION ALL SELECT 'From_A_4' 
UNION ALL SELECT 'From_A_5' 
UNION ALL SELECT 'From_BOTH_6' 
UNION ALL SELECT 'From_BOTH_7' 
UNION ALL SELECT 'From_BOTH_8' 
) 
, 
table_b(name) AS (
      SELECT 'From_B_1' 
UNION ALL SELECT 'From_B_2' 
UNION ALL SELECT 'From_B_3' 
UNION ALL SELECT 'From_B_4' 
UNION ALL SELECT 'From_B_5' 
UNION ALL SELECT 'From_BOTH_6' 
UNION ALL SELECT 'From_BOTH_7' 
UNION ALL SELECT 'From_BOTH_8' 
) 
(SELECT * FROM table_a EXCEPT SELECT * FROM table_b) 
UNION ALL 
(SELECT * FROM table_b EXCEPT SELECT * FROM table_a) 
ORDER BY name 
; 

name 
From_A_1 
From_A_2 
From_A_3 
From_A_4 
From_A_5 
From_B_1 
From_B_2 
From_B_3 
From_B_4 
From_B_5 
1

Предоставлены таблицы A и B, вы ищете (АУБ) - (A & Б). Другими словами, вам нужно объединение B минус их пересечение. Помните, что A и B должны быть совместимы с профсоюзом, чтобы этот запрос работал. Я бы сделал:

(select * from A 
union 
select * from B 
) 
minus 
(select * from A 
    intersect 
select * from B 
)