Порядок по списку значений



у меня есть простой 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).

Как этого добиться?

591   16  

16 ответов:

вы можете сделать это довольно легко с (введенными в PostgreSQL 8.2) значениями (), ().

синтаксис будет выглядеть так:

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

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

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

Я думаю, что этот путь лучше :

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

В 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-returning как таблицу.

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

подробное описание:

другой способ сделать это в Postgres было бы использовать

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

В Postgresql:

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

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

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

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

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

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

без последовательности, работает только на 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
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,')

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

и вот еще одно решение, которое работает и использует постоянную таблицу (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

но опять же я не уверен, что это непроизводительно.

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

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

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]

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

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

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

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

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

CREATE OR REPLACE FUNCTION in_sort(anyarray, out id anyelement, out ordinal int)
LANGUAGE SQL AS
$$
    SELECT [i], i FROM generate_series(array_lower(,1),array_upper(,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;
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-это массив, в котором хранятся comments.id .

передано в postgresql 9.6

Comments

    Ничего не найдено.