IN値リストによるORDER BY


166

PostgreSQL 8.3には、たくさんのコメントを取得する簡単なSQLクエリがあります。句の構成に値のソートされたリストを提供します。INWHERE

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

これはコメントを任意の順序で返しますが、私の場合はのようなIDです1,2,3,4

私は、結果の行は、リストのように並べ替えたいIN構文:(1,3,2,4)
それを達成する方法は?


また、ソートのためだけに新しいテーブルを作成したくない(SQLの純粋さにもかかわらず)。
くるみ割り人形

2
答えはたくさんあります。投票とコメントを受け取って、どちらが勝者かを知ることができますか?おかげですべて:-)
くるみ割り人形

回答:


107

(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

2
@ user80168 IN句に何千もの値がある場合はどうなりますか?何千ものレコードに対してそれをしなければならないからです
kamal

@kamalそのため私は使ったwith ordered_products as (select row_number() OVER (ORDER BY whatever) as reportingorder, id from comments) ... ORDER BY reportingorder
物自体

66

それを見つけるのが非常に難しく、それを広めなければならないからといって、mySQLではこれをはるかに簡単行うことができますが、他のSQLで機能するかどうかはわかりません。

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

3
値のリストは、2つの異なる方法で2 提供する必要があります。それほど単純ではありません。受け入れられた回答は、それを1回だけ必要とします(より冗長な方法であっても)。また、最新のPostgresを使用するとさらに簡単になります(新しい回答で示されています)。また、この質問はPostgresに関するもののようです。
Erwin Brandstetter 2016年

8
ERROR: cannot pass more than 100 arguments to a function
brauliobo 2016年

54

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;
  • 新しい@a_horseをすでに使用してWITH ORDINALITYいることを使用します。

  • サブクエリは必要ありません。テーブルのようにセットを返す関数を使用できます。

  • 一部のクライアントでは、ARRAYコンストラクタの代わりに配列を渡す文字列リテラルを実装する方が簡単な場合があります。

詳細な説明:


46

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

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

1
バインドされた値を使用してこれを行うことができました。つまり... order by id=? desc, id=? desc, id=? desc、それは
正常

postgresで動作し、最善の解決策のようです!
Mike Szyndel、2015年

このソリューションは私にとってはトリックでしたが、このソリューションがパフォーマンスに関してどのように機能しているかを誰かが研究しましたか?句による複数の順序を追加します。したがって、(まだテストしていませんが)Order-IDの数が増えると指数関数的に遅くなる可能性がありますか?これに関するどんな情報でも大歓迎です!
FabianSchöner2016

1
エラー:ターゲットリストに
含める

@Manngo MS SQL。どのバージョンか思い出せません。2012
可能性

43

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

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

これはINWHERE節内のORDER BY節のリスト全体を再び繰り返すわけではないため、これが最良の答えであると言えます...これで、MySQLに似たものを見つけるだけです...
Stijn de Witt

1
私のお気に入りの答えですが、array_positionはbigintでは機能せず、キャストする必要があることに注意してくださいorder by array_position(array[42,48,43], c.id::int);。場合によっては、バグにつながる可能性があります。
aaandre

1
@aaandre以下の鋳造は、(少なくとも、Postgresの12で)正常に動作しているarray_position(array[42, 48, 43]::bigint[], c.id::bigint)、不要切り捨てるようbigintint
Vic

29

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

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

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


11
この関数は、PostgreSQLに付属する拡張機能で利用できるようになりました。postgresql.org/ docs / 9.2 / static / intarray.htmlでインストールしCREATE EXTENSION intarray;ます。
Alex Kahn

1
さらにenable_extension重ねると、Amazon RDSユーザーの場合、ROR移行機能を使用すると、アプリユーザーがrds_superuserグループのメンバーである限り、これをアクティブ化できます。
デイブ

PG 9.6.2にPG :: UndefinedFunction:ERROR:関数IDX(整数[]、整数)が存在しない
Yakob Ubaidi

ありがとう、@ AlexKahnのコメントと組み合わせたときの最良の回答
Andrew

21

Postgresqlの場合:

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

2
うーん...それはバグposition(id::text in '123,345,3,678')。ID 3はIDの前に一致345しますが、いけませんか?
alanjds 14

4
私はあなたが正しいと思いますし、開始と終了の両方の区切り文字が必要になるでしょう、おそらく次のようになります:order by position( '、' || id :: text || '、' in '、1,3,2,4、 ')
Michael Rush

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

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


7
もちろん、コメントできます。SQLには得意なものと得意でないものがあります。SQLはこれが得意ではありません。クエリを作成する言語で結果を並べ替えるだけです。それはあなたの歯の多くの嘆きと歯ぎしりを保存します。SQLはセット指向の言語であり、セットは順序付けられたコレクションではありません。
kquinn 2009年

うーん...それは個人的な経験とテストに基づいていますか?私のテストした経験では、これは注文に非常に効果的なテクニックです。(ただし、「IN(...)」節がなくなるため、受け入れられた回答は全体的に良くなります)。妥当な結果セットのサイズの場合、セットの導出はコストのかかる部分であることに注意してください。数百レコード以下になると、並べ替えは簡単になります。
dkretz 2009年

IN句に何千もの値がある場合はどうなりますか?何千ものレコードに対してそれをしなければならないからです。
kamal

2

これを行うには、おそらく、注文するIDのマッピングを定義する追加の「ORDER」テーブルが必要だと思います(自分の質問に対する回答を効果的に行う)。これは、selectの追加列として使用できますその後、並べ替えることができます。

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


これはそれを行う正しい方法のようです。ただし、その順序表をその場で作成したいと思います。私は答えの1つに定数テーブルを使用することを提案しました。数百または数千のコメントを処理する場合、これはパフォーマンスが良いのでしょうか?
くるみ割り人形

2

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

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

ただし、これがパフォーマンスに優れているかどうかはわかりません。

答えはたくさんあります。投票とコメントを受け取って、どちらが勝者かを知ることができますか?

皆さんありがとう :-)


1
あなたの答えはdepeszとほとんど同じです。c.IDIN(1,3,2,4)を削除するだけです。とにかく彼の方がいいです、彼はJOINを使用します。可能な限りANSI SQLの結合方法を使用し、テーブルコンマテーブルは使用しないでください。私はあなたの答えを注意深く読むべきでした、私は2つの列にエイリアスを付ける方法を理解するのに苦労しています、最初にこれを試しました:(values(1,1)as x(id、sort_order)、(3,2)、 (2,3)、(4,4))としてy。しかし、役に立たなかった:-Dもし私がそれを注意深く読んだなら、あなたの答えは私に手掛かりを与えたかもしれない:-)
Michael Buen

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にはまだ組み込まれていませんが、自分で作成できます(任意の美しさ*)。

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

Michaelに感謝しますが、私のPSQLにはunnest関数が存在しないようで、ドキュメントにもそれについての言及はありません。8.4だけですか?
くるみ割り人形

unnestは8.3にはまだ組み込まれていませんが、自分で実装できます。上記のコードを参照してください
Michael Buen

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で渡されました


このクエリをテストしましたか?
lalithkumar 2017

ここで、idsは{1,2,3,4}のような配列型です。
user6161156

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


-1

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.