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


170

Мені потрібно написати запит, щоб отримати великий список ідентифікаторів.

Ми підтримуємо безліч програм (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 значно скорочує час відгуку SQL-сервера, вибираючи ідентифікатори 7k, з яких деякі не існували. Зазвичай запит займає близько 1300 мс, він зменшується до 80 мс за допомогою IN! Я зробив шахту як ваше рішення 1 + 3. Просто остаточний запит був один, довгий рядок запиту, відправлений SQL для виконання.
Piotr Kula

Відповіді:


108

Варіант 1 - єдине хороше рішення.

Чому?

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

  • Варіант 3 - просто жахливий показник. Він надсилає запит кожен цикл і забиває базу даних невеликими запитами. Це також заважає використовувати будь-які оптимізації для "значення є одним із тих, що в заданому списку"


2
Я погоджуюся, але зауважте, що список в багатьох RDMS обмежений, і тому вам потрібно буде використовувати рішення @Ed Guiness, але тут тимчасові таблиці різняться між RDBMS. (Ефективно для складних проблем ви не можете використовувати просто чистий стандартний SQL)
mmmmmm

28

Альтернативним підходом може бути використання іншої таблиці, щоб містити значення id. Потім інша таблиця може бути внутрішньо з'єднана на вашій ТАБЛИЦІ, щоб обмежити повернуті рядки. Це матиме головну перевагу в тому, що вам не знадобиться динамічний SQL (у кращі рази проблематичний), і ви не матимете нескінченно довгий пункт IN.

Ви повинні усікати цю іншу таблицю, вставити велику кількість рядків, а потім, можливо, створити індекс, щоб сприяти ефективності приєднання. Це також дозволить вам від'єднати накопичення цих рядків від отримання даних, можливо, надасть більше можливостей для налаштування продуктивності.

Оновлення : Хоча ви могли використовувати тимчасову таблицю, я не мав на увазі, що ви повинні або навіть повинні. Постійна таблиця, що використовується для тимчасових даних, - це звичайне рішення, яке має переваги, ніж описані тут.


1
Але як би ви передали список ідентифікаторів, які вам потрібні? (Бачачи, що ви не можете вибрати діапазон чи щось подібне).
raam86

1
@ raam86: список ідентифікаторів, можливо, був отриманий за допомогою selectоператора в іншій таблиці. Список передається як інша таблиця, inner joinпроти якої ви працюєте .
bdforbes

19

Що запропонував Ед Ґайнес - це справді прискорення продуктивності, у мене був такий запит

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

А продуктивність різко покращилась.


1
Привіт, fnSplitter є функцією від MSSQL? Тому що я не зміг його знайти.
WiiMaxx

Це не стандартна річ. Вони повинні означати, що вони написали цю функцію для цієї мети, або, наприклад, мали програму, яка вже її надала.
підкреслюйте_d

fnSplitter - це функція, створена Ritu, ви можете знайти її в Інтернеті / Google, подібній до неї
Башар Абу Шамаа

9

Перший варіант, безумовно, найкращий варіант.

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

Однак, враховуючи, що список ідентифікаторів дуже величезний , скажімо мільйони, слід врахувати розміри фрагментів, як показано нижче:

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

Навіщо вам ділитися на шматки?

Ви ніколи не отримаєте виняток із переповненням пам'яті, що дуже часто зустрічається у ваших сценаріях. У вас буде оптимізована кількість дзвінків до бази даних, що призведе до кращої продуктивності.

Це завжди працювало як шарм для мене. Сподіваюся, це буде працювати і для моїх колег-розробників :)


4

Виконання SELECT * FROM MyTable, де id в () команді таблиці Azure SQL з 500 мільйонами записів призвело до часу очікування> 7 хв!

Це замість негайно повертає результати:

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

Використовуйте приєднання.


3

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

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

Ви можете прочитати ці статті:


3

Зразок 3 був би найгіршим результатом з усіх, тому що ви безліч причин безперешкодно обробляєте базу даних.

Завантаження даних у темп-таблицю, а потім приєднання до цього було б набагато швидшим. Після цього IN повинен працювати трохи швидше, ніж група АБО.


2

Я думаю, ви маєте на увазі SqlServer, але для Oracle у вас є жорсткий ліміт, скільки елементів IN ви можете вказати: 1000.


1
Навіть SQL Server перестає працювати після ~ 40k IN елементів. Згідно з MSDN: Включення надзвичайно великої кількості значень (багато тисяч) у пункт IN може споживати ресурси та повертати помилки 8623 або 8632. Щоб вирішити цю проблему, зберігайте елементи у списку IN у таблиці.
jahav
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.