2012-09-27 43 views
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 

任何提示来解决这个?

+0

为什么不是COUNT(DISTINCT u.Gender)? –

+1

你正在使用哪个引擎? – Sebas

+0

@WillPalmer如果我使用COUNT(DISTINCT u.Gender),我会得到'male => 1,female => 1,trans => 1' –

回答

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 

关于速度:查询优化器会经常转换IN在可能的情况下为EXISTS,以避免不必要地选择额外的行。但是,使用多列需要使用ORUNION,因此即使在这种情况下也是如此。即:既没有OR也没有UNION与索引很好地发挥。

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(在这种情况下完全有效的BTW),你可以使用或这样的:

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值。

+0

因为那里的联盟看起来很糟糕。 – davidmontoyago

+0

您可以避免使用联合并使用OR。最后都是同样的事情...... – PinnyM

+0

@PinnyM简单地用'OR'替换'UNION'似乎会在关键字'OR'和')'附近导致错误的语法 –

0

您应该使用

count(distinct u.UserId) 

这样用户只能获得一次计数:计数(不同的field_name)计算包含在FIELD_NAME唯一值的数量,在主键,以便计算不同给你的唯一的号码用户,这是你在找什么。

此外,而不是加盟,你可能会在条款使用这样

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) 

它也可能会稍微快一点好起来的。

+0

与此相同:使用脚本会产生性别数量的总和,远大于用户的实际数量 –

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 
+0

将第1行中的脚本更正为“SELECT u1.Gender”后,它似乎可以正常工作。 –

+0

抱歉,是的,tipo – Sebas

+0

我upvoted你的答案,因为你的脚本结果正确的性别数目。但是,使用'EXISTS'的Will方法看起来更聪明,因为它避免了'DISTINCT',这会在sql server上造成额外的负载。 –

相关问题