Упорядочивание по порядку значений в предложении SQL IN ()

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

Освоение архитектуры микросервисов с Laravel: Лучшие практики, преимущества и советы для разработчиков
Освоение архитектуры микросервисов с Laravel: Лучшие практики, преимущества и советы для разработчиков
В последние годы архитектура микросервисов приобрела популярность как способ построения масштабируемых и гибких приложений. Laravel , популярный PHP...
Как построить CRUD-приложение в Laravel
Как построить CRUD-приложение в Laravel
Laravel - это популярный PHP-фреймворк, который позволяет быстро и легко создавать веб-приложения. Одной из наиболее распространенных задач в...
Освоение PHP и управление базами данных: Создание собственной СУБД - часть II
Освоение PHP и управление базами данных: Создание собственной СУБД - часть II
В предыдущем посте мы создали функциональность вставки и чтения для нашей динамической СУБД. В этом посте мы собираемся реализовать функции обновления...
Документирование API с помощью Swagger на Springboot
Документирование API с помощью Swagger на Springboot
В предыдущей статье мы уже узнали, как создать Rest API с помощью Springboot и MySql .
Роли и разрешения пользователей без пакета Laravel 9
Роли и разрешения пользователей без пакета Laravel 9
Этот пост изначально был опубликован на techsolutionstuff.com .
Как установить LAMP Stack - Security 5/5 на виртуальную машину Azure Linux VM
Как установить LAMP Stack - Security 5/5 на виртуальную машину Azure Linux VM
В предыдущей статье мы завершили установку базы данных, для тех, кто не знает.
159
0
130 605
13
Перейти к ответу Данный вопрос помечен как решенный

Ответы 13

Попробуйте это:

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

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

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

Darryl Hein 29.12.2008 01:20
Ответ принят как подходящий

Используйте функцию 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() в одном и том же порядке.

Сортировка @Vojto в произвольном порядке по определению выполняется медленно. Это делает все возможное, поскольку нет гарантии, что параметры IN и FIELD равны. Выполнение этого в программном коде может быть быстрее, если использовать эти дополнительные знания. Конечно, было бы разумнее возложить эту нагрузку на клиента, а не на сервер, если вы имеете в виду производительность сервера.

ivan_pozdeev 08.02.2015 01:21

что насчет sqlite?

fnc12 23.05.2017 18:35

Мне нужно посмотреть следующий ответ, чтобы он заработал, не могу понять ваш синтаксис, я пытался использовать [], затем (), и в этом нет необходимости, просто идентификаторы, разделенные запятыми.

Windgate 17.02.2021 16:26

На ум приходят два решения:

  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 29.12.2008 02:23

Моей первой мыслью было написать один запрос, но вы сказали, что это невозможно, потому что один выполняется пользователем, а другой - в фоновом режиме. Как вы храните список идентификаторов для передачи от пользователя фоновому процессу? Почему бы не поместить их во временную таблицу со столбцом для обозначения порядка.

Так как насчет этого:

  1. Бит пользовательского интерфейса запускается и вставляет значения в новую таблицу, которую вы создаете. Он вставит идентификатор, должность и какой-то идентификатор номера вакансии)
  2. Номер задания передается фоновому процессу (вместо всех идентификаторов)
  3. Фоновый процесс делает выбор из таблицы на шаге 1, и вы присоединяетесь к нему, чтобы получить другую необходимую информацию. Он использует номер задания в предложении WHERE и упорядочивает по столбцу позиции.
  4. По завершении фоновый процесс удаляется из таблицы на основе идентификатора задания.

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

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

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

  SELECT * 
    FROM table_name 
   WHERE id IN (..,..,..,..) 
ORDER BY FIND_IN_SET (coloumn_name, .., .., ..);

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

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

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

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. Быстро и просто. Спасибо.

Menachem 31.08.2015 23:22

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

SELECT ...
FROM ...
ORDER  BY FIELD(user_id,5,3,2,...,50)  LIMIT 10

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

У меня, например, есть список идентификаторов треков «Недавно воспроизведенные», на SQLite я просто делаю:

SELECT * FROM recently NATURAL JOIN tracks;

О, если бы жизнь была такой простой :)

Darryl Hein 23.10.2015 21:35

Если вы хотите выполнить произвольную сортировку запроса с использованием значений, введенных запросом в 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.

Этот вопрос касается MySQL ... не уверен, что ваш запрос будет работать в MySQL.

Darryl Hein 07.12.2015 22:29

@ Дэррил, это не так. Но этот пост становится одним из лучших результатов, когда вы гуглите, как заказывать с помощью предложения IN, поэтому я решил, что могу опубликовать его здесь, и общая методология применяется ко всем ANSI-совместимым SQL-серверам (просто замените оператор VALUES стандартным способ создания таблицы).

Ian 08.12.2015 00:21

Обратите внимание, у меня это сработало, но только после того, как я заменил ссылки на orderTbl.orderKey, orderTbl.orderIndex на orderKey, orderIndex

Peter 15.06.2020 23:50

Я просто пытался сделать это на 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

Обратите внимание, что я тоже разрешаю дублирование.

Другие вопросы по теме