0

現在、アイテムのスコア表を作成しています。

各項目にはスコアがあるため、データベース (postgres) は項目をスコアでソートしてユーザーに返すことができます。

現在、製品の合計スコアは次の式で決定されます。

  • 鮮度スコア(プロセスAで計算)
  • 人気スコア(プロセスBで計算)
  • 関連性スコア (プロセス C で計算)

合計 = 0.5 * 鮮度 + 0.25 * 人気 + 0.25 * 関連性

プロセス A、B、C は数時間にわたって実行され、(item_id、スコア、タイプ) が生成されます。ここで、タイプは「新鮮」、「人気」、または「関連性」のいずれかです。

これらの値は異なるプロセスによって生成されるため、これらの値を保持する必要があることに注意してください。

実行できるようにするために何をする必要がありますかSELECT * FROM items JOIN scores ON items.id == scores.item_id ORDER BY <total_score ??> DESC LIMIT 10 OFFSET 0;

編集

明白な答えはtype = total、すべてのアイテムに対して別のプロセスを生成させることです。これは機能しますが、これらのスコアのいずれかが変更されるたびに合計を更新する必要があるため、面倒です。さらに、データ ストレージを 25% から 100% に増やすことができます。これを組み込むにはかなりの手間がかかるため、これを最適なソリューションとは考えていません。

アップデート

これは私のスコア表です:

    Column     |            Type             |                         Modifiers                         | Storage  | Description
---------------+-----------------------------+-----------------------------------------------------------+----------+-------------
 created_at    | timestamp without time zone |                                                           | plain    |
 updated_at    | timestamp without time zone |                                                           | plain    |
 id            | integer                     | not null default                             | plain    |
 score         | double precision            | not null                                                  | plain    |
 type          | character varying           | not null                                                  | extended |
4

4 に答える 4

2

すべてのスコア タイプを計算に使用できるように、合計スコアの式で並べ替え、各スコア行に個別に結合します。

SELECT * FROM items
LEFT JOIN scores f ON items.id = f.item_id and type = 'freshness'
LEFT JOIN scores p ON items.id = p.item_id and type = 'popularity'
LEFT JOIN scores r ON items.id = r.item_id and type = 'relevance'
ORDER BY 
    0.5 * COALESCE(f.score, 0) +
    0.25 * COALESCE((p.score, 0) +
    0.25 * COALESCE(r.score) DESC
LIMIT 10 OFFSET 0

合計を保存する必要はありません。

の使用に注意してくださいLEFT JOIN。これは、特定のスコア タイプのないアイテムが返されることを意味します。以前COALESCE()は、欠落しているスコア タイプにはゼロのスコアを付けていました。

これがパフォーマンスの問題を引き起こすと思うかもしれませんが、私はそうは思いません。合計を保存することを検討する前に、それを試してパフォーマンスを確認してください。これはパフォーマンス上の理由のみであり、したがって「早期に最適化」する場合 (避けるべきアンチパターン) になります。

于 2013-04-16T20:49:02.777 に答える
2

ここで説明されているように、仮想列を使用してこれを行う別のクールな方法を次に示します。

まず、各項目のスコアを集計するビューを作成します。

CREATE OR REPLACE VIEW vw_scores_rollup AS
SELECT id,
  SUM(CASE WHEN type = 'freshness' THEN score ELSE 0 END) AS freshness,
  SUM(CASE WHEN type = 'popularity' THEN score ELSE 0 END) AS popularity,
  SUM(CASE WHEN type = 'relevance' THEN score ELSE 0 END) AS relevance
FROM scores
GROUP BY id;

次に、この関数はソース テーブル/ビューを引数として受け取ります。

CREATE OR REPLACE FUNCTION total(vw_scores_rollup) RETURNS numeric AS
$BODY$
  SELECT 0.5 * COALESCE($1.freshness, 0) + 0.25 * COALESCE($1.popularity, 0) + 0.25 * COALESCE($1.relevance, 0);
$BODY$
  LANGUAGE sql;

アクセスするために:

SELECT *, s.total
FROM items i
JOIN vw_scores_rollup s USING (id)
ORDER BY s.total DESC
LIMIT 10 OFFSET 0;

これは巧妙なトリックであり、合計にアクセスするための簡単な方法を提供します。

于 2013-04-16T21:09:28.250 に答える