web-dev-qa-db-ja.com

IN値リストによるORDER BY

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)のリストのように、結果の行を並べ替えたいです。
それを達成するには?

136
nutcracker

(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
90
user80168

見つけるのが非常に難しく、広がらなければならないという理由だけで: mySQLでこれをもっと簡単に行うことができます 他のSQLで動作します。

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

私はこの方法が良いと思います:

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
42

Postgres9.4以降では、これはおそらく最も簡単で最速です

SELECT c.*
FROM   comments c
JOIN   unnest('{1,3,2,4}'::int[]) WITH ORDINALITY t(id, ord) USING (id)
ORDER  BY t.ord;

詳細な説明:

39

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が使用されるため、値のリストは1回だけ指定する必要があります。それが重要でない場合、これは次のように書くこともできます。

select c.*
from comments c
where id in (42,48,43)
order by array_position(array[42,48,43], c.id);
30

Postgresでそれを行う別の方法は、idx関数を使用することです。

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

ここで説明するように、最初にidx関数を作成することを忘れないでください。 http://wiki.postgresql.org/wiki/Array_Index

28
Carl Mercier

Postgresqlの場合:

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

これをさらに調査すると、この解決策が見つかりました:

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

ただし、これはかなり冗長に見えるため、大規模なデータセットではパフォーマンスの問題が発生する可能性があります。これらの問題について誰でもコメントできますか?

4
nutcracker

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
2
Michael Buen

これを行うには、おそらく、注文へのIDのマッピングを定義する追加の「ORDER」テーブルが必要であると思います(あなた自身の質問への応答を効果的に実行します)その後、ソートできます。

そのようにして、データベース内で必要な順序を明示的に記述します。

2
Paul Sonier
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,')
1
Hafthor

すでに言ったことについて視覚的な印象を取得できます。たとえば、いくつかのタスクを含むテーブルがあります。

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)

秘Theは、各ステータス値にintergerを与え、リストを数字順に並べることです。

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

0
Manuel
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
user6161156
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ではunnestはまだ組み込まれていませんが、自分で作成することができます(任意の美しさ*):

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
Michael Buen

動作し、定数テーブルを使用する別のソリューションがあります( 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
nutcracker

私が思うにシーケンスを使用するバージョンに比べてわずかな改善:

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
Jon Erdman

私は、「それをしないでください」または「SQLはそれが得意ではない」と言う他のすべてのポスターに同意します。コメントのファセットでソートする場合は、別の整数列をテーブルの1つに追加して、ソート基準を保持し、その値でソートします。例: "ORDER BY comments.sort DESC"毎回異なる順序でこれらをソートしたい場合...この場合、SQLはあなたのためではありません。

0
Trey