Try
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
However, when joining using an interval, as we are doing here on tblAgeGroup, it is important to test if we get the correct number of rows.
So I would run the following queries, and validate that they indeed do return the same number of rows:
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'
and
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'
There are two things that come to my mind, which can go wrong when joining using intervals as here.
Firstly; If the intervals is not uniquely defined, each member would get returned several times.
Eg consider tblAgeGroup containing
...
, minAge = 15
, maxAge = 35
...
and another entry:
...
, minAge = 20
, maxAge = 25
...
A member with an age of 21 would thus get returned in both intervals, thus creating inflation in your count.
Secondly; If Age is not defined in tblMembers, the row will be excluded, but this is probably intended, since we are using inner joins.
You could get around this, with following:
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
Edit:
Added a SQLFiddle to test:
http://sqlfiddle.com/#!6/137b8/5
Queries in the fiddle are:
Build:
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';
Query:
-- 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: Edit:
I see in your comments, that you also need a count for the empty groups. In order to do this, I've "inverted" the from clause, so that we select all from tblAgeGroup, and then left join the other tables.
Also, I assumed that you needed a count of log entries, but after reading comments, I see that you ask for distinct users. I've altered the query accordingly.
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
;
which returns
AgeGroup_Des AMT Log entries AMT distinct members
10-15 3 1
15-25 2 1
25-55 1 1
55-65 0 0
However, this approach does ignores every member which doesn't have a DOB / age registered.
If we want to include these aswell, we need to edit the query to a full outer join:
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
;
which returns
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
See updated fiddle here: http://sqlfiddle.com/#!6/d8733/5