Почему стоимость SQL взрывается с простым "или"? - PullRequest
7 голосов
/ 23 мая 2011

У меня есть следующее утверждение, чтобы найти однозначные имена в моих данных (~ 1 миллион записей):

select Prename, Surname from person p1 
where Prename is not null and Surname is not null 
and not exists (
   select * from person p2 where (p1.Surname = p2.Surname OR p1.Surname = p2.Altname) 
   and p2.Prename LIKE CONCAT(CONCAT('%', p1.Prename), '%') and p2.id <> p1.id
) and inv_date IS NULL

Oracle показывает огромную стоимость 1477315000, и выполнение не заканчивается через 5 минут.Простое разбиение ИЛИ на собственное существующее подраздел повышает производительность до 0,5 с и стоит до 45000:

select Prename, Surname from person p1 
where Prename is not null and Surname is not null 
and not exists (
   select * from person p2 where p1.Surname = p2.Surname and
   p2.Prename LIKE CONCAT(CONCAT('%', p1.Prename), '%') and p2.id <> p1.id
) and not exists (
   select * from person p2 where p1.Surname = p2.Altname and 
   p2.Prename LIKE CONCAT(CONCAT('%', p1.Prename), '%') and p2.id <> p1.id
) and inv_date IS NULL

Это не мой вопрос, чтобы настроить это к лучшему, так как это только редко выполняемый запрос,и я знаю, что CONTACT превосходит любой показатель, но мне просто интересно, откуда такая высокая стоимость.Оба запроса семантически эквивалентны мне.

Ответы [ 3 ]

6 голосов
/ 23 мая 2011

Ответ находится в ОБЪЯСНИТЕЛЬНОМ ПЛАНЕ для ваших запросов. Они могут быть семантически эквивалентными, но план выполнения ваших запросов значительно отличается.

EXISTS работает не так, как JOIN, и, по сути, ваш оператор OR - это то, что объединяет таблицы.

Во втором запросе не происходит JOIN, поскольку вы извлекаете записи только из одной таблицы.

2 голосов
/ 23 мая 2011

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

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

1 голос
/ 23 мая 2011

Я бы посоветовал проверить переписанный запрос, как показано ниже ... Выполните прямое соединение одного с другим по критериям, которые "квалифицируют" то, что считается совпадением ... Затем в предложении WHERE выведите его, если это не подходит к совпадению

select 
      p1.Prename, 
      p1.Surname
   from 
      person p1 
         join person p2
            on p1.ID <> p2.ID
            and (  p1.Surname = p2.Surname
                or p1.SurName = p2.AltName )
            and p2.PreName like concat( concat( '%', p1.Prename ), '%' )
   where
          p1.PreName is not null
      and p1.SurName is not null
      and p1.Inv_date is null
      and p2.id is null

Согласно вашим комментариям, но из того, что вы искали ... НЕТ, НЕ выполняйте внешнее левое соединение ... Если вы ищете имена, которые похожи на те, которые вы хотите удалить (как бы вы Я справлюсь с этим), вы хотите ПРЕКВАЛИФИЦИРОВАТЬ те записи, которые ДЕЛАЮТ МАТЧ через самосоединение (следовательно, обычное соединение). Если у вас есть имя, которое НЕ имеет похожего имени, вы, вероятно, захотите оставить его в покое ... таким образом, оно автоматически останется ВНЕ из набора результатов.

Теперь, предложение WHERE вступает в силу ... У вас есть действующий человек слева ... У него есть человек справа ... Это ДУБЛИКАТЫ ... так что у вас есть совпадение, теперь, добавив логический p2.ID IS NULL создает тот же результат, что и NOT EXIST, давая окончательные результаты.

Я вернул свой запрос к обычному соединению.

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