3

フィールドを持つ本のテーブルがありauthor_idます。

すべての著者の本を 1 つだけ含む本の配列を取得したいと思います。最新のupdated_atフィールドを持つもの。

Postgresのような単純なアプローチの問題Books.all.group('author_id')は、要求されたすべてのフィールドがそのGROUP BYブロックに必要であることです。( https://stackoverflow.com/a/6106195/1245302を参照)

しかし、他のすべてのフィールドを無視して、すべての Book オブジェクトを著者ごとに 1 つずつ、最近のオブジェクトを取得する必要があります。DBMSが必要な行を正確に見つけるのに十分なデータがあるように思えます.少なくとも、GROUP BYブロック内の他のフィールドなしで自分でそれを行うことができました. :)

簡単な Rails 3 + Postgres (バージョン < 9) または SQL 実装に依存しない方法はありますか?

UPDATE Postgresの素敵なソリューション:

books.unscoped.select('DISTINCT ON(author_id) *').order('author_id').order('updated_at DESC') 

しかし!まだ問題が残っています – 結果はauthor_id最初に並べ替えられupdated_atますが、同じauthor_id-s 内で並べ替える必要があります (見つけるには、最近の本の著者の上位 10 人と言います)。

また、Postgres では、クエリのORDER BY引数の順序を変更することはできません:(DISTINCT

4

2 に答える 2

1

私は Rails を知りませんが、目的の SQL を示して、適切な SQL を生成する方法を理解できるようになれば幸いです。

SELECT DISTINCT ON (author_id) *
  FROM Books
  ORDER BY author_id, updated_at DESC;

このDISTINCT ON (author_id)部分は、結果の列リストの一部と混同しないでください。author_id ごとに 1 つの行があることを示しているだけです。句のリストは、そのようなクエリDISTINCT ONの句の先頭部分である必要があり、保持される行は、句の残りORDER BYの部分に基づいて最初にソートされる行です。ORDER BY

行数が多い場合、この方法でクエリを作成すると、またはウィンドウ関数に基づくソリューションよりもはるかに高速になりGROUP BY、多くの場合、1 桁以上速くなります。ただし、これは PostgreSQL の拡張機能です。そのため、移植性を意図したコードでは使用しないでください。

この結果セットを別のクエリ内で使用する場合 (たとえば、最近更新された 10 人の著者を検索する場合)、それを行うには 2 つの方法があります。次のようにサブクエリを使用できます。

SELECT *
  FROM (SELECT DISTINCT ON (author_id) *
          FROM Books
          ORDER BY author_id, updated_at DESC) w
  ORDER BY updated_at DESC
  LIMIT 10;

次のように CTE を使用することもできます。

WITH w AS (
  SELECT DISTINCT ON (author_id) *
    FROM Books
    ORDER BY author_id, updated_at DESC)
SELECT * FROM w
  ORDER BY updated_at DESC
  LIMIT 10;

CTE に関する通常のアドバイスはここでも当てはまります。クエリを記述する別の方法がない場合、または最適化バリアを導入してプランナーを強制する必要がある場合にのみ、CTE を使用してください。計画は非常に似ていますが、中間結果を CTE スキャンに渡すと、オーバーヘッドが少し増えます。私の小さなテスト セットでは、CTE フォームは 17% 遅くなります。

于 2012-04-13T19:30:05.893 に答える
0

これは遅れていますが、デフォルトの順序のオーバーライド/リセットに関する質問に答えて、.reorder(nil).order(:whatever_you_want_instead)

(コメントできないので、今のところ回答として投稿してください)

于 2017-03-17T19:18:44.860 に答える