按IN值列表排序

nut*_*ker 146 sql postgresql sql-order-by sql-in

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

SELECT * FROM comments WHERE (comments.id IN (1,3,2,4));
Run Code Online (Sandbox Code Playgroud)

这将以任意顺序返回注释,在我碰巧是ids之类的1,2,3,4.

我希望结果行像IN构造中的列表一样排序:(1,3,2,4).
怎么实现呢?

小智 94

你可以很容易地使用(在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
Run Code Online (Sandbox Code Playgroud)

  • 谢谢.这也适用于SQL Server (2认同)
  • @ user80168如果IN子句中有数千个值,该怎么办?因为我必须为成千上万的记录做这件事 (2认同)

小智 62

仅仅因为它很难找到并且必须传播:在mySQL中,这可以做得更简单,但我不知道它是否适用于其他SQL.

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

  • `ERROR:不能将超过100个参数传递给函数` (5认同)
  • 值列表必须以两种不同的方式提供*两次*.没那么简单.接受的答案只需***(即使是更冗长的方式).现代Postgres更简单(如新答案所示).此外,这个问题似乎与Postgres有关. (3认同)

Erw*_*ter 44

在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;
Run Code Online (Sandbox Code Playgroud)

详细说明:

  • 这是最好的答案。 (3认同)
  • 就是这样。顺便说一句,如果您想根据以下顺序进行排序,您还可以执行“JOIN UNNEST(ARRAY['B','C','A']::text[]) WITH ORDINALITY t(id, ord) USING (id)`字符串列表而不是整数。 (2认同)

van*_*con 43

我认为这种方式更好:

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
Run Code Online (Sandbox Code Playgroud)


a_h*_*ame 33

使用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
Run Code Online (Sandbox Code Playgroud)

无需为每个值手动分配/维护位置.

使用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);
Run Code Online (Sandbox Code Playgroud)

使用CTE使得值列表仅需要指定一次.如果这不重要,这也可以写成:

select c.*
from comments c
where id in (42,48,43)
order by array_position(array[42,48,43], c.id);
Run Code Online (Sandbox Code Playgroud)

  • @aaandre 以下转换工作正常(至少在 Postgres 12 中) `array_position(array[42, 48, 43]::bigint[], c.id::bigint)`,因此无需将 `bigint` 截断为`int`。 (2认同)

Car*_*ier 29

在Postgres中执行此操作的另一种方法是使用该idx功能.

SELECT *
FROM comments
ORDER BY idx(array[1,3,2,4], comments.id)
Run Code Online (Sandbox Code Playgroud)

不要忘记首先创建idx函数,如下所述:http://wiki.postgresql.org/wiki/Array_Index

  • 此功能现在可以在PostgreSQL附带的扩展中使用:http://www.postgresql.org/docs/9.2/static/intarray.html使用`CREATE EXTENSION intarray;`安装它. (11认同)

Clo*_*eto 18

在Postgresql中:

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

  • 我认为你是对的,然后需要有一个开始和结束分隔符,可能是:按位置排序(','|| id :: text ||','in',1,3,2,4, ") (4认同)
  • 嗯...如果`position(id :: text in'123,345,3,678')`它就会出错.id`3`将在id"345"之前匹配,不是吗? (2认同)