ORDER В списке значений IN - программирование
Подтвердить что ты не робот

ORDER В списке значений IN

У меня есть простой 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).
Как достичь этого?

4b9b3361

Ответ 1

Вы можете сделать это довольно легко с помощью (введено в 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

Ответ 2

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

SELECT * FROM `comments`
WHERE `comments`.`id` IN ('12','5','3','17')
ORDER BY FIELD(`comments`.`id`,'12','5','3','17')

Ответ 3

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

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

Ответ 4

В 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 уже упоминался.

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

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

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

Ответ 5

Другим способом сделать это в Postgres будет использование функции idx.

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

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

Ответ 6

С 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);

Ответ 7

В Postgresql:

select *
from comments
where id in (1,3,2,4)
order by position(id::text in '1,3,2,4')

Ответ 8

Изучив это, я нашел это решение:

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

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

Ответ 9

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

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

Ответ 10

sans SEQUENCE, работает только на 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

Ответ 11

SELECT * FROM "comments" JOIN (
  SELECT 1 as "id",1 as "order" UNION ALL 
  SELECT 3,2 UNION ALL SELECT 2,3 UNION ALL SELECT 4,4
) j ON "comments"."id" = j."id" ORDER BY j.ORDER

или если вы предпочитаете зло над добром:

SELECT * FROM "comments" WHERE ("comments"."id" IN (1,3,2,4))
ORDER BY POSITION(','+"comments"."id"+',' IN ',1,3,2,4,')

Ответ 12

Я согласен со всеми другими плакатами, которые говорят "не делай этого", или "SQL не хорош в этом". Если вы хотите отсортировать по некоторым аспектам комментариев, добавьте еще один целочисленный столбец в одну из ваших таблиц, чтобы сохранить критерии сортировки и отсортировать их по этому значению. например "ORDER BY comments.sort DESC" Если вы хотите сортировать их в другом порядке каждый раз, тогда... SQL не будет для вас в этом случае.

Ответ 13

create sequence serial start 1;

select * from comments c
join (select unnest(ARRAY[1,3,2,4]) as id, nextval('serial') as id_sorter) x
on x.id = c.id
order by x.id_sorter;

drop sequence serial;

[EDIT]

unsest еще не встроен в 8.3, но вы можете создать его самостоятельно (красота любого *):

create function unnest(anyarray) returns setof anyelement
language sql as
$$
    select $1[i] from generate_series(array_lower($1,1),array_upper($1,1)) i;
$$;

эта функция может работать в любом типе:

select unnest(array['John','Paul','George','Ringo']) as beatle
select unnest(array[1,3,2,4]) as id

Ответ 14

И здесь другое решение, которое работает и использует постоянную таблицу (http://www.postgresql.org/docs/8.3/interactive/sql-values.html):

SELECT * FROM comments AS c,
(VALUES (1,1),(3,2),(2,3),(4,4) ) AS t (ord_id,ord)
WHERE (c.id IN (1,3,2,4)) AND (c.id = t.ord_id)
ORDER BY ord

Но я не уверен, что это исполнитель.

Теперь у меня есть куча ответов. Могу ли я получить голосование и комментарии, чтобы я знал, кто победитель!

Спасибо всем:-)

Ответ 15

Небольшое улучшение по сравнению с версией, которая использует последовательность, я думаю:

CREATE OR REPLACE FUNCTION in_sort(anyarray, out id anyelement, out ordinal int)
LANGUAGE SQL AS
$$
    SELECT $1[i], i FROM generate_series(array_lower($1,1),array_upper($1,1)) i;
$$;

SELECT 
    * 
FROM 
    comments c
    INNER JOIN (SELECT * FROM in_sort(ARRAY[1,3,2,4])) AS in_sort
        USING (id)
ORDER BY in_sort.ordinal;

Ответ 16

select * from comments where comments.id in 
(select unnest(ids) from bbs where id=19795) 
order by array_position((select ids from bbs where id=19795),comments.id)

здесь [bbs] - это основная таблица, в которой есть поле с именем ids, и, ids - это массив, в котором хранятся комментарии .id.

передано в postgresql 9.6

Ответ 17

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

SELECT a.id,a.status,a.description FROM minicloud_tasks as a ORDER BY random();

 id |   status   |   description    
----+------------+------------------
  4 | processing | work on postgres
  6 | deleted    | need some rest
  3 | pending    | garden party
  5 | completed  | work on html

И вы хотите упорядочить список задач по его статусу. Статус представляет собой список строковых значений:

(processing, pending,  completed, deleted)

Хитрость заключается в том, чтобы присвоить каждому значению статуса целое число и упорядочить список по порядку:

SELECT a.id,a.status,a.description FROM minicloud_tasks AS a
  JOIN (
    VALUES ('processing', 1), ('pending', 2), ('completed', 3), ('deleted', 4)
  ) AS b (status, id) ON (a.status = b.status)
  ORDER BY b.id ASC;

Что приводит к:

 id |   status   |   description    
----+------------+------------------
  4 | processing | work on postgres
  3 | pending    | garden party
  5 | completed  | work on html
  6 | deleted    | need some rest

Credit @user80168