SQL WHERE ID IN (id1, id2,..., idn)

Мне нужно написать запрос, чтобы получить большой список идентификаторов.

Мы поддерживаем множество бэкэндов (MySQL, Firebird, SQLServer, Oracle, PostgreSQL...), поэтому мне нужно написать стандартный SQL.

Размер набора идентификаторов может быть большим, запрос будет сгенерирован программно. Итак, каков наилучший подход?

1) Написание запроса с использованием IN

SELECT * FROM TABLE WHERE ID IN (id1, id2, ..., idn)

Мой вопрос здесь Что будет, если n очень большое? А как насчет производительности?

2) Написание запроса с использованием ИЛИ

SELECT * FROM TABLE WHERE ID = id1 OR ID = id2 OR ... OR ID = idn

Я думаю, что у этого подхода нет предела n, но как насчет производительности, если n очень велико?

3) Написание программного решения:

  foreach (var id in myIdList)
  {
      var item = GetItemByQuery("SELECT * FROM TABLE WHERE ID = " + id);
      myObjectList.Add(item);
  }

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

Каково было бы правильное решение этой проблемы?

Ответ 1

Вариант 1 является единственным хорошим решением.

Почему?

  • Вариант 2 делает то же самое, но вы повторяете имя столбца много раз; кроме того, механизм SQL не сразу знает, что вы хотите проверить, является ли это значение одним из значений в фиксированном списке. Однако хороший SQL-движок мог бы оптимизировать его, чтобы иметь равную производительность, например, с помощью IN. Там все еще проблема читаемости, хотя...

  • Вариант 3 просто ужасен по производительности. Он отправляет запрос в каждый цикл и забивает базу данных небольшими запросами. Это также предотвращает использование каких-либо оптимизаций для того, чтобы "значение является одним из значений в данном списке"

Ответ 2

Альтернативный подход может заключаться в использовании другой таблицы для хранения значений id. Затем эта другая таблица может быть объединена внутри вашего TABLE для ограничения возвращаемых строк. Это будет иметь главное преимущество в том, что вам не понадобится динамический SQL (проблематично в лучшие времена), и у вас не будет бесконечно длинного предложения IN.

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

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

Ответ 3

То, что предложил Эд Гиннес, действительно повышает производительность, у меня был такой запрос

select * from table where id in (id1,id2.........long list)

что я сделал:

DECLARE @temp table(
            ID  int
            )
insert into @temp 
select * from dbo.fnSplitter('#idlist#')

Затем внутреннее объединяется с основной таблицей:

select * from table inner join temp on temp.id = table.id

И производительность резко улучшилась.

Ответ 4

Первый вариант, безусловно, лучший вариант.

SELECT * FROM TABLE WHERE ID IN (id1, id2, ..., idn)

Однако учитывая, что список идентификаторов очень большой, скажем, миллионы, вы должны рассмотреть размеры блоков, как показано ниже:

  • Разделите список идентификаторов на куски фиксированного номера, скажем, 100
  • Размер пакета должен определяться на основе размера памяти вашего сервера.
  • Предположим, что у вас есть 10000 идентификаторов, у вас будет 10000/100 = 100 кусков.
  • Обработать один фрагмент за один раз, в результате чего 100 запросов на доступ к базе данных для выбора

Зачем вам делиться на куски?

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

Он всегда работал у меня как шарм. Надеюсь, что это сработает и для моих коллег-разработчиков:)

Ответ 5

Выполнение команды SELECT * FROM MyTable where id in() для таблицы SQL Azure с 500 миллионами записей привело к времени ожидания> 7 минут!

Делая это, вы сразу же получили результаты:

select b.id, a.* from MyTable a
join (values (250000), (2500001), (2600000)) as b(id)
ON a.id = b.id

Используйте объединение.

Ответ 6

В большинстве систем баз данных IN (val1, val2, …) и ряд OR оптимизированы для одного и того же плана.

Третий способ - импортировать список значений во временную таблицу и присоединиться к ней, что более эффективно в большинстве систем, если есть много значений.

Вы можете прочитать следующие статьи:

Ответ 7

Образец 3 был бы худшим исполнителем из всех, потому что вы бесцельно забиваете базу данных без видимых причин.

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

Ответ 8

Я думаю, что вы имеете в виду SqlServer, но в Oracle у вас есть жесткий предел, сколько IN элементов вы можете указать: 1000.