SQL Not a GROUP BY expression - sql

I'm still new to SQL.
I've got a query to count the number of students that attend a certain lecture and I've been trying to group the records by the lectureid so I don't have 10 records for the same lecture.
SELECT ATTENDANCESHEET.LECTUREID,TOPIC, (
SELECT COUNT(STUDENTID) AS ATTENDANCE
FROM ATTENDANCESHEET
WHERE ATTENDANCESHEET.STUDENTID = LECTURE.STUDENTID
)
FROM ATTENDANCESHEET,LECTURE
WHERE ATTENDANCESHEET.LECTUREID = LECTURE.LECTUREID
GROUP BY ATTENDANCESHEET.LECTUREID;
I'm getting the error "not a GROUP BY expression". Can someone help me, please?

The error is because you have a correlated query. The correlation clause (the where in the subquery) is using a column from the outer query that is not aggregated. In addition, you have a column topic that is not in the group by.
I believe the query you want is more simply written as:
select a.lectureid, count(*) as attendance
from attendancesheet a
group by a.lectureid;
I notice that you have topic in the select. That is also an issue. Perhaps you want:
select l.lectureid, l.topic, count(*) as attendance
from attendancesheet a join
lecture l
on a.lectureid = l.lectureid
group by l.lectureid;
Or, if you have studentid in lecture, perhaps:
select l.lectureid, l.topic, count(*) as attendance
from lecture l
group by l.lectureid;
EDIT:
The data structure doesn't make sense to me, but perhaps you need both keys for the join:
select l.lectureid, l.topic, count(*) as attendance
from attendancesheet a join
lecture l
on a.lectureid = l.lectureid and a.studentid = l.lectureid
group by l.lectureid;

to solve the issue of group by without knowing the expected result
SELECT ATTENDANCESHEET.LECTUREID,TOPIC, (
SELECT COUNT(STUDENTID) AS ATTENDANCE
FROM ATTENDANCESHEET
WHERE ATTENDANCESHEET.STUDENTID = LECTURE.STUDENTID
)
FROM ATTENDANCESHEET,LECTURE
WHERE ATTENDANCESHEET.LECTUREID = LECTURE.LECTUREID
GROUP BY ATTENDANCESHEET.LECTUREID,TOPIC,LECTURE.STUDENTID; -- added the topic and studentid from lecture table
but I think what he's trying to do is
SELECT ATTENDANCESHEET.LECTUREID,TOPIC, count(LECTURE.STUDENTID) cntstudent
FROM ATTENDANCESHEET,LECTURE
WHERE ATTENDANCESHEET.LECTUREID = LECTURE.LECTUREID
GROUP BY ATTENDANCESHEET.LECTUREID,TOPIC

Try adding TOPIC to the group by :)

Related

right way to alias count * in a subquery

I have query below as
select t.comment_count, count(*) as frequency
from
(select u.id, count(c.user_id) as comment_count
from users u
left join comments c
on u.id = c.user_id
and c.created_at between '2020-01-01' and '2020-01-31'
group by 1) t
group by 1
order by 1
when I also try to alias the count(*) as count(t.*) it gives error, can I not alias that with the t from the table? Not sure what I am missing
Thank you
Count(*) stands for the count of all rows returned by a query (with respect to GROUP BY columns). So it makes no sence to specify one of the involved tables. Consider counting rows produced by a join for example. If you need a count of rows of the specific table t you can use count(distinct t.<unique column>)

How to make Multi Table Sub Query less wordy Access

I am trying to make a query to find all details of students interviewed more than once
I have gotten the results successfully using:
SELECT S.StudNo, S.StudLName, S.StudFName, S.StudMobile, S.City, S.DateEnrolled, S.ProgNo, S.AmountDue, S.Gender
FROM STUDENT AS S INNER JOIN STUDENT_INTERVIEW AS SI ON S.StudNo = SI.StudID
WHERE
(SELECT COUNT(SI.StudID)
FROM STUDENT_INTERVIEW)
GROUP BY S.StudNo, S.StudLName, S.StudFName, S.StudMobile, S.City, S.DateEnrolled, S.ProgNo, S.AmountDue, S.Gender
HAVING COUNT(SI.StudID) > 1;
But it seems excessively long.. I am trying to rewrite it to make it less wordy although am unable to get the correct results. I have been trying to use more sub queries to not have to use GROUP BY. When I do so I get the results for all students, not the specific 2 I'm after
SELECT *
FROM STUDENT
WHERE StudNo IN
(SELECT StudID
FROM STUDENT_INTERVIEW
WHERE
(SELECT COUNT(StudID)
FROM STUDENT_INTERVIEW
HAVING COUNT(StudID) > 1;))
Your first query is a good start. Here is an improvement:
SELECT S.*
FROM STUDENT AS S
WHERE S.StudNo IN (SELECT SI.StudId
FROM STUDENT_INTERVIEW as SI
GROUP BY SI.StudId
HAVING COUNT(*) > 1
);
The outer query needs neither the JOIN nor the GROUP BY. You can basically do all the work in the subquery.

How to group my table for latest date and ID?

I have a table like this:
I need group this table latest date for every ID.
I mean, I want to get last row every ID. Here is my query:
SELECT DISTINCT ch.Date,ID FROM dbo.tblrisk AS rk
inner join (Select TableIdentity, [Date] from tblCommonHistory ) ch
ON ch.TableIDentity = rk.ID order by ID
How can I do what I want?
EDIT: This query worked for me:
SELECT DISTINCT ch.dt,ID FROM dbo.tblrisk AS rk
inner join (Select TableIdentity, max([Date]) as dt from tblCommonHistory group by TableIdentity) ch ON ch.TableIDentity = rk.ID order by ID
Just use aggregation:
select TableIdentity, max([date])
from tblCommonHistory
group by TableIdentity;
Your question only mentions one table. Your query has two; I don't understand the discrepancy.
It's strange that you have duplicated TableIdentity in tblCommonHistory, but otherwise you should not be getting multiple dates for the same ID from your query.
And also, the only reason to join the 2 tables seems to be that you need to skip those ID that are not present in the tblrisk (is it what you need to do?)
In that case, I'd suggest
SELECT max(ch.Date) AS [Date],ID FROM dbo.tblrisk AS rk
inner join tblCommonHistory AS ch ON ch.TableIDentity = rk.ID
group by ID order by ID

How to join two SQL queries into one?

I'm new to SQL and I'm currently trying to learn how to make reports in Visual Studio. I need to make a table, graph and few other things. I decided to do matrix as the last part and now I'm stuck. I write my queries in SQL Server.
I have two tables: Staff (empID, StaffLevel, Surname) and WorkOfArt (artID, name, curator, helpingCurator). In the columns Curator and HelpingCurator I used numbers from empID.
I'd like my matrix to show every empID and the number of paintings where they're acting as a Curator and the number of paintings where they're acting as a Helping Curator (so I want three columns: empID, count(curator), count(helpingCurator).
Select Staff.empID, count(WorkOfArt.Curator) as CuratorTotal
FROM Staff, WorkOfArt
WHERE Staff.empID=WorkOfArt.Curator
and Staff.StaffLevel<7
group by Staff.empID;
Select Staff.empID, count(WorkOfArt.HelpingCurator) as HelpingCuratorTotal
FROM Staff, WorkOfArt
WHERE Staff.empID=WorkOfArt.HelpingCurator
and Staff.StaffLevel<7
group by Staff.empID;
I created those two queries and they work perfectly fine, but I need it in one query.
I tried:
Select Staff.empID, count(WorkOfArt.Curator) as CuratorTotal,
COUNT(WorkOfArt.HelpingCurator) as HelpingCuratorTotal
FROM Staff FULL OUTER JOIN WorkOfArt on Staff.empID=WorkOfArt.Curator
and Staff.empID=WorkOfArt.HelpingCurator
WHERE Staff.StaffLevel<7
group by Staff.empID;
(as well as using left or right outer join)
- this one gives me a table with empID, but in both count columns there are only 0s - and:
Select Staff.empID, count(WorkOfArt.Curator) as CuratorTotal,
COUNT(WorkOfArt.HelpingCurator) as HelpingCuratorTotal
FROM Staff, WorkOfArt
WHERE Staff.empID=WorkOfArt.Curator
and Staff.empID=WorkOfArt.HelpingCurator
and Staff.StaffLevel<7
group by Staff.empID;
And this one gives me just the names of the columns.
I have no idea what to do next. I tried to find the answer in google, but all explanations I found were far more advanced for me, so I couldn't understand them... Could you please help me? Hints are fine as well.
The easiest way to do this is most likely with inner select in the select clause, with something like this:
Select
S.empID,
(select count(*) from WorkOfArt C where C.Curator = S.empID)
as CuratorTotal,
(select count(*) from WorkOfArt H where H.HelpingCurator = S.empID)
as HelpingCuratorTotal
FROM Staff S
WHERE S.StaffLevel<7
group by S.empID;
This way the rows with different role aren't causing problems with the calculation. If the tables are really large or you have a lot of different roles, then most likely more complex query with grouping the items first in the WorkOfArt table might have better performance since this requires reading the rows twice.
From a performance perspective, the following query is probably a little more efficient
select e.EmpId, CuratorForCount, HelpingCuratorForCount
from Staff s
inner join ( select Curator, count(*) as CuratorForCount
from WorkOfArt
group by Curator) mainCurator on s.EmpId = mainCurator.Curator
inner join ( select HelpingCurator, count(*) as HelpingCuratorForCount
from WorkOfArt
group by HelpingCurator) secondaryCurator on s.EmpId = secondaryCurator.HelpingCurator
One method, that can be useful if you want to get more than one value aggregated value from the WorkOfArt table is to pre-aggregate the results:
Select s.empID, COALESCE(woac.cnt, 0) as CuratorTotal,
COALESCE(woahc.cnt) as HelpingCuratorTotal
FROM Staff s LEFT JOIN
(SELECT woa.Curator, COUNT(*) as cnt
FROM WorkOfArt woa
GROUP BY woa.Curator
) woac
ON s.empID = woac.Curator LEFT JOIN
(SELECT woa.HelpingCurator, COUNT(*) as cnt
FROM WorkOfArt woa
GROUP BY woa.HelpingCurator
) woahc
ON s.empID = woahc.HelpingCurator
WHERE s.StaffLevel < 7;
Notice that the aggregation on the outer level is not needed.

SQL aggregate query error

I have 3 tables like this
player(id,name,age,teamid)
team(id,name,sponsor,totalplayer,totalchampion,boss,joindate)
playerdetail(id,playerid,position,number,allstar,joindate)
I want to select teaminfo include name,sponsor,totalplayer,totalchampion,boss,
the average age of the players, the number of the allstar players
I write the t-sql as below
SELECT T.NAME,T.SPONSOR,T.TOTALPLAYER,T.TOTALCHAMPION,T.BOSS,T.JOINDATE,
AVG(P.AGE) AS AverageAge,COUNT(D.ALLSTAR) As AllStarPlayer
FROM Team T,Player P,PlayerDetail D
WHERE T.ID=P.TID AND P.ID=D.PID
but it doesn't work, the error message is
'Column 'Team.Name' is invalid in the select list because it is not
contained in either an aggregate function or the GROUP BY clause.'
Who can help me?
Thx in advance!
Add
GROUP BY
T.NAME,T.SPONSOR,T.TOTALPLAYER,T.TOTALCHAMPION,T.BOSS,T.JOINDATE
In most RDBMS (except MySQL which will guess for you), a column must be either aggregated (COUNT, AVG) or in the GROUP BY
Also, you should use explicit JOINs.
This is clearer, less ambiguous and more difficult to bollix your code
SELECT
T.NAME, T.SPONSOR, T.TOTALPLAYER, T.TOTALCHAMPION, T.BOSS, T.JOINDATE,
AVG(P.AGE) AS AverageAge,
COUNT(D.ALLSTAR) As AllStarPlayer
FROM
Team T
JOIN
Player P ON T.ID=P.TID
JOIN
PlayerDetail D ON P.ID=D.PID
GROUP BY
T.NAME, T.SPONSOR, T.TOTALPLAYER, T.TOTALCHAMPION, T.BOSS, T.JOINDATE;
Given that you want this data per team, and team.ID uniquely identifies team, I suggest the following:
SELECT max(T.NAME) As TeamName,
max(T.SPONSOR) As Sponsor,
max(T.TOTALPLAYER) As TotalPlayers,
max(T.TOTALCHAMPION) As TotalChampions,
max(T.BOSS) As Boss,
max(T.JOINDATE) As JoinDate,
AVG(P.AGE) AS AverageAge,
COUNT(D.PID) As AllStarPlayer
FROM Team T
join Player P on T.ID=P.TID
left join PlayerDetail D on P.ID=D.PID and D.ALLSTAR = 'Y'
group by T.ID
Use:
SELECT T.NAME,T.SPONSOR,T.TOTALPLAYER,T.TOTALCHAMPION,T.BOSS,T.JOINDATE,
AVG(P.AGE) AS AverageAge,COUNT(D.ALLSTAR) As AllStarPlayer
FROM Team T
JOIN Player P ON T.ID = P.TEAMID
JOIN PlayerDetail D ON P.ID = D.PLAYERID
GROUP BY T.NAME,T.SPONSOR,T.TOTALPLAYER,T.TOTALCHAMPION,T.BOSS,T.JOINDATE