IN против OR Oracle, что быстрее?

17

Я разрабатываю приложение, которое обрабатывает многие данные в базе данных Oracle.
В некоторых случаях мне нужно получить много объектов на основе определенного списка условий, и я использую SELECT ...FROM.. WHERE... IN... , но выражение IN просто принимает список, размер которого не превышает 1000 элементов.

Итак, вместо этого я использую выражение OR , но, как я вижу, возможно, этот запрос (используя OR ) медленнее, чем IN (с тем же списком условий). Это правильно? И если да, то как повысить скорость запроса?     

задан Đinh Hồng Châu 29.06.2011 в 03:38
источник
  • Является ли список статическим или полученным из запроса? –  Phil 29.06.2011 в 03:47
  • Нет, список значений для запроса был получен из внешнего ресурса. Есть ли способ решить эту проблему, потому что мой список слишком велик, может содержать более 100000 элементов –  Đinh Hồng Châu 29.06.2011 в 03:52
  • Итак, вы создаете массивную строку запроса, содержащую что-то вроде IN (... 9997, 9998, 9999, 1000,1001 ...)? Это само по себе будет стоить дорого, передавать и анализировать. Не обращайте внимания на возможности инъекции sql. –  O. Jones 29.06.2011 в 13:51

5 ответов

27

IN предпочтительнее OR - OR - плохо известный исполнитель, и может вызвать другие проблемы, которые потребуются с использованием скобок в сложных запросах.

Лучший вариант, чем IN или OR , - это присоединиться к таблице, содержащей нужные вам значения (или не хотите). Эта таблица для сравнения может быть получена, временная или уже существующая в вашей схеме.     

ответ дан OMG Ponies 29.06.2011 в 03:48
источник
  • Нет, я просто запрашиваю только одну таблицу. Мой список может содержать слишком много элементов, поэтому я не могу использовать IN. Я попытался разбить список на более мелкие части и сделать запрос в пакете подсписок, но позже мне нужно заказать данные по памяти, это очень медленно. –  Đinh Hồng Châu 29.06.2011 в 03:55
  • Не являются IN и OR одинаковыми? То есть IN расширяется до OR в любом случае? Вот почему NOT IN с NULL терпит неудачу –  gbn 29.06.2011 в 06:11
  • @gbn: Логически, да. Но IN оптимизирован против использования OR - это больше, чем синтаксический сахар. –  OMG Ponies 29.06.2011 в 06:28
  • Можете ли вы представить какие-либо доказательства того, что IN больше, чем синтаксический сахар? Я протестировал и опубликовал результаты в своем ответе. –  jva 27.07.2012 в 16:48
  • @jva: Это общая политика для downvote после того, как кто-то отвечает, а не раньше. Честно говоря, это отрывается, пытаясь рекламировать ваш ответ. Имейте в виду, что ответы в более поздних версиях могут означать, что поведение не гарантируется. –  OMG Ponies 28.07.2012 в 04:16
7

В этом случае я бы сделал следующее:

  1. Создать глобальную временную таблицу с одним столбцом
  2. Заполните эту таблицу своим списком из внешнего источника (и быстро - еще одно обсуждение)
  3. Сделайте свой запрос, присоединив временную таблицу к другой таблице (рассмотрите динамическую выборку, поскольку временная таблица не будет иметь хорошую статистику).

Это означает, что вы можете оставить сортировку в базе данных и написать простой запрос.

    
ответ дан WW. 29.06.2011 в 04:17
источник
2

Я бы поставил под сомнение весь подход. Клиент SP должен отправить 100000 идентификаторов. Откуда клиент получает эти идентификаторы? В любом случае отправка такого большого количества идентификаторов, как параметр proc, будет стоить значительно.

    
ответ дан oazabir 04.07.2011 в 19:34
источник
2

Oracle внутренне конвертирует списки IN в списки ORs так или иначе, поэтому действительно не должно быть различий в производительности. Единственное различие заключается в том, что Oracle должен преобразовывать INs, но имеет более длинные строки для синтаксического анализа, если вы сами поставляете OR.

Вот как вы это тестируете.

CREATE TABLE my_test (id NUMBER);

SELECT 1 
FROM my_test
WHERE id IN (1,2,3,4,5,6,7,8,9,10,
             21,22,23,24,25,26,27,28,29,30,
             31,32,33,34,35,36,37,38,39,40,
             41,42,43,44,45,46,47,48,49,50,
             51,52,53,54,55,56,57,58,59,60,
             61,62,63,64,65,66,67,68,69,70,
             71,72,73,74,75,76,77,78,79,80,
             81,82,83,84,85,86,87,88,89,90,
             91,92,93,94,95,96,97,98,99,100
             );

SELECT sql_text, hash_value
FROM v$sql 
WHERE sql_text LIKE '%my_test%';

SELECT operation, options, filter_predicates
FROM v$sql_plan
WHERE hash_value = '1181594990'; -- hash_value from previous query
  

ВЫБОР ЗАЯВЛЕНИЯ
  TABLE ACCESS FULL («ID» = 1 ИЛИ «ID» = 2 ИЛИ «ID» = 3 ИЛИ «ID» = 4 ИЛИ «ID» = 5   ИЛИ «ИД» = 6 ИЛИ «ИД» = 7 ИЛИ «ИД» = 8 ИЛИ «ИД» = 9 ИЛИ «ИД» = 10 ИЛИ «ИД» = 21 ИЛИ   «ID» = 22 ИЛИ «ID» = 23 ИЛИ «ИД» = 24 ИЛИ «ИД» = 25 ИЛИ «ИД» = 26 ИЛИ «ИД» = 27 ИЛИ   «ID» = 28 ИЛИ «ID» = 29 ИЛИ «ИД» = 30 ИЛИ «ИД» = 31 ИЛИ «ИД» = 32 ИЛИ «ИД» = 33 ИЛИ   «ID» = 34 ИЛИ «ID» = 35 ИЛИ «ID» = 36 ИЛИ «ID» = 37 ИЛИ «ID» = 38 ИЛИ «ID» = 39 ИЛИ   «ID» = 40 ИЛИ «ID» = 41 ИЛИ «ID» = 42 ИЛИ «ID» = 43 ИЛИ «ID» = 44 ИЛИ «ID» = 45 ИЛИ   «ID» = 46 ИЛИ «ID» = 47 ИЛИ «ID» = 48 ИЛИ «ID» = 49 ИЛИ «ID» = 50 ИЛИ «ID» = 51 ИЛИ   «ID» = 52 ИЛИ «ID» = 53 ИЛИ «ID» = 54 ИЛИ «ID» = 55 ИЛИ «ID» = 56 ИЛИ «ID» = 57 ИЛИ   «ID» = 58 ИЛИ «ID» = 59 ИЛИ «ID» = 60 ИЛИ «ID» = 61 ИЛИ «ID» = 62 ИЛИ «ID» = 63 ИЛИ   «ID» = 64 ИЛИ «ID» = 65 ИЛИ «ID» = 66 ИЛИ «ID» = 67 ИЛИ «ID» = 68 ИЛИ «ID» = 69 ИЛИ   «ИД» = 70 ИЛИ «ИД» = 71 ИЛИ «ИД» = 72 ИЛИ «ИД» = 73 ИЛИ «ИД» = 74 ИЛИ «ИД» = 75 ИЛИ   «ID» = 76 ИЛИ «ID» = 77 ИЛИ «ID» = 78 ИЛИ «ID» = 79 ИЛИ «ID» = 80 ИЛИ «ID» = 81 ИЛИ   «ИД» = 82 ИЛИ «ИД» = 83 ИЛИ «ИД» = 84 ИЛИ «ИД» = 85 ИЛИ «ИД» = 86 ИЛИ «ИД» = 87 ИЛИ   «ID» = 88 ИЛИ «ID» = 89 ИЛИ «ID» = 90 ИЛИ «ID» = 91 ИЛИ «ID» = 92 ИЛИ «ID» = 93 ИЛИ   «ID» = 94 ИЛИ «ID» = 95 ИЛИ «ID» = 96 ИЛИ «ID» = 97 ИЛИ «ID» = 98 ИЛИ «ID» = 99 ИЛИ   "ID" = 100)

    
ответ дан jva 27.07.2012 в 16:47
источник
  • Созданная таблица представляет собой таблицу кучи - нет первичного ключа / индекса –  OMG Ponies 28.07.2012 в 04:15
  • @OMGPonies Plus 1 по вашему ответу за то, что вы не преувеличиваете jva в возмездии. Я знаю, что многие люди это сделают. –  Mukus 04.05.2015 в 06:25
1

Если вы создаете таблицу с помощью первичного ключа:

CREATE TABLE my_test (id NUMBER,
CONSTRAINT PK PRIMARY KEY (id));

и пройти через те же самые SELECT, чтобы запустить запрос с несколькими значениями IN, а затем получить план выполнения с помощью хэш-значения, что вы получаете:

SELECT STATEMENT
INLIST ITERATOR
INDEX                  RANGE SCAN

Это означает, что если у вас есть список IN и используется с столбцом PK, Oracle сохраняет этот список внутренне как «INLIST», потому что более эффективно обрабатывать это, а не преобразовывать его в ORs, как в случай неиндексированной таблицы.

Я использовал Oracle 10gR2 выше.

    
ответ дан TrevorH 17.05.2013 в 13:42
источник