Впорядкування за порядком значень у пункті SQL IN ()


154

Мені цікаво, чи немає вподобань (можливо, кращого способу) впорядкувати за порядком значень у пункті IN ().

Проблема полягає в тому, що у мене є 2 запити, один, який отримує всі ідентифікатори, а другий, який отримує всю інформацію. Перший створює порядок ідентифікаторів, за якими я хочу, щоб другий був упорядкований. Ідентифікатори ставлять у пункті IN () у правильному порядку.

Отже, це буде щось на зразок (надзвичайно спрощене):

SELECT id FROM table1 WHERE ... ORDER BY display_order, name

SELECT name, description, ... WHERE id IN ([id's from first])

Проблема полягає в тому, що другий запит не повертає результати в тому ж порядку, що ідентифікатори, що вводяться в пункт IN ().

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

Чи є кращий варіант?

Примітка. Оскільки перший запит виконується "користувачем", а другий виконується у фоновому процесі, немає можливості поєднати запит 2 в 1, використовуючи підзапити.

Я використовую MySQL, але я думаю, що може бути корисним, щоб він зазначив, які варіанти є і для інших БД.

Відповіді:


186

Використовуйте FIELD()функцію MySQL :

SELECT name, description, ...
FROM ...
WHERE id IN([ids, any order])
ORDER BY FIELD(id, [ids in order])

FIELD() поверне індекс першого параметра, який дорівнює першому параметру (крім самого першого параметра).

FIELD('a', 'a', 'b', 'c')

поверне 1

FIELD('a', 'c', 'b', 'a')

повернеться 3

Це зробить саме те, що ви хочете, якщо вставити ідентифікатори в IN()пункт і FIELD()функцію в тому ж порядку.


3
@ Войто сортування за довільним порядком є ​​повільним за визначенням. Це робить все можливе, оскільки немає гарантії рівності INта FIELDпараметрів. Зробити це в програмному коді можливо швидше, використовуючи ці додаткові знання. Звичайно, було б розумніше покласти цей тягар на клієнта, а не на сервер, якщо ви маєте на увазі продуктивність сервера.
ivan_pozdeev

1
про що sqlite?
fnc12

15

Дивіться далі, як отримати відсортовані дані.

SELECT ...
  FROM ...
 WHERE zip IN (91709,92886,92807,...,91356)
   AND user.status=1
ORDER 
    BY provider.package_id DESC 
     , FIELD(zip,91709,92886,92807,...,91356)
LIMIT 10

11

Два рішення, які спадають на думку:

  1. order by case id when 123 then 1 when 456 then 2 else null end asc

  2. order by instr(','||id||',',',123,456,') asc

( instr()Від Oracle, може бути , у вас є locate()або charindex()чи що - то в цьому роді)


Для MySQL також можна використовувати FIELD_IN_SET (): dev.mysql.com/doc/refman/5.0/en/…
Darryl Hein


6

Якщо ви хочете зробити довільне сортування за запитом, використовуючи значення, введені запитом у MS SQL Server 2008+ , це можна зробити, створивши таблицю на ходу та зробивши з'єднання так (використовуючи номенклатуру з ОП).

SELECT table1.name, table1.description ... 
FROM (VALUES (id1,1), (id2,2), (id3,3) ...) AS orderTbl(orderKey, orderIdx) 
LEFT JOIN table1 ON orderTbl.orderKey=table1.id
ORDER BY orderTbl.orderIdx

Якщо ви заміните оператор VALUES чимось іншим, що робить те саме, але в ANSI SQL, то це повинно працювати на будь-якій базі даних SQL.

Примітка: Другий стовпець у створеній таблиці (orderTbl.orderIdx) необхідний, коли запити наборів записів перевищують 100 або більше. Спочатку у мене не було стовпця orderIdx, але я виявив, що при наборах результатів більше 100 я повинен був явно сортувати за цим стовпцем; в будь-якому випадку в SQL Server Express 2014.


Це питання стосується MySQL ... не впевнений, що ваш запит буде працювати в MySQL.
Дарріл Хайн

2
@Darryl, це не буде. Але ця публікація з'являється як найкращі результати, коли ви google як замовити за допомогою пункту IN, тому я зрозумів, що я можу також розмістити його тут, і загальна методологія застосовується до всіх сумісних з ANSI серверів, сумісних з ANSI (просто замініть оператор VALUES на стандартний спосіб створення таблиці).
Ян

Зверніть увагу, що це працює для мене, але тільки після того, як я замінив посилання на orderTbl.orderKey, orderTbl.orderIndexпо orderKey,orderIndex
Пітер

5
SELECT ORDER_NO, DELIVERY_ADDRESS 
from IFSAPP.PURCHASE_ORDER_TAB 
where ORDER_NO in ('52000077','52000079','52000167','52000297','52000204','52000409','52000126') 
ORDER BY instr('52000077,52000079,52000167,52000297,52000204,52000409,52000126',ORDER_NO)

працював дійсно чудово


Працював для мене на Oracle. Швидко і просто. Дякую.
Менахем

4

Стаття IN описує набір значень, а набори не мають порядку.

Ваше рішення із з'єднанням та замовленням у display_orderстовпці - це майже майже правильне рішення; що-небудь інше, мабуть, специфічний злом для СУБД (або виконує деякі завдання з функціями OLAP у стандартному SQL). Звичайно, об'єднання - це майже майже портативне рішення (хоча генерування даних зі display_orderзначеннями може бути проблематичним). Зауважте, що вам може знадобитися вибрати стовпці для замовлення; це було вимогою до стандартних SQL, хоча я вважаю, що це було розслаблено, як правило, деякий час тому (можливо, так давно, як SQL-92).


2

Для Oracle працює рішення Джона за допомогою функції instr (). Ось дещо інше рішення, яке спрацювало - SELECT id FROM table1 WHERE id IN (1, 20, 45, 60) ORDER BY instr('1, 20, 45, 60', id)



2

Я щойно намагався зробити це MS SQL Server, де у нас немає FIELD ():

SELECT table1.id
... 
INNER JOIN
    (VALUES (10,1),(3,2),(4,3),(5,4),(7,5),(8,6),(9,7),(2,8),(6,9),(5,10)
    ) AS X(id,sortorder)
        ON X.id = table1.id
    ORDER BY X.sortorder

Зауважте, що я дозволяю і дублювання.


1

Перша моя думка полягала в тому, щоб написати єдиний запит, але ви сказали, що це неможливо, оскільки один працює користувачем, а другий працює у фоновому режимі. Як ви зберігаєте список ідентифікаторів, які потрібно перейти від користувача до фонового процесу? Чому б не помістити їх у тимчасову таблицю з стовпчиком для позначення замовлення.

То як щодо цього:

  1. Біт інтерфейсу користувача запускається і вставляє значення в нову створену вами таблицю. Він буде вставляти ідентифікатор, посаду та якийсь ідентифікатор номера роботи)
  2. Номер завдання передається у фоновий процес (замість усіх ідентифікаторів)
  3. Фоновий процес робить вибір із таблиці на кроці 1, і ви приєднуєтесь, щоб отримати іншу інформацію, яка вам потрібна. Він використовує номер завдання в пункті WHERE та замовлення стовпця позиції.
  4. Після закінчення фоновий процес видаляється з таблиці на основі ідентифікатора завдання.

1

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

У мене, наприклад, є список нещодавно відтворених ідентифікаторів треків на SQLite, я просто роблю:

SELECT * FROM recently NATURAL JOIN tracks;

1
О, я б хотів, щоб життя було таким простим :)
Дарріл Хайн

0

Спробуйте:

SELECT name, description, ...
WHERE id IN
    (SELECT id FROM table1 WHERE...)
ORDER BY
    (SELECT display_order FROM table1 WHERE...),
    (SELECT name FROM table1 WHERE...)

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


див. примітку, а також, як зазначено, приклади запитів надзвичайно спрощені, тому я не можу поєднати їх у 1 запит із підзапитими.
Дарріл Хайн
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.