You need to change it round a little bit...
First off, on the aggregate part, yes, anything that's not an aggregate function, i.e. COUNT(), FIRST(), MAX(), MIN(), SUM(), needs to be listed in the GROUP BY clause. Achievements.ADate and Behaviour.BDate also come under this category.
But secondly, you'll get too many points! The trick is to think about what rows you would see before you do any grouping. If you try this query:
Code:
SELECT S.Forename, S.Surname, A.ADate, B.BDate
FROM (Students AS S INNER JOIN Achievements AS A
ON S.Admission = A.Admission) INNER JOIN Behaviour AS B
ON S.Admission = B.Admission;
then you'll see what Access is grouping for you: it's all records from Students, with all records from Behaviour that match, PLUS all records that match from Achievements, in all possible combinations! Say, for a given student, there are 12 behaviour entries, and 20 achievements entries, you'll actually get 12*20 = 240 lines back!
One way to do it is with a few queries. One query calculates the numbers behaviour points for each student, one does the same for achievement, and then a third joins these onto the students table to show the numbers. (You can skip a step and make it two queries, but it's not as "clean").
Code:
query BehaviourCounts
SELECT Admission, COUNT(*) AS BehaviourPoints
FROM Behaviour
WHERE BDate BETWEEN [Forms].[ReportFilter]![txtStartDate] AND [Forms].[ReportFilter]![txtEndDate]
GROUP BY Admission;
query AchievementCounts
SELECT Admission, COUNT(*) AS AchievementPoints
FROM Achievements
WHERE ADate BETWEEN [Forms].[ReportFilter]![txtStartDate] AND [Forms].[ReportFilter]![txtEndDate]
GROUP BY Admission;
query SummaryInfo
SELECT S.Admission, S.Forename, S.Surname, S.Reg,
Nz(A.AchievementPoints, 0) AS AchevementTotal
Nz(B.BehaviourPoints, 0) AS BehaviourTotal
FROM (Students AS S LEFT OUTER JOIN BehaviourCounts AS B
ON S.Admission = B.Admission) LEFT OUTER JOIN AchievementCounts AS A
ON S.Admission=A.Admission;
That way, you can add as many fields from Students as you want, without the grouping issue.
You need the left outer join so that students with no behaviour codes, or with no achievement codes, will still appear in the query. If you do it all with an INNER JOIN, then you'll only see students with an entry in all three tables.
The Nz() converts a NULL to a zero, i.e. any student who has no negative behaviour points will come up as having a 0, rather than a blank value.
Another way to do it is with subqueries, which has the benefit of only needing one query, but makes it a bit less readable. I suspect, although I've not checked, that Access doesn't optimise these all that well either, but try it and see how it works for you:
Code:
SELECT S.Admission, S.Forename, S.Surname, S.Reg,
(SELECT COUNT(*) FROM Behaviour AS B WHERE B.Admission=S.Admission
AND BDate BETWEEN [Forms].[ReportFilter]![txtStartDate] AND [Forms].[ReportFilter]![txtEndDate]) AS BehaviourTotal,
(SELECT COUNT(*) FROM Achievements AS A WHERE A.Admission=S.Admission
AND ADate BETWEEN [Forms].[ReportFilter]![txtStartDate] AND [Forms].[ReportFilter]![txtEndDate]) AS AchievementTotal
FROM Students AS S;
One advantage to this approach, is that you could run it as a parameter query. But it does look nasty (especially in Access which reformats your SQL).
In terms of handling the date thing, if you start running multiple queries as part of a reporting procedure, you may like to store the date range in a separate table, and have "views" (really just saved queries in Access) that only show behaviour/achievement points that fit in the date range, but that's something for another time!
The BETWEEN operator can work differently in different SQL engines;
"X BETWEEN A AND B" means
"A<= X AND X <= B" in some engines, and
"A < X AND X < B" in others, so check it's giving you the results you expect. Also bear in mind that dates are funny, and "24/02/2010" on its own tends to mean "24/02/2010 00:00" which is earlier than "24/02/2010 09:30". Play with some queries and check that you get the records returned that you're expecting to.