3

はい/いいえの投票質問に対するユーザーの回答のMySQLテーブルがあります。次のようになります。

| user_id    | poll_id  | response  |
|------------|----------|-----------|
|    111     |    1     |   'yes'   |
|    111     |    2     |   'no'    |
|    111     |    3     |   'no'    |
|    222     |    1     |   'yes'   |
|    222     |    2     |   'yes'   |
|    222     |    3     |   'yes'   |
|    333     |    1     |   'no'    |
|    333     |    2     |   'no'    |
|    333     |    3     |   'no'    |

各ユーザーの応答と他のすべてのユーザーの応答との類似性を計算したいと思います。したがって、ユーザー 111 とユーザー 222 は 0.333 類似しており (3 回中 1 回同じ応答があるため)、ユーザー 111 とユーザー 333 は 0.666 回類似しています (3 回中 2 回同じ応答があるため)。

指定された 2 人のユーザーに対して同じ応答の数を返すクエリを作成しました。

SELECT  COUNT(*) AS same_count 
FROM    (
            SELECT  response 
            FROM    results 
            WHERE   user_id = 111
        ) AS t1
    ,   (
            SELECT  response 
            FROM    results 
            WHERE   user_id = 222
        ) AS t2 
WHERE   t1.response = t2.response

現在、すべてのユーザーの情報を取得して、次のような結果を生成する方法を見つけようとしています。

| user_1  |  user_2  |  same_count  |
|---------|----------|--------------|
|  111    |   222    |    0.333     |
|  111    |   333    |    0.666     |
|  222    |   111    |    0.333     |
|  222    |   333    |    0         |
|  333    |   111    |    0.666     |
|  333    |   222    |    0         |

または、可能であれば、冗長な情報なしで:

| user_1  |  user_2  |  same_count  |
|---------|----------|--------------|
|  111    |   222    |    0.333     |
|  111    |   333    |    0.666     |
|  222    |   333    |    0         |

私の直感では、PHP のループを介して一連のクエリを実行することなく、単一の巨大な MySQL クエリとしてこれを行う方法があることがわかりました。誰かが私を正しい方向に向けることができますか?

4

2 に答える 2

3

列 *poll_id* と *user_id* を使用して、同じテーブルで完全外部結合を実行する必要があります。結果が 2 回表示されるため、 alias2テーブルのuser_id値よりも小さいalias1テーブルのuser_id値のみが結果セットに含まれるように条件を指定する必要があることを回避する必要があります。

SQL Fiddle でデモを表示するには、ここをクリックしてください。

スクリプト:

CREATE TABLE poll
(
    user_id     INT         NOT NULL
  , poll_id     INT         NOT NULL
  , response    VARCHAR(10) NOT NULL  
);

INSERT INTO poll (user_id, poll_id, response) VALUES
   (111, 1, 'yes'),
   (111, 2, 'no'),
   (111, 3, 'no'),
   (222, 1, 'yes'),
   (222, 2, 'yes'),
   (222, 3, 'yes'),
   (333, 1, 'no'),
   (333, 2, 'no'),
   (333, 3, 'no');

SELECT      p1.user_id AS user_1
        ,   p2.user_id AS user_2, 
            AVG(CASE 
                    WHEN p1.response = p2.response THEN 1 
                    ELSE 0 
                END) Average_Response
FROM        poll p1
,           poll p2 
WHERE       p1.poll_id = p2.poll_id 
AND         p1.user_id < p2.user_id
GROUP BY    p1.user_id
        ,   p2.user_id;

出力:

USER_1 USER_2 AVERAGE_RESPONSE
------ ------ ----------------
111     222      0.3333
111     333      0.6667
222     333      0
于 2012-04-29T17:37:56.100 に答える
1

これにより、目的の結果が得られるはずです。

SELECT
  t1.user_id AS user_1,
  t2.user_id AS user_2,
  SUM(CASE WHEN t1.response = t2.response THEN 1 ELSE 0 END) / COUNT(1)
    AS same_count
FROM t t1
JOIN t t2 ON ( t2.user_id > t1.user_id AND t2.poll_id = t1.poll_id )
GROUP BY t1.user_id, t2.user_id
ORDER BY user_1, user_2

私のテスト結果:

111 222 0.333333333333333
111 333 0.666666666666667
222 333 0

このCASE部分は、MySQL では ( ) のように簡単に記述できt1.response = t2.responseます。私のバージョンは、他のタイプのデータベースでも動作します。
この部分は、一致するすべてのエントリをカウントし、そのカウントをエントリ数で割ることによって、主なトリックを実行します。

重複を削除しますt2.user_id > t1.user_id(111 - 222、222 - 111)。

于 2012-04-29T17:27:12.860 に答える