How to use group by in SQL Server - sql

Query:
SELECT
dbo.tblDivision.DivisionName, dbo.tblDistrict.DistrictName,
case
when Gender = 'Male'
then count(Gender)
end as male,
case
when Gender = 'female'
then count(Gender)
end as female,
UnitEName
FROM
dbo.tblDistrict
INNER JOIN
dbo.tblThana ON dbo.tblDistrict.DistrictNo = dbo.tblThana.DistrictNo
INNER JOIN
dbo.tblDivision ON dbo.tblDistrict.DivisionNo = dbo.tblDivision.DivisionNo
INNER JOIN
dbo.vw_EmpInfo ON dbo.tblThana.ThanaNo = dbo.vw_EmpInfo.PerThanaNo
GROUP BY
Gender, DistrictName, DivisionName, UnitEName, UnitEAddress
ORDER BY
DivisionName, DistrictName, UnitEName
This results like below:
but I want every unit's result in one single row. may be I have problem in my group by.
How should I refactor my query?

If you want both genders in one row, you should not group by gender.
Instead you should add up how many male and how many female there are using sum():
SELECT dbo.tblDivision.DivisionName, dbo.tblDistrict.DistrictName,
SUM(case when Gender='Male' then 1 else 0 end) as male,
SUM(case when Gender='Female' then 1 else 0 end) as female,
UnitEName
FROM dbo.tblDistrict INNER JOIN
dbo.tblThana ON dbo.tblDistrict.DistrictNo = dbo.tblThana.DistrictNo INNER JOIN
dbo.tblDivision ON dbo.tblDistrict.DivisionNo = dbo.tblDivision.DivisionNo INNER JOIN
dbo.vw_EmpInfo ON dbo.tblThana.ThanaNo = dbo.vw_EmpInfo.PerThanaNo
group by DistrictName,DivisionName,UnitEName,UnitEAddress
order by DivisionName,DistrictName,UnitEName

You have to leave Gender column out of the grouping clause:
SELECT dbo.tblDivision.DivisionName, dbo.tblDistrict.DistrictName,
COUNT(CASE Gender WHEN 'Male' THEN 1 END) AS male,
COUNT(CASE Gender WHEN 'female' THEN 1 END) AS female,
UnitEName
FROM dbo.tblDistrict
INNER JOIN dbo.tblThana ON dbo.tblDistrict.DistrictNo = dbo.tblThana.DistrictNo
INNER JOIN dbo.tblDivision ON dbo.tblDistrict.DivisionNo = dbo.tblDivision.DivisionNo
INNER JOIN dbo.vw_EmpInfo ON dbo.tblThana.ThanaNo = dbo.vw_EmpInfo.PerThanaNo
GROUP BY DistrictName,DivisionName,UnitEName,UnitEAddress
ORDER BY DivisionName,DistrictName,UnitEName

SELECT dbo.tblDivision.DivisionName
, dbo.tblDistrict.DistrictName
,COUNT(case when Gender='Male' then 1 end) as male
,COUNT(case when Gender='female' then 1 end) as female
,UnitEName
FROM dbo.tblDistrict
INNER JOIN dbo.tblThana ON dbo.tblDistrict.DistrictNo = dbo.tblThana.DistrictNo
INNER JOIN dbo.tblDivision ON dbo.tblDistrict.DivisionNo = dbo.tblDivision.DivisionNo
INNER JOIN dbo.vw_EmpInfo ON dbo.tblThana.ThanaNo = dbo.vw_EmpInfo.PerThanaNo
GROUP BY DivisionName
,DistrictName
,UnitEName

Remove Gender from Group by clause
SELECT dbo.tblDivision.DivisionName, dbo.tblDistrict.DistrictName,
sum (case when Gender='Male' then 1 else 0 end) as male
sum (case when Gender='female' then 1 else 0 end) as female,
UnitEName
FROM dbo.tblDistrict
INNER JOIN dbo.tblThana ON dbo.tblDistrict.DistrictNo = dbo.tblThana.DistrictNo
INNER JOIN dbo.tblDivision ON dbo.tblDistrict.DivisionNo = dbo.tblDivision.DivisionNo
INNER JOIN dbo.vw_EmpInfo ON dbo.tblThana.ThanaNo = dbo.vw_EmpInfo.PerThanaNo
GROUP BY DistrictName,DivisionName,UnitEName,UnitEAddress
ORDER BY DivisionName,DistrictName,UnitEName

Related

SQL Counting Using Group By With Multiple Tables and Joins

I have 3 tables (Schools, Students, Activities) such as simply like these:
School
Student
Activity
I want to get the list for each school the ALL activities and number of participants according to the gender in the 2020 ONLY like this table:
I did a query like this but it didn't worked as I wanted:
SELECT Sc.SchoolName, A.ActivityName, Sc.Year COUNT(Gender)
FROM School Sc
JOIN Student S ON Sc.SchoolID=S.SchoolID
JOIN Activity A ON S.ActivityID=A.ActivityID
GROUP BY Gender
How can I fix this? Can you give me solution?
You want conditional aggregation:
SELECT
Sc.SchoolName,
A.ActivityName,
Sc.Year,
SUM(CASE WHEN S.Gender = 'F' THEN 1 ELSE 0 ENd) F,
SUM(CASE WHEN S.Gender = 'M' THEN 1 ELSE 0 ENd) M
FROM School Sc
JOIN Student S ON Sc.SchoolID = S.SchoolID
JOIN Activity A ON S.ActivityID = A.ActivityID
GROUP BY
Sc.SchoolName,
A.ActivityName,
Sc.Year
SELECT
Sc.SchoolName,
A.ActivityName,
Sc.Year,
COUNT(CASE WHEN S.Gender = 'F' THEN StudentID END) as F,
COUNT(CASE WHEN S.Gender = 'M' THEN StudentID END) as M
FROM School Sc
INNER JOIN Student S ON Sc.SchoolID = S.SchoolID
INNER JOIN Activity A ON S.ActivityID = A.ActivityID
WHERE YEAR LIKE '2020-%'
GROUP BY
Sc.SchoolName,
A.ActivityName,
Sc.Year
Please use below query,
SELECT Sc.SchoolName, Sc.Year, A.ActivityName,
case when Sc.Gender = 'M' then count(1) end as M,
case when Sc.Gender = 'F' then count(1) end as F
FROM School Sc
JOIN Student S ON Sc.SchoolID=S.SchoolID
JOIN Activity A ON S.ActivityID=A.ActivityID
GROUP BY Sc.SchoolName, Sc.Year, A.ActivityName;

SQL Server 2012 - is there a better way to do this as when there are duplicates it counts them more than once?

This is not accurate as the count can be wrong so is there a better way using exists? I want to identify if one case of each course exists.
SELECT
IdentityCourses.IdentityID AS ID,Identities.LastName AS LastName,
Identities.FirstNames AS FirstName,Units.UnitID, Units.Description AS Unit
FROM
dbo.UnitIdentities
INNER JOIN
dbo.IdentityCourses ON dbo.UnitIdentities.IdentityID = dbo.IdentityCourses.IdentityID
INNER JOIN
dbo.COCSourceCourses ON dbo.IdentityCourses.CourseID = dbo.COCSourceCourses.CBESCourseID
INNER JOIN
dbo.Identities ON dbo.UnitIdentities.IdentityID = dbo.Identities.IdentityID
INNER JOIN
dbo.Units ON dbo.UnitIdentities.UnitID = dbo.Units.UnitID
WHERE
(dbo.UnitIdentities.IsActiveMember = 1)
GROUP BY
IdentityCourses.IdentityID, Identities.LastName, Identities.FirstNames,
Units.Description, Units.UnitID
HAVING
(SUM((CASE WHEN COCSourceCourses.COCID = 10048 then 1 else 0 end)+
(CASE WHEN COCSourceCourses.COCID = 10049 then 1 else 0 end)+
(CASE WHEN COCSourceCourses.COCID = 10050 then 1 else 0 end)+
(CASE WHEN COCSourceCourses.COCID = 10051 then 1 else 0 end)+
(CASE WHEN COCSourceCourses.COCID = 10063 then 1 else 0 end)+
(CASE WHEN COCSourceCourses.COCID = 10073 then 1 else 0 end))) = 6
AND IdentityCourses.IdentityID NOT IN (SELECT IdentityID
FROM IdentityQualifications
WHERE QualificationID IN (1012, 1014, 1025))
ORDER BY
Units.UnitID
Try using count(distinct ..):
SELECT (..columns..)
FROM dbo.UnitIdentities UI
LEFT JOIN IdentityQualifications IQ
ON IQ.IdentityID = UI.IdentityID
AND IQ.QualificationID IN (1012, 1014, 1025)
INNER JOIN dbo.IdentityCourses IC
ON IC.IdentityID = dbo.UnitIdentities.IdentityID
INNER JOIN dbo.COCSourceCourses COC
ON COC.CBESCourseID = IC.CourseID
AND COC.COCID IN (10048, 10049, 10050, 10051, 10063, 10073)
(..two more table joins on identities and units..)
WHERE IQ.IdentityID IS NULL
GROUP BY (..columns..)
HAVING COUNT(DISTINCT COC.COCID) = 6
ORDER BY Units.UnitID
When you are only interested in certain records, then why don't you use the WHERE clause? Only select the COCIDs you are interested in and then count distinct results.
You don't need any GROUP BY and HAVING by the way, as you only display identities/units, so you can count associated courses in a subquery in your WHERE clause.
select
i.identityid as id,
i.lastname as lastname,
i.firstnames as firstname,
u.unitid,
u.description as unit
from dbo.identities i
join dbo.unitidentities ui on ui.identityid = i.identityid and ui.isactivemember = 1
join dbo.units u on u.unitid = ui.unitid
where i.identityid not in
(
select iq.identityid
from identityqualifications iq
where iq.qualificationid in (1012, 1014, 1025)
)
and
(
select count(distinct sc.cocid)
from dbo.cocsourcecourses sc
join dbo.identitycourses ic on ic.courseid = sc.cbescourseid
where sc.cocid in (10048, 10049, 10050, 10051, 10063, 10073)
and ic.identityid = i.identityid
) = 6
order by u.unitid;

How to link sql sub queries together?

is it possible to link the following sub query (see below.) The first three sub queries work fine however I'm struggling to see how to do the rest any guidance would be great cheers.
Ps. apologies for the long code
SELECT c.[Status],
c.CompanyId,
c.Name,
(SELECT count(DISTINCT usr.UserID)
FROM [ondemand.10cms.com].Security.[user] usr
INNER JOIN [ondemand.10cms.com].Company.Company
ON usr.CompanyID = Company.CompanyID
WHERE usr.CompanyID = c.CompanyID) AS TotalUsers,
(SELECT sum (CASE WHEN usr.Status = 2 THEN 1 ELSE 0 END)
FROM [ondemand.10cms.com].Security.[user] usr
INNER JOIN [ondemand.10cms.com].Company.Company
ON usr.CompanyID = Company.CompanyID
WHERE usr.CompanyID = c.CompanyID) AS ActiveUsers,
(SELECT sum (CASE WHEN usr.Status = 3 THEN 1 ELSE 0 END)
FROM [ondemand.10cms.com].Security.[User] usr
INNER JOIN [ondemand.10cms.com].Company.Company
ON usr.CompanyID = Company.CompanyID WHERE usr.CompanyID = c.CompanyID) AS SuspendedUsers,
(Select COUNT (distinct usrs.id)
From [ondemand.10cms.com].Security.UserSession usrs
inner join [ondemand.10cms.com].Security.[user] usr on usrs.UserID=usr.UserID
) as TotalLogin,
(Select
COUNT( MerchandisingModule.Name)
From [ondemand.10cms.com].Project.Template
inner join [ondemand.10cms.com].Project.MerchandisingModule on Template.TemplateID= MerchandisingModule.TemplateId
)as CurrentModules,
(Select
count(MerchandisingModule.CreatedDate)
from [ondemand.10cms.com].Project.MerchandisingModule
inner join [ondemand.10cms.com].Project.Template on Template.TemplateID= MerchandisingModule.TemplateId
)as ModulesCreated,
(Select
count(mm.UpdatedDate)
from [ondemand.10cms.com].Project.MerchandisingModule mm
inner join [ondemand.10cms.com].Project.Template on Template.TemplateID= mm.TemplateId
)as ModulesUpdated,
(Select
COUNT(MA.MerchandisingAreaID)
from [ondemand.10cms.com].Project.MerchandisingArea MA
inner join [ondemand.10cms.com].Project.Project on Project.ProjectID= MA.ProjectID
) as Currentareas,
(Select
COUNT (MA.name)
from [ondemand.10cms.com].Project.MerchandisingArea MA
inner join [ondemand.10cms.com].Project.Project on Project.ProjectID= MA.ProjectID
) as AreasCreated,
(select
COUNT (MerchandisingArea.UpdatedDate)
from [ondemand.10cms.com].Project.MerchandisingArea
inner join [ondemand.10cms.com].Project.Project on Project.ProjectID= MerchandisingArea.ProjectID
) as AreasUpdated,
(Select
SUM ( case when MA.PublishStatus = 1 then 1 else 0 end)
from [ondemand.10cms.com].Project.MerchandisingArea MA
inner join [ondemand.10cms.com].Project.PublishingStatus on PublishingStatus.PublishStatusId = MA.PublishStatus
) as SuccessPublished,
(Select
SUM ( case when MA.PublishStatus = 3 then 1 else 0 end)
from [ondemand.10cms.com].Project.MerchandisingArea MA
inner join [ondemand.10cms.com].Project.PublishingStatus on PublishingStatus.PublishStatusId= MA.PublishStatus
) as FailedPublished
from [ondemand.10cms.com].Company.Company c
This was fixed by adding another inner join to each sub query and also another where clause

Sub sum query bringing back more results then possible

I am currently running the following query.( See below) However when I run this query the active users and Suspended users are bring back a far greater result then that's in the database.
I just wondered if you could possibly shed light on the reason why and correct me where I'm going wrong?
SELECT c.[Status],
c.CompanyId,
c.Name,
(SELECT count(DISTINCT usr.UserID)
FROM [ondemand.10cms.com].Security.[user] usr
INNER JOIN [ondemand.10cms.com].Company.Company
ON usr.CompanyID = c.CompanyID) AS TotalUsers,
(SELECT sum (CASE
WHEN usr.Status = 2 THEN 1
ELSE 0
END)
FROM [ondemand.10cms.com].Security.[user] usr
INNER JOIN [ondemand.10cms.com].Company.Company
ON usr.CompanyID = c.CompanyID) AS ActiveUsers,
(SELECT sum (CASE
WHEN usr.Status = 3 THEN 1
ELSE 0
END)
FROM [ondemand.10cms.com].Security.[User] usr
INNER JOIN [ondemand.10cms.com].Company.Company
ON usr.CompanyID = c.CompanyID) AS SuspendedUsers
FROM [ondemand.10cms.com].Company.Company c
In each of your sub queries you have two tables, one is being joined to the outer query but there is no join between the two inner tables. All of those sub queries are a bit unnecessary, I would rewrite as a simpler query, something like so:
SELECT
Company.[Status]
,Company.CompanyId
,Company.Name
,COUNT(DISTINCT usr.UserID) AS TotalUsers
,SUM(CASE WHEN usr.Status = 2 THEN 1
ELSE 0
END) AS ActiveUsers
,SUM(CASE WHEN usr.Status = 3 THEN 1
ELSE 0
END) AS SuspendedUsers
FROM [ondemand.10cms.com].Security.[user] usr
INNER JOIN [ondemand.10cms.com].Company.Company
ON usr.CompanyID = Company.CompanyID
GROUP BY
Company.[Status]
,Company.CompanyId
,Company.Name
If you just wabt a fix for your query as is then try this:
SELECT c.[Status],
c.CompanyId,
c.Name,
(SELECT count(DISTINCT usr.UserID)
FROM [ondemand.10cms.com].Security.[user] usr
INNER JOIN [ondemand.10cms.com].Company.Company
ON usr.CompanyID = Company.CompanyID
WHERE usr.CompanyID = c.CompanyID) AS TotalUsers,
(SELECT sum (CASE
WHEN usr.Status = 2 THEN 1
ELSE 0
END)
FROM [ondemand.10cms.com].Security.[user] usr
INNER JOIN [ondemand.10cms.com].Company.Company
ON usr.CompanyID = Company.CompanyID
WHERE usr.CompanyID = c.CompanyID) AS ActiveUsers,
(SELECT sum (CASE
WHEN usr.Status = 3 THEN 1
ELSE 0
END)
FROM [ondemand.10cms.com].Security.[User] usr
INNER JOIN [ondemand.10cms.com].Company.Company
ON usr.CompanyID = Company.CompanyID
WHERE usr.CompanyID = c.CompanyID) AS SuspendedUsers
FROM [ondemand.10cms.com].Company.Company c
I don't know your data (it could cause duplicates), but couldnt you use a standard group by and not use sub queries?
Select
c.[Status],
c.CompanyId,
c.Name,
count(distinct usr.UserID) as TotalUsers,
sum(case when usr.Status = 2 then 1 else 0 end) as ActiveUsers,
sum(case when usr.Status = 3 then 1 else 0 end) as SuspendedUsers
from [ondemand.10cms.com].Company.Company c
inner join [ondemand.10cms.com].Security.[user] usr
on usr.CompanyID=c.CompanyID
GROUP BY
c.[Status],
c.CompanyId,
c.Name
Try adding a group by CompanyId and let us know how that worked.

SQL SELECT string Greater than using count()

I am trying to list groups that have more graduate than undergraduate student members. I feel I have the concept behind my idea, but making the query is a little more difficult then a simple translation. Below is my code, I currently am getting a missing right parenthesis error where COUNT(student.career = 'GRD'). Thanks.
SELECT studentgroup.name
COUNT(student.career = 'GRD') - COUNT(student.career = 'UGRD')
AS Gradnum FROM studentgroup
INNER JOIN memberof ON studentgroup.GID = memberof.GroupID
INNER JOIN student ON memberof.StudentID = student.SID
WHERE Gradnum > 1;
SELECT studentgroup.GID, max(studentgroup.name)
FROM studentgroup
INNER JOIN memberof ON studentgroup.GID = memberof.GroupID
INNER JOIN student ON memberof.StudentID = student.SID
GROUP BY studentgroup.GID
HAVING SUM(CASE WHEN student.career = 'GRD' THEN 1
WHEN student.career = 'UGRD'THEN -1
ELSE 0
END) >0
SELECT studentgroup.name
SUM(CASE WHEN student.career = 'GRD' THEN 1 ELSE 0 END) - SUM(CASE WHEN student.career = 'UGRD' THEN 1 ELSE 0 END)
AS Gradnum FROM studentgroup
INNER JOIN memberof ON studentgroup.GID = memberof.GroupID
INNER JOIN student ON memberof.StudentID = student.SID
WHERE Gradnum > 1
GROUP BY studentgroup.name;
I have used WITH As clause which is supported by most of DBMS like SQL Server, PostGresSQL except MySQL
With grpTbl As
(
SELECT studentgroup.name As StudentGroupName,
SUM( CASE WHEN student.career = 'GRD' THEN 1 ELSE 0 END ) AS 'TotalGraduate',
SUM( CASE WHEN student.career = 'UGRD' THEN 1 ELSE 0 END ) AS 'TotalUnderGraduate'
FROM studentgroup
INNER JOIN memberof ON studentgroup.GID = memberof.GroupID
INNER JOIN student ON memberof.StudentID = student.SID
)
SELECT StudentGroupName
FROM grpTbl
WHERE TotalGraduate > TotalUnderGraduate
For MySQL you can use Temporary table to store resultset from First query and filter out the GroupNames which have more Graduate than UnderGraduate in WHERE clause .
This method will work for other DBMS also difference being syntax of creating temporary table.
CREATE TEMPORARY TABLE grpTbl (
StudentGroupName varchar(255),
TotalGraduate INT,
TotalUnderGraduate INT
);
INSERT INTO grpTbl
SELECT studentgroup.name As StudentGroupName,
SUM( CASE WHEN student.career = 'GRD' THEN 1 ELSE 0 END ) ,
SUM( CASE WHEN student.career = 'UGRD' THEN 1 ELSE 0 END )
FROM studentgroup
INNER JOIN memberof ON studentgroup.GID = memberof.GroupID
INNER JOIN student ON memberof.StudentID = student.SID
SELECT StudentGroupName
FROM grpTbl
WHERE TotalGraduate > TotalUnderGraduate
DROP TABLE grpTbl
One more option
SELECT studentgroup.name
FROM studentgroup INNER JOIN memberof ON studentgroup.GID = memberof.GroupID
INNER JOIN student ON memberof.StudentID = student.SID
GROUP BY studentgroup.name
HAVING COUNT(CASE WHEN student.career = 'GRD' THEN student.career END)
> COUNT(CASE WHEN student.career = 'UGRD' THEN student.career END)