获取属于某个组的用户数

时间:2014-05-12 08:06:30

标签: sql sql-server

我有以下三个表,成员,年龄组和日志。我想知道每个年龄组中有多少人在给定的时间内访问系统。 以下是表格详情

tblMembers:

Member_Id,DOB,年龄

tblAgeGroup:

AgeGroup Id,AgeGroup_Des,minAge,maxAge,meanAge

tblLog:

Member_Id,AccessDate

因此,当管理员选择日期范围时,例如01/04/2014至30/04/2014

我需要显示类似于以下内容的输出:

年龄组用户数

15-25 -------------- 3

25-35 -------------- 2

我怎么也做不到。这是我到目前为止所做的事情

 SELECT Member_Id, Age
   FROM tblLog
        JOIN tblMembers ON tblMembers.Member_Id = tblLog.Member_Id
  WHERE AccessDate >= '2014-04-01'
    AND AccessDate <= '2014-04-30'
  GROUP BY Member_Id, Age

根据脚本,我可以列出Members_Id及其年龄。如何通过tblAgeGroup表中的组详细信息进行分组继续进行。

请建议我。感谢

3 个答案:

答案 0 :(得分:1)

尝试

 SELECT ag.AgeGroup_Des, count(1)
   FROM tblLog l
        INNER JOIN tblMembers m ON m.Member_Id = l.Member_Id
        INNER JOIN tblAgeGroup ag ON ag.minAge <= m.Age and ag.maxAge > m.Age
  WHERE AccessDate >= '2014-04-01'
    AND AccessDate <= '2014-04-30'
  GROUP BY ag.AgeGroup_Des

但是,当使用间隔加入时,正如我们在tblAgeGroup上所做的那样,测试我们是否获得正确的行数非常重要。

因此,我将运行以下查询,并验证它们确实返回相同数量的行:

 SELECT COUNT(1)
   FROM tblLog l
        INNER JOIN tblMembers m ON m.Member_Id = l.Member_Id
        --INNER JOIN tblAgeGroup ag ON ag.minAge <= m.Age and ag.maxAge > m.Age
  WHERE AccessDate >= '2014-04-01'
    AND AccessDate <= '2014-04-30'

 SELECT COUNT(1)
   FROM tblLog l
        INNER JOIN tblMembers m ON m.Member_Id = l.Member_Id
        INNER JOIN tblAgeGroup ag ON ag.minAge <= m.Age and ag.maxAge > m.Age
  WHERE AccessDate >= '2014-04-01'
    AND AccessDate <= '2014-04-30'

有两件事情在我脑海中浮现,在使用区间加入时可能会出错。

首先;如果没有唯一定义的间隔,则会多次返回每个成员。 例如,考虑包含tblAgeGroup ... ,minAge = 15 ,maxAge = 35 ... 和另一个条目: ... ,minAge = 20 ,maxAge = 25 ...

因此,年龄为21岁的成员将在两个时间间隔内返回,从而在您的计数中产生通货膨胀。

其次;如果未在tblMembers中定义Age,则将排除该行,但这可能是预期的,因为我们正在使用内部联接。 你可以通过以下方式解决这个问题:

 SELECT ISNULL(ag.AgeGroup_Des, 'No group found') as AgeGroup_Des,
        COUNT(1) as "AMT users"
   FROM tblLog l
        INNER JOIN tblMembers m ON m.Member_Id = l.Member_Id
         LEFT JOIN tblAgeGroup ag ON ag.minAge <= m.Age and ag.maxAge > m.Age
  WHERE AccessDate >= '2014-04-01'
    AND AccessDate <= '2014-04-30'
  GROUP BY ISNULL(ag.AgeGroup_Des, 'No group found')
  ORDER BY 1

修改 添加了一个SQLFiddle来测试: http://sqlfiddle.com/#!6/137b8/5

小提琴中的查询是:

<强>构建

CREATE TABLE tblMembers
(
    Member_ID int,
    DOB date,
    Age int
);

CREATE TABLE tblAgeGroup
(
    [AgeGroup Id] int,
    AgeGroup_Des varchar(16),
    minAge int,
    maxAge int,
    meanAge float
);

CREATE TABLE tblLog
(
    Member_id int,
    AccessDate date
);



INSERT INTO tblMembers
       (Member_ID, DOB, Age) 
SELECT 1, '1991-03-22', 23
 UNION ALL
SELECT 2, '2000-03-22', 14
 UNION ALL
SELECT 3, '1981-03-22', 33
 UNION ALL
SELECT 4, null, null;

INSERT INTO tblAgeGroup
       ([AgeGroup Id], AgeGroup_Des, minAge, maxAge, meanAge)
SELECT 1, '15-25', 15, 25, null
 UNION ALL
SELECT 2, '10-15', 10, 15, null
 UNION ALL
SELECT 3, '25-55', 25, 55, null;

INSERT INTO tblLog
       (Member_id, AccessDate)
SELECT 1, GETDATE()
 UNION ALL
SELECT 2, GETDATE()
 UNION ALL
SELECT 1, '2014-04-02'
 UNION ALL
SELECT 2, '2014-04-03'
 UNION ALL
SELECT 2, '2014-04-03'
 UNION ALL
SELECT 3, '2014-04-03'
 UNION ALL
SELECT 4, '2014-04-03';

<强>查询:

-- First query: (exludes member 4, since no DOB is registered:
 SELECT ag.AgeGroup_Des, COUNT(1)
   FROM tblLog l
        INNER JOIN tblMembers m ON m.Member_Id = l.Member_Id
        INNER JOIN tblAgeGroup ag ON ag.minAge <= m.Age and ag.maxAge > m.Age
  WHERE AccessDate >= '2014-04-01'
    AND AccessDate <= '2014-04-30'
  GROUP BY ag.AgeGroup_Des;

-- To validate that the counts are the same:
 SELECT COUNT(1)
   FROM tblLog l
        INNER JOIN tblMembers m ON m.Member_Id = l.Member_Id
        --INNER JOIN tblAgeGroup ag ON ag.minAge <= m.Age AND ag.maxAge > m.Age
  WHERE AccessDate >= '2014-04-01'
    AND AccessDate <= '2014-04-30';

-- Second validation:
-- Notice: this does return one less row, since we are using inner join,
-- and thus ommitting member 4
 SELECT COUNT(1)
  FROM tblLog l
       INNER JOIN tblMembers m ON m.Member_Id = l.Member_Id
       INNER JOIN tblAgeGroup ag ON ag.minAge <= m.Age AND ag.maxAge > m.Age
 WHERE AccessDate >= '2014-04-01'
   AND AccessDate <= '2014-04-30';

-- Last query, which includes members with no DOB registered, 
-- as well as members that does not fall into any group:
 SELECT ISNULL(ag.AgeGroup_Des, 'No group found') as AgeGroup_Des,
        COUNT(1) as "AMT users"
   FROM tblLog l
        INNER JOIN tblMembers m ON m.Member_Id = l.Member_Id
         LEFT JOIN tblAgeGroup ag ON ag.minAge <= m.Age AND ag.maxAge > m.Age
  WHERE AccessDate >= '2014-04-01'
    AND AccessDate <= '2014-04-30'
  GRUOP BY ISNULL(ag.AgeGroup_Des, 'No group found')
  ORDER BY 1;

2014-05-14:修改:

我在你的评论中看到,你还需要计算空组。为了做到这一点,我已经“反转”了from子句,因此我们从tblAgeGroup中选择所有,然后 left join 其他表。

另外,我假设您需要一些日志条目,但在阅读评论后,我看到您要求不同的用户。我已相应地更改了查询。

select 
  ag.AgeGroup_Des
, count(l.Member_ID)          as [AMT Log entries]
, count(distinct l.Member_ID) as [AMT distinct members]
from tblAgeGroup ag
left join tblMembers m on ag.minAge <= m.Age and ag.maxAge > m.Age
left join tblLog l on l.Member_id = m.Member_ID
group by isnull(ag.AgeGroup_Des, 'No group found')
order by 1
;

返回

AgeGroup_Des    AMT Log entries AMT distinct members
10-15               3               1
15-25               2               1
25-55               1               1
55-65               0               0

但是,这种方法确实忽略了没有注册DOB /年龄的每个成员。 如果我们想要包含这些内容,我们需要将查询编辑为完全外部联接:

select 
  isnull(ag.AgeGroup_Des, 'No group found') as AgeGroup_Des
, count(l.Member_ID)          as [AMT Log entries]
, count(distinct l.Member_ID) as [AMT distinct members]
from tblAgeGroup ag
full outer join tblMembers m on ag.minAge <= m.Age and ag.maxAge > m.Age
full outer join tblLog l on l.Member_id = m.Member_ID
group by isnull(ag.AgeGroup_Des, 'No group found')
order by 1
;

返回

AgeGroup_Des    AMT Log entries AMT distinct members
10-15               3               1
15-25               2               1
25-55               1               1
55-65               0               0
No group found      1               1

请在此处查看更新的小提琴:http://sqlfiddle.com/#!6/d8733/5

答案 1 :(得分:0)

 SELECT usr_type, SUM(_15_25 + _25_35) AS qty
   FROM
        (SELECT CASE 
                     WHEN  age>=15 AND age<25 THEN '15-25'
                     WHEN age>=25 AND age<=35 THEN '25-35'
                     ELSE 'other'
                 END AS usr_type, 
                CASE
                     WHEN age>= 15 AND age < 25 THEN 1
                     ELSE 0
                 END AS _15_25, 
                CASE
                     WHEN age >= 25 AND age <= 35 THEN 1
                     ELSE 0
                 END AS _25_35 
                 Age
           FROM tblLog
                JOIN tblMembers ON tblMembers.Member_Id = tblLog.Member_Id
          WHERE AccessDate >= '2014-04-01'
            AND AccessDate <= '2014-04-30'
        ) T
  GROUP BY usr_type

尝试这种逻辑解决方案。

答案 2 :(得分:0)

 SELECT minAge, maxAge, count(*) as "No of Users" 
   FROM tblAgeGroup g 
        INNER JOIN tblMembers m ON Age BETWEEN minAge AND maxAge
        INNER JOIN tblLog l ON m.Member_ID = l.Member_ID
  WHERE AccessDate BETWEEN '2014-04-01' and '2014-04-30'
  GROUP BY minAge, maxAge
  ORDER BY minAge, maxAge