Напротив INTERSECT в Oracle

У меня есть два выбора, и я хочу объединить их таким образом, чтобы возвращались только строки, уникальные в обоих select. Есть ли встроенный способ в Oracle 10g для этого?

Я знаю, что могу сделать что-то вроде этого:

(select1 UNION select2)
MINUS
(select1 INTERSECT select2)

но я хотел бы избежать этого. Оба select1 и select2 имеют 20 строк, поэтому этот способ будет действительно неясным и сложным в обслуживании.

Ответ 1

Если оба select1 и select2 не возвращают дубликатов, вы можете использовать что-то вроде этого:

SELECT * FROM (select1 UNION ALL select2) a
GROUP BY a.col1, a.col2, ...
HAVING count(*) = 1

Ответ 2

В Oracle 10g у вас есть общие табличные выражения в вашем распоряжении.

WITH
  select_1 AS (
    SELECT *
    FROM your_table
    WHERE your_condition = 1
  ),
  select_2 AS (
    SELECT *
    FROM your_other_table
    WHERE your_other_condition = 1
  )
SELECT * FROM select_1
UNION
SELECT * FROM select_2
MINUS
(
  SELECT * FROM select_1
  INTERSECT
  SELECT * FROM select_2
);

Это позволяет поддерживать подзапросы, а цель вашего окончательного запроса понятна.

Конечно, если Oracle добавит оператор SYM_DIFFERENCE к SQL, будет еще лучше, но я не задерживаю дыхание — они все еще не уверены, что тип данных BOOLEAN будет хорошей идеей.

Ответ 3

Вот еще одна идея:

  • Сделайте полное внешнее соединение select1 и select2
  • Используйте только те записи с select1.id = NULL (запись только в select2) или select2.ID = NULL (запись только в select1)

вот так:

SELECT *
FROM select1 FULL OUTER JOIN select2 on select1.id = select2.id
WHERE select1.id is null or select2.id is null

Ответ 4

Это сработало для меня - не уверен, насколько это быстро.

(select table_name from dba_tables where user = 'X'
union
select table_name from dba_tables where user = 'Y')
minus
(select table_name from dba_tables where user = 'X'
intersect
select table_name from dba_tables where user = 'Y')

Ответ 5

-- get not intersect data
SELECT_FINAL
WHERE FIELD_PK IS NOT IN(
    -- get ids of intersect
    SELECT_AUX FIELD_PK1 FROM (
        SELECT1
        INTERSECT
        SELECT2
     )
)

Я делаю это

Ответ 6

Здесь другое решение, на этот раз использующее аналитику count() (Oracle 10 или новее).

Преимущества:

  • мы можем указать, в каких столбцах указывать EXTRASECT (например, KK1, KK2 в примере).
  • мы можем выбрать несимвольные столбцы (например, NK1, NK2... в примере), которые нам не требуются.
  • эффективный план.
  • Подобно примеру FULL OUTER JOIN, но мы не получаем ключевые столбцы как отдельные поля, требующие декодирования или дела, чтобы свернуть их вместе.

select KK1, KK2, NK1, NK2 from ( select KK1, KK2, NK1, NK2, count( * ) over( partition by KK1, KK2 ) cnt from ( select KK1, KK2, NK1, NK2 from X union all select KK1, KK2, NK1, NK2 from Y ) ) where cnt = 1;