1

我很长时间以来一直在努力解决这个问题,不知道如何解决它。我很难描述,所以请耐心等待。有两个表:

表“用户”

UserId PK
Gender

表格“表格”

FormId PK
UserId1 FK
UserId2 FK
Type

表单始终与两个用户相关,但并非所有用户都有相关表单。现在我想只计算那些具有相关表格的用户的指定性别。

所以结果,我想拥有某物。像这样:

# |  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

让我们来看看你想要什么:

  • 每个性别有多少人回答了任何表格?
  • 注意:每个用户都应该只计算一次,无论他们填写了多少表格。

像这样措辞,答案变得相当明显,至少在伪代码中:

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

关于速度:查询优化器通常会尽可能地转换,INEXISTS避免不必要地选择额外的行。但是,使用多个列需要 anOR或 a UNION,因此即使在这种情况下也可能很漂亮。即:既OR不能UNION很好地使用索引,也不能很好地使用索引。

于 2012-09-27T22:16:02.643 回答
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 为 DBMS 引擎增加了不必要的复杂性,因为它首先需要匹配行,然后减少到 DISTINCT 值。

于 2012-09-27T20:13:12.033 回答
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 回答
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 回答