3

私はArticlesその have_manyを持っていますMetricsArticlesMetric.name = "score" の場合、特定の Metric.value で並べ替えたいと思います。(Metricさまざまな記事の統計を「名前」と「値」のペアとして記録します。記事には複数のメトリック、さらには複数の「スコア」を含めることができますが、私は最新のものだけを並べることに関心があります。)

class Article
  has_many :metrics

class Metric
  #  name       :string(255)
  #  value      :decimal(, )
  belongs_to :article

これを行うためのスコープを書くのに苦労しています-何かアイデアはありますか? このようなもの?

scope :highest_score, joins(:metrics).order('metrics.value DESC')
                      .where('metrics.name = "score"')

アップデート:

  • 記事には多くの「スコア」がmetricsテーブルに格納されている場合があります (毎週/毎月/毎年計算されるため) が、私は最初に見つかった (最新の) 「スコア」を任意の 1 つの記事に使用することにのみ関心があります。モデルには、MetricDESCending 順序を保証する default_scope があります。

  • 「metrics.value DESC」の引用位置のタイプミスを修正しました。

  • 友達に電話している uber rails ハッカーと話していると、これには未加工の SQL クエリが必要なようです。今、私は頭を抱えています...(それが役立つ場合はPostgresを使用しています。)

ありがとう!

更新 2:

Erwin の素晴らしい SQL クエリの提案のおかげで、動作する生の SQL クエリができました。

SELECT a.*
FROM   articles a
LEFT   JOIN (
   SELECT DISTINCT ON (article_id)
          article_id, value
   FROM   metrics m
   WHERE  name = 'score'
   ORDER  BY article_id, date_created DESC
   ) m ON m.article_id = a.id
ORDER  BY m.value DESC;

article_list_by_desc_score = ActiveRecord::Base.connection.execute(sql)

これは、記事データを表すハッシュの配列を提供します (記事オブジェクトではありません??)。

フォローアップの質問:

これをRailsのアクティブレコードクエリに変換する方法はありますか? (そのため、スコープで使用できます)

ソリューションの更新:

誰かが最終的な ActiveRecord クエリを探している場合 - この質問で私を助けてくれた Mattherick に感謝します。最終的な作業クエリは次のとおりです。

scope :highest_score, joins(:metrics).where("metrics.name"
      => "score").order("metrics.value desc").group("metrics.article_id", 
      "articles.id", "metrics.value", "metrics.date_created")
      .order("metrics.date_created desc")

みんな、ありがとう!

4

1 に答える 1

3

クエリは次のように機能します。

SELECT a.*
FROM   article a
LEFT   JOIN (
   SELECT DISTINCT ON (article_id)
          article_id, value
   FROM   metrics m
   WHERE  name = 'score'
   ORDER  BY article_id, date_created DESC
   ) m ON m.metrics_id = a.metrics_id
ORDER  BY m.value DESC;

まず、サブクエリで記事ごとに「最新」valueを取得します。この関連する回答で使用されている手法の詳細な説明:name = 'score'm

ただし、非常に基本的な誤解の犠牲になっているようです。

しかし、私が興味があるのは、最初に見つかった (最新の) "スコア" をいずれか 1 つの記事に使用することだけです。Metric モデルには、DESCending 順序を保証する default_scope があります。

テーブルには「自然な順序」はありません。では、基準SELECTを明確に定義する必要があります。ORDER BYこのクエリでは、 column を想定していますmetrics.date_created。そのようなものが何もない場合、「最新」を定義する方法がなく、複数の適格な行から任意の選択にフォールバックすることを余儀なくされます。

   ORDER  BY article_id

これは信頼できません。Postgres は選択したとおりに行を選択します。テーブルの更新またはクエリ プランの変更によって変更される場合があります。

次にLEFT JOINテーブルarticleORDER BY valueNULLは最後にソートされるため、修飾値のない記事は最後になります。

注: それほどスマートではない ORM の中には (残念ながら Ruby の ActiveRecord もその 1 つです) id、主キーの名前として説明的でなく、区別のつかないものを使用します。指定しなかった実際の列名に適応する必要があります。

パフォーマンス

まともなはずです。Postgres に関する限り、これは「単純な」クエリです。table の部分的な複数列インデックスをmetrics使用すると、より高速になります。

CREATE INDEX metrics_some_name_idx ON metrics(article_id, date_created)
WHERE name = 'score';

この順序で列。PostgreSQL 9.2+ では、列の値を追加して、インデックスのみのスキャンを可能にすることができます。

CREATE INDEX metrics_some_name_idx ON metrics(article_id, date_created, value)
WHERE name = 'score';
于 2013-05-06T01:30:39.920 に答える