Oracle: Fast NOT IN для нескольких столбцов - PullRequest
7 голосов
/ 03 декабря 2010

Мне нужно синхронизировать две таблицы.Предположим, что таблицы содержат следующие столбцы:

Table1: A, B, C, D  
Table2: A, B, C, E

Мне нужно найти такие строки в Таблице 1, что в Таблице 2 нет записи с соответствующими значениями (A, B, C), а затем вычислить E как F (D) иобновить таблицу2.

Если мне нужно сопоставить, например, только A, я бы написал следующий запрос:

SELECT * FROM Table1 WHERE A NOT IN (SELECT A FROM Table2)

Многостолбцовый аналог кажется слишком медленным:

SELECT * FROM Table1 WHERE A NOT IN (SELECT A FROM Table2)
                       AND B NOT IN (SELECT B FROM Table2)
                       AND C NOT IN (SELECT C FROM Table2)

Как лучше написать такой запрос?

Ответы [ 6 ]

20 голосов
/ 03 декабря 2010

Если (a, b, c) НЕ равны NULL в обеих таблицах, то как NOT IN, так и NOT EXISTS, скорее всего (в попытках, которые я пробовал), сгенерируют один и тот же план выполнения., b, c) объявлены как nullable, но вы знаете , что столбцы на самом деле не равны NULL, вы можете обмануть оптимизатор, чтобы в любом случае выполнить хэш-анти-объединение, добавив «И a не является нулевымне является нулевым И c не является нулевым "на ваш запрос.(Возможно, вам также потребуется добавить подсказку / * + HASH_AJ * / в подзапросе.)

Кроме того, следующие запросы НЕ идентичны:

 from table1
where (a,b,c) not in (select a,b,c from table2)

 from table1
where a not in(select a from table2)
  and b not in(select b from table2)
  and c not in(select c from table2)
6 голосов
/ 03 декабря 2010
   SELECT * FROM Table1 
   WHERE (A, B, C) NOT IN 
     (SELECT A,B,C FROM Table2)
0 голосов
/ 24 октября 2018

по причинам производительности никогда не используйте

NOT IN (SELECT

используйте

NOT EXISTS   (SELECT 1 FROM
0 голосов
/ 13 октября 2014

Небольшое дополнение: Я обнаружил, что Oracle (11gR1 в моем случае) отказывается хэшировать анти-объединение, когда предложение NOT IN содержит более одного столбца, например,

SELECT * FROM Table1 WHERE (A,B,C) NOT IN (
    SELECT /*+ HASH_AJ */ A,B,C FROM Table2
        WHERE A IS NOT NULL AND B IS NOT NULL AND C IS NOT NULL
)

и это даже при добавлении одной из подсказок (то же самое с UNNEST) и условий, отличных от NULL. Работает только с одним столбцом.

0 голосов
/ 03 декабря 2010

Вы можете попробовать

SELECT * FROM Table1 
WHERE 
not exists (
  SELECT 1 FROM Table2 
  where Table2.a=Table1.a 
  and Table2.b=Table1.b 
  and Table2.c=Table1.c );

как отправлено guigui42.Он делает хеш-соединение анти-и избегает фильтра.

ИЛИ try

select t1.*
from table1 t1, table2 t2
where t1.a = t2.a(+)
and t1.b = t2.b(+)
and t1.c = t2.c(+)
and (t2.a is null or t2.b is null or t2.c is null);

Это делает внешнее соединение + фильтр.И то, и другое должно быть намного быстрее, чем делать NOT IN.

0 голосов
/ 03 декабря 2010
SELECT * FROM Table1 
WHERE 
not exist (
  SELECT 1 FROM Table2 
  where Table2.a=Table1.a 
  and Table2.b=Table1.b 
  and Table2.c=Table1.c )

РЕДАКТИРОВАТЬ: обратите внимание, что Not существует и NOT IN не полностью идентичны в некоторых случаях (значения NULL) см .: http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::p11_question_id:442029737684

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...