按 IN 值列表排序

问题描述 投票:0回答:17

我在 PostgreSQL 8.3 中有一个简单的 SQL 查询,可以获取一堆评论。我在 IN 子句中为

WHERE
构造提供了一个
sorted
值列表:

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

这会以任意顺序返回评论,在我看来,这些评论恰好是像

1,2,3,4
这样的id。

我希望结果行像

IN
构造中的列表一样排序:
(1,3,2,4)

如何实现?

sql postgresql sql-order-by sql-in
17个回答
132
投票

您可以使用(在 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

117
投票

在 Postgres 9.4 或更高版本中使用

WITH ORDINALITY

SELECT c.*
FROM   comments c
JOIN   unnest('{1,3,2,4}'::int[]) WITH ORDINALITY t(id, ord) USING (id)
ORDER  BY t.ord;
  • 不需要子查询,我们可以像直接使用表一样使用设置返回函数 - 又名“表函数”。

  • 传递数组的字符串文字而不是ARRAY构造函数可能更容易在某些客户端上实现。

  • 为了方便起见(可选),匹配我们要连接的列名称(示例中的“id”),这样我们就可以使用短的

    USING
    子句进行连接,并且只在结果中获得连接列的单个实例。

  • 适用于any输入类型。如果您的键列类型为

    text
    ,请提供类似
    '{foo,bar,baz}'::text[]
    的内容。

详细说明:


74
投票

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

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

72
投票

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

或者没有派生表的情况下更紧凑一点:

select c.*
from comments c
  join 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);

50
投票

我认为这种方式更好:

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

35
投票

在 Postgresql 中:

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

31
投票

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

idx
函数。

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

不要忘记首先创建

idx
函数,如下所述:http://wiki.postgresql.org/wiki/Array_Index


3
投票

在进一步研究这个问题时,我发现了这个解决方案:

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

然而,这看起来相当冗长,并且对于大型数据集可能会出现性能问题。 谁能评论一下这些问题?


2
投票

为此,我认为您可能应该有一个额外的“ORDER”表,它定义了 ID 到订单的映射(有效地执行您对自己问题的回答所说的操作),然后您可以将其用作选择上的附加列然后您可以对其进行排序。

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


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

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

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

但我再次不确定这是否有效。

我现在已经得到了很多答案。我可以获得一些投票和评论,以便我知道哪一个是获胜者!

谢谢大家:-)


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;

[编辑]

unnest 尚未内置于 8.3 中,但您可以自己创建一个(any* 的美妙之处):

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

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;

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中通过


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


-4
投票

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

© www.soinside.com 2019 - 2024. All rights reserved.