1

最も近い日付から最も遠い日付にデータを並べ替えるスコープを作成したいと思います。

たとえば、次の 3 つの値があります。

<Value id: 1, date: '2012-12-20'>
<Value id: 2, date: '2012-12-28'>
<Value id: 3, date: '2012-12-31'>

次に、特定の日付に最も近い日付を並べ替えたいと思います: 2012-12-29.
結果として、次の順序にする必要があります2, 3, 1
私が選択した場合2012-12-30、結果は次のようになります3, 2, 1

私はこのようなことを試しました:

scope :order_by_closest_date, lambda{|time| 
  select("*, (date - DATE('#{time}')) AS time").order("time ASC")
}

しかし、うまくいきません。
情報: Rails 3.2.9 Ruby 1.9.3 Postgresql 9.1.4。
何か案は?

4

2 に答える 2

2

簡単なクエリ

最初の例は、date最初の質問に従って列を処理します。

Rubyの構文についてはよくわかりませんが、適切なSQLステートメントは次のようになります。

SELECT * 
FROM   tbl
ORDER  BY @(date_col - '2012-12-29'::date)

@「絶対値」演算子です。

dateまたはtime識別子として使用しないでください。PostgreSQLでは(いくつかの制限付きで)許可されていますが、これらはSQL標準で予約語であり、混乱を招くエラーメッセージや予期しないエラーにつながる可能性があります。

最高のパフォーマンス

残りtimestampは、コメントの更新に従って列で機能します。

小さなテーブルやアドホッククエリの場合、上記のソリューションで十分です。中規模または大規模のテーブルの場合、パフォーマンスが重要な場合は、より洗練されたアプローチをお勧めします。

Condicio sine qua nonは、または列のインデックスです。このような:datetimestamp

CREATE INDEX tbl_my_timestamp_idx ON tbl(my_timestamp);

インデックスを配置すると、次のクエリが実行され、より大きなテーブルの単純なクエリのパフォーマンスが低下します。

SELECT *
FROM  (
    (
    SELECT *
    FROM   tbl
    WHERE  my_timestamp >= '2012-12-30 11:32'::timestamp
    ORDER  BY my_timestamp
    LIMIT  3
    )

    UNION ALL
    (
    SELECT *
    FROM   tbl
    WHERE  my_timestamp < '2012-12-30 11:32'::timestamp
    ORDER  BY my_timestamp DESC
    LIMIT  3
    )
    ) x
ORDER  BY @extract('epoch' FROM (my_timestamp - '2012-12-28 11:32'::timestamp))
LIMIT  3;
  • UNION ALL-の2本の脚の周りの括弧SELECTはオプションではありません。LIMIT各脚に適用する必要があります。

  • 追加の列で注文する場合は、それをインデックスに反映します。その場合は、複数列のインデックスを使用します。

どうして?

最初のクエリは、条件として式を使用します。Postgresはすべての行の値を計算し、結果で並べ替えて最初の数行を選択する必要があります。小さなテーブルでは問題ありませんが、大きなテーブルでは非常に高価です。O(n) ; nテーブルの行数です。プレーンインデックスは使用できません。さらに、すべての行の中から勝者を並べ替えて選択するための重要なコストがあります。
式にインデックスを作成することもできますが、これは最速ですが、比較するために一定のタイムスタンプに対してのみ機能します。現実的なユースケースとは言えません。

2番目のクエリは、インデックス内のタイムスタンプに従って位置を検索し、次の2行のタプルポインタを順番に読み取り、テーブルから直接(または、9.2のインデックスのみのスキャンでインデックスから直接)フェッチします。ピアがどのように比較されるかわからないので、2回、1回アップ、1回ダウン。ただし、これは2 x O(log(n))通常のbツリールックアップコスト)です。計算は、事前に選択されたいくつかの行に対してのみ実行されます。小さなサンプルから勝者を選ぶことは、取るに足らない一定のコストを伴います。

でテストするだけEXPLAIN ANALYZEです。実際のテーブルでの簡単なテストで、5万行のテーブルで係数1000を取得しました。そして、それはより大きなテーブルのためにスケールアップし続けます。

于 2012-12-29T13:00:30.270 に答える
0

試す

scope :order_by_closest_date, lambda{|time| 
  select("*, DATEDIFF(date,DATE('#{time}')) AS time").order("time ASC")
}
于 2012-12-29T12:51:12.333 に答える