ПОРЯДОК ВЫПОЛНЕНИЯ В списке значений IN - PullRequest
130 голосов
/ 15 мая 2009

У меня есть простой SQL-запрос в PostgreSQL 8.3, который собирает кучу комментариев. Я предоставляю отсортированный список значений для конструкции IN в предложении WHERE:

SELECT * FROM comments WHERE (comments.id IN (1,3,2,4));

Возвращает комментарии в произвольном порядке, который в моем случае имеет идентификаторы типа 1,2,3,4.

Я хочу, чтобы результирующие строки были отсортированы как список в конструкции IN: (1,3,2,4).
Как этого добиться?

Ответы [ 16 ]

87 голосов
/ 15 мая 2009

Вы можете сделать это довольно легко с (представлен в PostgreSQL 8.2) VALUES (), ().

Синтаксис будет таким:

select c.*
from comments c
join (
  values
    (1,1),
    (3,2),
    (2,3),
    (4,4)
) as x (id, ordering) on c.id = x.id
order by x.ordering
61 голосов
/ 16 января 2012

Просто потому, что его так трудно найти и нужно распространять: в mySQL это можно сделать намного проще , но я не знаю, работает ли он в другом SQL.

SELECT * FROM `comments`
WHERE `comments`.`id` IN ('12','5','3','17')
ORDER BY FIELD(`comments`.`id`,'12','5','3','17')
41 голосов
/ 28 февраля 2012

Я думаю, что так лучше:

SELECT * FROM "comments" WHERE ("comments"."id" IN (1,3,2,4))
    ORDER BY  id=1 DESC, id=3 DESC, id=2 DESC, id=4 DESC
36 голосов
/ 17 февраля 2016

В Postgres 9,4 или более поздних версиях это, вероятно, простейших и быстрых :

SELECT c.*
FROM   comments c
JOIN   unnest('{1,3,2,4}'::int[]) WITH ORDINALITY t(id, ord) USING (id)
ORDER  BY t.ord;
  • Используя новый WITH ORDINALITY, который @ a_horse уже упоминался .

  • Нам не нужен подзапрос, мы можем использовать функцию возврата набора, как таблицу.

  • Строковый литерал, передаваемый в массив вместо ARRAY-конструктора , может быть проще реализовать с некоторыми клиентами.

Подробное объяснение:

27 голосов
/ 13 января 2013

Еще один способ сделать это в Postgres - использовать функцию idx.

SELECT *
FROM comments
ORDER BY idx(array[1,3,2,4], comments.id)

Не забудьте сначала создать функцию idx, как описано здесь: http://wiki.postgresql.org/wiki/Array_Index

26 голосов
/ 13 апреля 2015

С Postgres 9.4 это можно сделать немного короче:

select c.*
from comments c
join (
  select *
  from unnest(array[43,47,42]) with ordinality
) as x (id, ordering) on c.id = x.id
order by x.ordering

Снятие необходимости вручную назначать / поддерживать позицию для каждого значения.

С Postgres 9,6 это можно сделать с помощью array_position():

with x (id_list) as (
  values (array[42,48,43])
)
select c.*
from comments c, x
where id = any (x.id_list)
order by array_position(x.id_list, c.id);

CTE используется для того, чтобы список значений задавался только один раз. Если это не важно, это также можно записать так:

select c.*
from comments c
where id in (42,48,43)
order by array_position(array[42,48,43], c.id);
17 голосов
/ 26 марта 2013

В Postgresql:

select *
from comments
where id in (1,3,2,4)
order by position(id::text in '1,3,2,4')
4 голосов
/ 15 мая 2009

Исследуя это, я нашел это решение:

SELECT * FROM "comments" WHERE ("comments"."id" IN (1,3,2,4)) 
ORDER BY CASE "comments"."id"
WHEN 1 THEN 1
WHEN 3 THEN 2
WHEN 2 THEN 3
WHEN 4 THEN 4
END

Однако это выглядит довольно многословно и может иметь проблемы с производительностью для больших наборов данных. Кто-нибудь может прокомментировать эти вопросы?

2 голосов
/ 15 мая 2009

без последовательности, работает только на 8.4:

select * from comments c
join 
(
    select id, row_number() over() as id_sorter  
    from (select unnest(ARRAY[1,3,2,4]) as id) as y
) x on x.id = c.id
order by x.id_sorter
2 голосов
/ 15 мая 2009

Для этого, я думаю, у вас, вероятно, должна быть дополнительная таблица «ORDER», которая определяет отображение идентификаторов на порядок (эффективно выполняющий то, что сказал ваш ответ на ваш собственный вопрос), которую вы затем можете использовать в качестве дополнительного столбца на ваш выбор, который вы можете затем сортировать.

Таким образом, вы явно описываете желаемый порядок в базе данных, где он и должен быть.

...