ORDER BY IN值列表
我在PostgreSQL 8.3中有一个简单的SQL查询来抓取一堆评论。 我在WHERE
子句中为IN
构造提供了一个sorting的值列表:
SELECT * FROM comments WHERE (comments.id IN (1,3,2,4));
这个以任意顺序返回的评论,在我碰巧是1,2,3,4
。
我想要结果行像IN
结构中的列表一样sorting: (1,3,2,4)
。
如何实现?
你可以很容易地使用(在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
只是因为它很难find,它必须传播: 在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 。 -
我们不需要子查询,我们可以使用像表一样的返回函数。
-
交给数组而不是ARRAY构造函数的string文字可能更容易与一些客户端实现。
详细说明:
- PostgreSQL unnest()与元素编号
在Postgres中做的另一种方法是使用idx
函数。
SELECT * FROM comments ORDER BY idx(array[1,3,2,4], comments.id)
不要忘了先创buildidx
函数,如下所述: http : //wiki.postgresql.org/wiki/Array_Index
使用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')
在研究这一些,我发现这个解决scheme:
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
但是,这看起来相当冗长,并可能有大型数据集的性能问题。 任何人都可以评论这些问题?
要做到这一点,我想你应该有一个额外的“ORDER”表,它定义了ID的映射顺序(有效地做你对自己的问题的回应说),然后你可以使用作为一个额外的列在你的select你可以sorting。
这样,您就明确地描述了数据库中您希望的顺序。
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
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不擅长”的海报。 如果你想按照某些方面的评论sorting,然后添加另一个整数列到您的表之一来保存您的sorting标准和sorting的价值。 例如“ORDER BY comments.sort DESC”如果你想每次都按不同的顺序sorting,那么… SQL在这种情况下不适合你。
这是另一个解决scheme,使用常量表( 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;
[编辑]
unnest在8.3里还没有内置,但是你可以自己创build一个(任何*的美):
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; $$;
该function可以在任何types的工作:
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 $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;
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]是有一个叫做id的字段的主表,而id是存储comments.id的数组。
通过postgresql 9.6