web-dev-qa-db-ja.com

PostgreSQLの "ORDER BY ... USING"句

ORDER BY句は、PostgreSQLのドキュメントで次のように説明されています。

ORDER BY expression [ ASC | DESC | USING operator ] [ NULLS { FIRST | LAST } ] [, ...]

誰かがUSING operatorの使用例を教えてもらえますか?結果セットの交互の順序を取得することは可能ですか?

38
markus

非常に簡単な例は次のとおりです。

_> SELECT * FROM tab ORDER BY col USING <
_

しかし、これは従来の_ORDER BY col ASC_では得られないものなので、退屈です。

また、標準のカタログでは、奇妙な比較関数/演算子についてエキサイティングなことは何も述べられていません。それらのリストを取得できます。

_    > SELECT amoplefttype::regtype, amoprighttype::regtype, amopopr::regoper 
      FROM pg_am JOIN pg_amop ON pg_am.oid = pg_amop.amopmethod 
      WHERE amname = 'btree' AND amopstrategy IN (1,5);
_

integerdateなどのプリミティブ型には主に_<_および_>_関数があり、配列やベクトルなどにはさらにいくつかあることに気づくでしょう。これらの演算子は、カスタムの注文を取得するのに役立ちません。

カスタム順序付けが必要なmostの場合、... ORDER BY somefunc(tablecolumn) ...のようなものを使用して回避できます。ここで、somefuncは値を適切にマップします。これはすべてのデータベースで機能するため、これも最も一般的な方法です。単純なものについては、カスタム関数の代わりに式を書くこともできます。

ギアの切り替え

_ORDER BY ... USING_はいくつかの場合に意味があります:

  • 順序付けはあまり一般的ではないため、somefuncトリックは機能しません。
  • プリミティブ以外のタイプ(pointcircle、または虚数など)を使用していて、奇妙な計算でクエリを繰り返したくない場合。
  • 並べ替えるデータセットが非常に大きいため、インデックスによるサポートが必要な場合や、必要な場合さえあります。

ここでは、複雑なデータ型に焦点を当てます。多くの場合、合理的な方法でそれらを並べ替える方法は複数あります。良い例はpointです:(0,0)までの距離、または最初にx、次にyの順に並べるか、またはyまたはその他の必要なものだけ。

もちろん、PostgreSQL haspointの定義済み演算子:

_    > CREATE TABLE p ( p point );
    > SELECT p <-> point(0,0) FROM p;
_

しかしnoneそれらのうち、デフォルトで_ORDER BY_に対して使用可能と宣言されています(上記を参照):

_    > SELECT * FROM p ORDER BY p;
    ERROR:  could not identify an ordering operator for type point
    TIP:  Use an explicit ordering operator or modify the query.
_

pointの単純な演算子は、「下」および「上」の演算子_<^_および_>^_です。彼らは単にポイントのy部分を比較します。だが:

_    >  SELECT * FROM p ORDER BY p USING >^;
    ERROR: operator > is not a valid ordering operator
    TIP: Ordering operators must be "<" or ">" members of __btree__ operator families.
_

_ORDER BY USING_には、定義されたセマンティクスの演算子が必要です。明らかに、これは2項演算子でなければならず、引数と同じ型を受け入れ、ブール値を返す必要があります。また、推移的である必要があると思います(a <bおよびb <cの場合、a <c)。さらに要件がある場合があります。しかし、これらすべての要件は、適切なbtree-index順序付けにも必要です。これは、btreeへの参照を含む奇妙なエラーメッセージについて説明しています。

_ORDER BY USING_を定義するには、演算子1つだけでなく演算子クラス演算子ファミリも必要です。 1つのcouldは1つの演算子のみでソートを実装しますが、PostgreSQLは効率的にソートして比較を最小限に抑えようとします。したがって、1つだけを指定した場合でも、いくつかの演算子が使用されます。他の演算子は特定の数学的制約に従う必要があります-推移性についてはすでに説明しましたが、他にもあります。

ギアの切り替え

適切なものを定義しましょう:y部分のみを比較するポイントの演算子。

最初のステップは、btreeインデックスアクセスメソッドで使用できるカスタムオペレーターファミリーを作成することです。 参照

_    > CREATE OPERATOR FAMILY xyzfam USING btree;   -- superuser access required!
    CREATE OPERATOR FAMILY
_

次に、2つのポイントを比較するときに-1、0、+ 1を返すコンパレーター関数を提供する必要があります。この関数[〜#〜]] [〜#〜]は内部的に呼び出されます!

_    > CREATE FUNCTION xyz_v_cmp(p1 point, p2 point) RETURNS int 
      AS $$BEGIN RETURN btfloat8cmp(p1[1],p2[1]); END $$ LANGUAGE plpgsql;
    CREATE FUNCTION
_

次に、ファミリの演算子クラスを定義します。 マニュアルを参照 番号の説明については==。

_    > CREATE OPERATOR CLASS xyz_ops FOR TYPE point USING btree FAMILY xyzfam AS 
        OPERATOR 1 <^ ,
        OPERATOR 3 ?- ,
        OPERATOR 5 >^ ,
        FUNCTION 1 xyz_v_cmp(point, point) ;
    CREATE OPERATOR CLASS
_

このステップでは、いくつかの演算子と関数を組み合わせ、それらの関係と意味も定義します。たとえば、_OPERATOR 1_の意味:これは_less-than_テストの演算子です。

演算子_<^_および_>^_を_ORDER BY USING_で使用できるようになりました。

_> INSERT INTO p SELECT point(floor(random()*100), floor(random()*100)) FROM generate_series(1, 5);
INSERT 0 5
> SELECT * FROM p ORDER BY p USING >^;
    p    
---------
 (17,8)
 (74,57)
 (59,65)
 (0,87)
 (58,91)
_

出来上がり-yでソート。

要約すると:_ORDER BY ... USING_は、PostgreSQLの内部で興味深い外観です。しかし、データベーステクノロジーの特定の領域veryで作業しない限り、すぐに必要になるものはありません。

別の例は Postgres docs。 にあり、例のソースコードは here および here です。この例では、演算子の作成方法も示しています。

44
A.H.

サンプル:

CREATE TABLE test
(
  id serial NOT NULL,
  "number" integer,
  CONSTRAINT test_pkey PRIMARY KEY (id)
)

insert into test("number") values (1),(2),(3),(0),(-1);

select * from test order by number USING > //gives 3=>2=>1=>0=>-1

select * from test order by number USING < //gives -1=>0=>1=>2=>3

したがって、descおよびascと同等です。しかし、あなたはあなた自身の演算子を使うかもしれません、それはUSINGの本質的な機能です

4
J0HN

いい答えですが、「USING」の本当に価値あるケースについては言及されていません。

デフォルト以外の演算子ファミリを使用してインデックスを作成した場合、たとえば、varchar_pattern_opsの代わりに~>~~<~~>=~<、...) >>=インデックスに基づいて検索し、order by句でインデックスを使用する場合は、適切な演算子でUSINGを指定する必要があります。

これは、次の例で説明できます。

CREATE INDEX index_words_Word ON words(Word text_pattern_ops); 

この2つのクエリを比較してみましょう。

SELECT * FROM words WHERE Word LIKE 'o%' LIMIT 10;

そして

SELECT * FROM words WHERE Word LIKE 'o%' ORDER BY Word LIMIT 10;

50万語のDBでは、実行の違いはほぼ100倍です。また、C以外のロケールでは結果が正しくない場合があります。

これはどうして起こりましたか?

LIKEおよびORDER BY句を使用して検索を行う場合、実際には次の呼び出しを行います。

SELECT * FROM words WHERE Word ~>=~ 'o' AND Word ~<~'p' ORDER BY Word USING < LIMIT 10;

インデックスは~<~演算子を考慮して作成されているため、PGは特定のORDER BY句で特定のインデックスを使用できません。正しい処理を行うには、クエリを次の形式に書き直す必要があります。

SELECT * FROM words WHERE Word ~>=~ 'o' AND Word ~<~'p' ORDER BY Word USING ~<~ LIMIT 10;

または

SELECT * FROM words WHERE Word LIKE 'o%' ORDER BY Word USING ~<~ LIMIT 10;

オプションで、ORDER BY句の任意の式の後に、Word ASC(昇順)またはDESC(降順)というキーを追加できます。指定しない場合、デフォルトでASCが想定されます。または、特定の順序付け演算子名をUSING句で指定できます。順序付け演算子は、一部のBツリー演算子ファミリーの「以下」または「以上」のメンバーである必要があります。 ASCは通常USING <と同等であり、DESCは通常USING>と同等です。

PostgreSQL 9.

このように見えるかもしれません(現在、これを確認するためのpostgresはありませんが、後で確認します)。

SELECT Name FROM Person
ORDER BY NameId USING >
0
VoodooChild