Порядок по списку значений
у меня есть простой 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 ответов:
вы можете сделать это довольно легко с (введенными в 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 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);
исследуя это еще немного, я нашел это решение:
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