Упорядочение по порядку значений в предложении SQL IN () - PullRequest
139 голосов
/ 29 декабря 2008

Мне интересно, есть ли возможность (возможно, лучший способ) упорядочить по порядку значений в предложении 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, но я думаю, что было бы полезно, чтобы он указывал, какие опции есть и для других БД.

Ответы [ 13 ]

175 голосов
/ 29 декабря 2008

Используйте функцию MySQL FIELD():

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() в том же порядке.

14 голосов
/ 01 декабря 2011

См. Ниже, как получить отсортированные данные.

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 голосов
/ 29 декабря 2008

Два решения, которые приходят на ум:

  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() или что-то в этом роде)

5 голосов
/ 07 декабря 2015

Если вы хотите выполнить произвольную сортировку запроса, используя значения, введенные запросом в MS SQL Server 2008 + , это можно сделать, создав таблицу на лету и выполнив объединение следующим образом ( используя номенклатуру из OP).

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.

5 голосов
/ 30 января 2014

Ответы для получения отсортированных данных.

SELECT ...
FROM ...
ORDER  BY FIELD(user_id,5,3,2,...,50)  LIMIT 10
4 голосов
/ 29 декабря 2008

Предложение IN описывает набор значений, а наборы не имеют порядка.

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

3 голосов
/ 05 июля 2012
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)

отлично работал

2 голосов
/ 01 июня 2012

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

2 голосов
/ 24 ноября 2011

Использовать MySQL FIND_IN_SET Функция:

  SELECT * 
    FROM table_name 
   WHERE id IN (..,..,..,..) 
ORDER BY FIND_IN_SET (coloumn_name, .., .., ..);
1 голос
/ 23 октября 2015

Я думаю, что вам нужно хранить свои данные так, чтобы вы просто делали соединение, и оно было бы идеальным, поэтому никаких хаков и сложных вещей не происходит.

У меня есть, например, список недавно воспроизведенных дорожек, на SQLite я просто делаю:

SELECT * FROM recently NATURAL JOIN tracks;
...