1

私は長い間この問題に苦しんでおり、解決方法がわかりません。説明するのは難しいので、しばらくお待ちください。次の 2 つのテーブルがあります。

表「ユーザー」

UserId PK
Gender

表「フォーム」

FormId PK
UserId1 FK
UserId2 FK
Type

フォームは常に 2 人のユーザーに関連付けられていますが、すべてのユーザーがフォームに関連付けられているわけではありません。ここで、フォームに関連するユーザーの特定の性別のみをカウントしたいと考えています。

結果として、私はsthを持ちたいと思っています。このような:

# |  Gender | GenderCount
1 |  male   |     43
2 |  female |     12
3 |  trans  |     2

次の SQL スクリプトを試しましたが、結果が明確ではありません (すべての GenderCount の合計が実際のユーザー数よりも大きくなっています)。

SELECT u.Gender AS 'Gender', COUNT(u.Gender) AS 'GenderCount' 
FROM Users u, Forms f 
WHERE ((f.UserId1 = u.UserId) 
    OR (f.UserId2 = u.UserId)) 
AND (Type = 'Foo') 
GROUP BY Gender 
ORDER BY GenderCount 
DESC

これを解決するためのヒントはありますか?

4

4 に答える 4

2

あなたが望むものを見てみましょう:

  • 性別ごとに何人が任意のフォームに答えましたか?
  • 注: 記入したフォームの数に関係なく、各ユーザーは 1 回だけカウントされます。

このように表現すると、少なくとも疑似コードでは、答えはかなり明白になります。

SELECT
    u.Gender,
    COUNT(u.Gender)
FROM
    Users u
WHERE
    [User has answered a form]
GROUP BY
    u.Gender

ユーザーがフォームに回答したかどうかを判断する最も簡単な方法は、使用されている SQL の特定のフレーバーによって異なります。サブクエリを使用する必要があります。アクセス方法にはいくつかのオプションがあります。

INが最も一般的な方法です。

SELECT
    u.Gender        Gender,
    COUNT(u.Gender) GenderCount
FROM
    Users u
WHERE
    u.id IN (
        SELECT f.UserId1 user_id FROM Forms f WHERE Type = 'Foo'
        UNION
        SELECT f.UserId2 user_id FROM Forms f WHERE Type = 'Foo'
    )
GROUP BY
    Gender
ORDER BY
    GenderCount DESC

利用可能な場合EXISTSは、より自然に読みやすく、より高速な場合もあります。

SELECT
    u.Gender        Gender,
    COUNT(u.Gender) GenderCount
FROM
    Users u
WHERE
    EXISTS(
        SELECT '1'
        FROM Forms f
        WHERE
            (f.UserId1 = u.id OR f.UserId2 = u.id)
            AND Type = 'Foo'
    )
GROUP BY
    Gender
ORDER BY
    GenderCount DESC

速度について: クエリ オプティマイザは、余分な行を不必要に選択しないように、可能な場合は変換INすることがよくあります。EXISTSただし、複数の列を使用するには、ORまたは のいずれかが必要なUNIONので、この場合でもきれいかもしれません。つまり、どちらORUNIONインデックスをうまく処理しません。

于 2012-09-27T22:16:02.643 に答える
1
SELECT u1.Gender AS 'Gender', COUNT(*) AS 'GenderCount'
FROM
    Users u1 
        INNER JOIN 
    (SELECT DISTINCT u.UserId
    FROM 
        Users u
            INNER JOIN Forms f ON ((f.UserId1 = u.UserId) 
                                OR (f.UserId2 = u.UserId))
                                AND (f.Type = 'Foo')) T ON T.UserId = u1.UserId
GROUP BY Gender 
ORDER BY GenderCount DESC
于 2012-09-27T20:15:25.260 に答える
1

ユーザーごとに複数の行を生成している結合をスキップします。

SELECT Gender, COUNT(Gender) AS 'GenderCount' 
FROM Users
WHERE UserId IN (SELECT UserId1 FROM Forms WHERE Type = 'Foo' 
                 UNION 
                 SELECT UserId2 FROM Forms WHERE Type = 'Foo')
GROUP BY Gender 
ORDER BY GenderCount DESC

または、UNION を避けたい場合 (このシナリオでは完全に有効です)、次のように OR を使用できます。

SELECT Gender, COUNT(Gender) AS 'GenderCount' 
FROM Users
WHERE UserId IN (SELECT UserId1 FROM Forms WHERE Type = 'Foo')
   OR UserId IN (SELECT UserId2 FROM Forms WHERE Type = 'Foo')
GROUP BY Gender 
ORDER BY GenderCount DESC

他の人が指摘したように、JOIN を使用してこれを行う方法もあります。ただし、JOIN は最初に行を一致させ、次に DISTINCT 値に減らす必要があるため、DBMS エンジンに不必要な複雑さを追加します。

于 2012-09-27T20:13:12.033 に答える
0

使用する必要があります

count(distinct u.UserId)

count(distinct field_name) は field_name に含まれる一意の値の数をカウントするため、主キーで個別にカウントすると、探している一意のユーザーの数が得られます。

また、結合する代わりに、おそらくこのような in 句を使用する方がよいでしょう

select Gender, count(distinct UserId) as GenderCount
from Users
where u.UserId in (select UserId1 from Forms) or u.UserId in (select UserId2 from Forms)

速度も若干速くなりそうです。

于 2012-09-27T20:14:01.197 に答える