5

現在、回答された質問に基づいてユーザーを照合する webapp を作成しています。たった 1 つのクエリでマッチング アルゴリズムを実現し、2 人のユーザー間の一致率を計算するのに 8.2 ミリ秒かかるように調整しました。しかし、私の webapp はユーザーのリストを取得し、このクエリを実行してリストを反復処理する必要があります。5000 人のユーザーの場合、ローカル マシンで 50 秒かかりました。user_id を含む 1 つの列と計算された一致を含む 1 つの列を返す 1 つのクエリにすべてを入れることは可能ですか? または、ストアド プロシージャはオプションですか?

私は現在 MySQL を使用していますが、必要に応じてデータベースを切り替えます。

スキーマとデータに興味がある人のために、SQLFiddle を作成しました: http://sqlfiddle.com/#!2/84233/1

そして私の一致するクエリ:

SELECT COALESCE(SQRT( (100.0*as1.actual_score/ps1.possible_score) * (100.0*as2.actual_score/ps2.possible_score) ) - (100/ps1.commonquestions), 0) AS perc
  FROM (SELECT SUM(imp.value) AS actual_score 
      FROM user_questions AS uq1
      INNER JOIN importances imp ON imp.id = uq1.importance
      INNER JOIN user_questions uq2 ON uq2.question_id = uq1.question_id AND uq2.user_id = 101
        AND (uq1.accans1 = uq2.answer_id 
          OR uq1.accans2 = uq2.answer_id
          OR uq1.accans3 = uq2.answer_id
          OR uq1.accans4 = uq2.answer_id)
      WHERE uq1.user_id = 1) AS as1, 
  (SELECT SUM(value) AS possible_score, COUNT(*) AS commonquestions
      FROM user_questions AS uq1
      INNER JOIN importances ON importances.id = uq1.importance
      INNER JOIN user_questions uq2 ON uq1.question_id = uq2.question_id AND uq2.user_id = 101
      WHERE uq1.user_id = 1) AS ps1,
  (SELECT SUM(imp.value) AS actual_score 
      FROM user_questions AS uq1
      INNER JOIN importances imp ON imp.id = uq1.importance
      INNER JOIN user_questions uq2 ON uq2.question_id = uq1.question_id AND uq2.user_id = 1
        AND (uq1.accans1 = uq2.answer_id 
          OR uq1.accans2 = uq2.answer_id
          OR uq1.accans3 = uq2.answer_id
          OR uq1.accans4 = uq2.answer_id)
      WHERE uq1.user_id = 101) AS as2, 
  (SELECT SUM(value) AS possible_score 
      FROM user_questions AS uq1
      INNER JOIN importances ON importances.id = uq1.importance
      INNER JOIN user_questions uq2 ON uq1.question_id = uq2.question_id AND uq2.user_id = 1
      WHERE uq1.user_id = 101) AS ps2
4

1 に答える 1

1

私は退屈していたので: これはあなたのクエリの書き直されたバージョンです - あなたのスキーマの PostgreSQL ポートに基づいて - 一度にすべてのユーザーペアリングの一致を計算します:

http://sqlfiddle.com/#!12/30524/6

確認したところ、ユーザー ペア (1,5) に対して同じ結果が得られました。

WITH
userids(uid) AS (
    select distinct user_id from user_questions
),
users(u1,u2) AS (
    SELECT u1.uid, u2.uid FROM userids u1 CROSS JOIN userids u2 WHERE u1 <> u2
),
scores AS (
        SELECT
            sum(CASE WHEN uq2.answer_id IN (uq1.accans1, uq1.accans2, uq1.accans3, uq1.accans4) THEN imp.value ELSE 0 END) AS actual_score,
            sum(imp.value) AS potential_score,
            count(1) AS common_questions,
            users.u1,
            users.u2
        FROM user_questions AS uq1
        INNER JOIN importances imp ON imp.id = uq1.importance
        INNER JOIN user_questions uq2 ON uq2.question_id = uq1.question_id
        INNER JOIN users ON (uq1.user_id=users.u1 AND uq2.user_id=users.u2)
        GROUP BY u1, u2
),
score_pairs(u1,u2,u1_actual,u2_actual,u1_potential,u2_potential,common_questions) AS (
    SELECT s1.u1, s1.u2, s1.actual_score, s2.actual_score, s1.potential_score, s2.potential_score, s1.common_questions
    FROM scores s1 INNER JOIN scores s2 ON (s1.u1 = s2.u2 AND s1.u2 = s2.u1)
    WHERE s1.u1 < s1.u2
)
SELECT
    u1, u2, 
    COALESCE(SQRT( (100.0*u1_actual/u1_potential) * (100.0*u2_actual/u2_potential) ) - (100/common_questions), 0) AS "match"
FROM  score_pairs;

これを MySQL に移植できない理由はありません。CTE は読みやすくするためだけに存在し、FROM (SELECT ...). 句はなくWITH RECURSIVE、CTE は複数の他の CTE から参照されません。入れ子になったクエリはちょっと怖いかもしれませんが、それは書式設定の問題にすぎません。

変更点:

  • 個別のユーザーのセットを生成する
  • その個別のユーザーのセットに自己参加して、ユーザーの組み合わせのセットを作成します
  • 次に、スコアクエリでそのペアリングのリストを結合して、スコアのテーブルを作成します
  • possiblescore1 と possiblescore2、actualscore1 と actualscore2 の大部分が重複するクエリを組み合わせて、スコア テーブルを作成します。
  • 次に、最終的な外部クエリで要約します

クエリを最適化していません。書かれているように、私のシステムでは5ミリ秒で実行されます。より大きなデータでは、その一部を再構築するか、いくつかの CTE 句をSELECT ... INTO TEMPORARY TABLE一時テーブル作成ステートメントに変換するなどのトリックを使用して、クエリを実行する前にインデックスを作成する必要がある可能性があります。

users行セットの生成を CTE からFROMサブクエリ節に移動することもできますscores。これWITHは、句間の最適化フェンスとして動作する必要があるためです。そのため、データベースは行を具体化する必要があり、句を上下にプッシュするなどのトリックを使用できません。

于 2012-10-21T12:20:14.657 に答える