6

私はPostgreSQL9.1でこれを行おうとしています:

SELECT m.id, vm.id, vm.value
FROM m
LEFT JOIN vm ON vm.m_id = m.id and vm.variation_id = 1
ORDER BY lower(trim(vm.value)) COLLATE "C" ASC LIMIT 10 OFFSET 120

結果は次のとおりです。

 id |  id | value
----+-----+---------------
504 | 511 | "andr-223322"
506 | 513 | "andr-322223"
824 | 831 | "angHybrid"
866 | 873 | "Another thing"
493 | 500 | "App update required!"
837 | 844 | "App update required!"
471 | 478 | "April"
905 | 912 | "Are you sure you want to delete this thing?"
 25 |  29 | "Assignment"
196 | 201 | "AT ADDRESS"

さて、同じクエリを次のように実行しましょうOFFSET 130

 id |  id | value
----+-----+---------------
196 | 201 | "AT ADDRESS"
256 | 261 | "Att Angle"
190 | 195 | "Att Angle"
273 | 278 | "Att Angle:"
830 | 837 | "attAngle"
475 | 482 | "August"
710 | 717 | "Averages"
411 | 416 | "AVG"
692 | 699 | "AVG SHAPE"
410 | 415 | "AVGs"

アイテムが再び表示されますAT ADDRESSが、最初は!!!

実際には、vmテーブルには次の2つの項目が含まれています。

 id | m_id | value
----+------+---------------
201 |  196 | "AT ADDRESS"
599 |  592 | "At Address"

私は回避策でこの状況を治します:

(lower(trim(vm.value)) || vm.id)

しかし、何地獄??? !!! なぜ回避策を使用する必要があるのですか?

4

1 に答える 1

10

罵倒しても、この動作を定義するSQL標準は変更されません。
で指定されていない限り、行の順序は未定義ORDER BYです。ドキュメントごと

並べ替えが選択されていない場合、行は指定されていない順序で返されます。その場合の実際の順序は、スキャンおよび結合プランのタイプとディスク上の順序によって異なりますが、信頼してはなりません。特定の出力順序は、ソートステップが明示的に選択されている場合にのみ保証されます。

これら2つのピアの順序を(ソート順で)定義しなかったため、次のようになります。

 id | m_id | value
----+------+---------------
201 |  196 | "AT ADDRESS"
599 |  592 | "At Address"

..任意の順序を取得します-Postgresに便利なものは何でも。のクエリは、LIMIT多くの場合、異なるクエリプランを使用します。これにより、異なる結果を説明できます。

修理:

ORDER BY lower(trim(vm.value)) COLLATE "C", vm.id;

または(おそらくもっと意味があります-おそらく既存のインデックスに合わせて調整することもできます):

ORDER BY lower(trim(vm.value)) COLLATE "C", vm.value, vm.id;

(これは、COLLATE "C"ここでの使用とは関係あり
ません。)この目的で連結しないでください。これははるかに高価であり、インデックスを使用できなくなる可能性があります(正確な式にインデックスがない場合)。リスト内の前の式がORDER BYあいまいさを残したときに開始する別の式を追加します。

また、そこにあるので、一致しないLEFT JOIN行は、現在のすべての式に対してnull値を持ちます。それらは最後に来て、それ以外の場合は任意にソートされます。全体的に安定したソート順が必要な場合は、それに対処する必要もあります。好き:mvmORDER BY

ORDER BY lower(trim(vm.value)) COLLATE "C", vm.id, m.id;

傍白

なぜ二重引用符を保存するのですか?コストのかかるノイズのようです。それらがない方が良いかもしれません。必要に応じて、いつでも出力に引用符を追加できます。

多くのクライアントは、1つの結果で同じ列名を複数回処理することはできません。少なくとも1つの列の列エイリアスが必要idですSELECT m.id AS m_id, vm.id AS vm_id ...。列の「id」がそもそもアンチパターンである理由を示します。

于 2013-03-01T16:59:34.813 に答える