按IN值列表排序

时间:2009-05-15 00:02:23

标签: sql postgresql sql-order-by sql-in

我在PostgreSQL 8.3中有一个简单的SQL查询,可以获取一堆注释。我在IN子句中为WHERE构造提供了排序值列表:

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

这会以任意顺序返回评论,而我恰好是1,2,3,4

我希望结果行的排序方式与IN构造中的列表类似:(1,3,2,4) 如何实现?

17 个答案:

答案 0 :(得分:86)

你可以很容易地使用(在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

答案 1 :(得分:61)

仅仅因为它很难找到并且必须传播:in mySQL this can be done much simpler,但我不知道它是否适用于其他SQL。

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

答案 2 :(得分:40)

我认为这种方式更好:

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

答案 3 :(得分:36)

在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;

详细说明:

答案 4 :(得分:27)

在Postgres中执行此操作的另一种方法是使用idx函数。

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

请勿忘记首先创建idx功能,如下所述:http://wiki.postgresql.org/wiki/Array_Index

答案 5 :(得分:26)

使用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);

答案 6 :(得分:17)

在Postgresql中:

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

答案 7 :(得分: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 :(得分:2)

要做到这一点,我认为你应该有一个额外的“ORDER”表来定义ID到订单的映射(有效地做你对自己问题的回答所说的),然后你可以将其用作附加列。您可以选择哪个选择。

通过这种方式,您可以在数据库中明确描述您想要的顺序。

答案 9 :(得分:2)

没有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

答案 10 :(得分:1)

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,')

答案 11 :(得分:0)

让我们对已说的内容有视觉印象。例如,您有一个包含一些任务的表:

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

信用@ user80168

答案 12 :(得分:0)

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中传递

答案 13 :(得分:0)

使用我认为的序列的版本略有改进:

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;

答案 14 :(得分:0)

这是另一个有效且使用常量表(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 :(得分:0)

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;

[编辑]

在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

答案 16 :(得分:0)

我同意所有其他海报说“不要那样做”或“SQL不擅长”。如果要按注释的某些方面进行排序,请在其中一个表中添加另一个整数列以保存排序条件并按该值排序。例如“ORDER BY comments.sort DESC”如果你想每次以不同的顺序对它们进行排序那么......在这种情况下,SQL将不适合你。