COUNT of different tables in GROUP - sql

I have a table that holds a list of classes available at a school. Each class can have a number of sessions. And each class can have pupils assigned to it.
What I need to do is get a count of all sessions for each class, as well as the number of students attending the class. I have done the first bit, but if I join to the pupil allocation table, my counts will be wrong.
I have conjured up some fake SQL that you can use.
I'm stuck with efficiently getting a count from the Pupil link table.
DECLARE #Class TABLE
(
ClassID INT NOT NULL,
ClassName VARCHAR(20) NOT NULL
)
INSERT INTO #Class VALUES (1, 'English')
INSERT INTO #Class VALUES (2, 'Maths')
DECLARE #ClassSession TABLE
(
ClassSessionID INT NOT NULL,
ClassID INT NOT NULL,
Description VARCHAR(100) NOT NULL
)
INSERT INTO #ClassSession VALUES (1, 1, 'Basic English')
INSERT INTO #ClassSession VALUES (2, 1, 'Advanced English')
INSERT INTO #ClassSession VALUES (3, 1, 'Amazing English')
INSERT INTO #ClassSession VALUES (4, 2, 'Basic English')
INSERT INTO #ClassSession VALUES (5, 2, 'Basic English')
DECLARE #ClassPupil TABLE
(
ClassPupilID INT NOT NULL,
ClassID INT NOT NULL,
PupilID INT NOT NULL -- FK to the Pupils table.
)
INSERT INTO #ClassPupil VALUES (1, 1, 1000)
INSERT INTO #ClassPupil VALUES (2, 1, 1001)
INSERT INTO #ClassPupil VALUES (3, 1, 1002)
INSERT INTO #ClassPupil VALUES (4, 1, 1003)
INSERT INTO #ClassPupil VALUES (5, 1, 1004)
INSERT INTO #ClassPupil VALUES (6, 2, 1005)
INSERT INTO #ClassPupil VALUES (7, 2, 1006)
INSERT INTO #ClassPupil VALUES (8, 2, 1007)
SELECT ClassName, COUNT(*) AS Sessions, '??' AS NumerOfPupils
FROM #Class c
INNER JOIN #ClassSession cs
ON cs.ClassID = c.ClassID
GROUP BY c.ClassID, c.ClassName
It can maybe be done with a sub query? Is that the best way?

You have two independent dimensions for each class. You need to aggregat them separately:
SELECT c.ClassName, cs.Sessions, cp.Pupils
FROM #Class c INNER JOIN
(SELECT ClassId, COUNT(*) as sessions
FROM #ClassSession cs
GROUP BY ClassId
) cs
ON cs.ClassID = c.ClassID INNER JOIN
(SELECT ClassId, COUNT(*) as pupils
FROM #ClassPupil cp
GROUP BY ClassId
) cp
ON cp.ClassId = c.ClassId;

Another method is to use CROSS APPLY to get the count of pupils:
SELECT
ClassName, COUNT(*) AS Sessions, cp.NumberOfPupils
FROM #Class c
INNER JOIN #ClassSession cs
ON cs.ClassID = c.ClassID
CROSS APPLY (
SELECT COUNT(*) AS NumberOfPupils
FROM #ClassPupil
WHERE
ClassID = c.ClassID
) cp
GROUP BY c.ClassID, c.ClassName, cp.NumberOfPupils

SELECT ClassName, COUNT(distinct cs.ClassSessionID) AS Sessions, /*'??'*/ count( distinct cp.PupilID) AS NumerOfPupils
FROM #Class c
INNER JOIN #ClassSession cs
ON cs.ClassID = c.ClassID
inner join #ClassPupil cp on c.ClassID=cp.ClassID
GROUP BY /*c.ClassID,*/ c.ClassName
count(distinct...) solves (works around) the problem.
Generally, this is (A -> B, C) issue.

Related

SQL Server : SELECT query to get DISTINCT and MAX display order value

I have a product table, Category table, and Mapping table. Category saved as a category tree. If a single product has mapped with the last category in a hierarchy of level three. All the levels saved in the mapping table with the same product id.
eg : Assume there is category tre like this Electronic>LapTops>DELL and when product id = 1 assigned to category 'DELL' mapping will save as [1,Electronic],[1,LapTops],[1,DELL]
When I get data with a select query all the category levels appear with the same product Id.
My problem is I need to retrieve data as [productId, ProductName, LastCategortLevel, CategoryName, CategoryId].
Refer actual result below. I just need to pick the highlighted product with the last category level which is the highest category order level.
I can't use another stored procedure or function because it's a small part of a large stored procedure.
The actual database tables are very big. But I have tried to implement the same scenario with small temp tables. see the below queries.
DECLARE #Products TABLE (ProductId INT NOT NULL)
INSERT INTO #Products(ProductId)
SELECT ProductId
FROM (VALUES (1), (2), (3), (4)) as x (ProductId)
DECLARE #Categories TABLE (CategoId INT NOT NULL,
Name VARCHAR(MAX) NOT NULL,
ParentCategoryId INT NOT NULL,
DisplayOrder INT NOT NULL)
-- 1st category tree
INSERT INTO #Categories VALUES (10, 'Electronic', 0, 1)
INSERT INTO #Categories VALUES (11, 'LapTops', 10, 2)
INSERT INTO #Categories VALUES (12, 'DELL', 11, 3)
INSERT INTO #Categories VALUES (13, 'HP', 11, 3)
-- 2st category tree
INSERT INTO #Categories VALUES (14, 'Clothes', 0, 1)
INSERT INTO #Categories VALUES (15, 'T-Shirts', 14, 2)
INSERT INTO #Categories VALUES (16, 'Red', 15, 3)
INSERT INTO #Categories VALUES (17, 'Denim', 14, 2)
INSERT INTO #Categories VALUES (18, 'Levise', 17, 3)
DECLARE #Product_Category_Mappings TABLE(MappingId INT NOT NULL,
ProductId INT NOT NULL,
CategoryId INT NOT NULL)
INSERT INTO #Product_Category_Mappings VALUES (100, 1, 10)
INSERT INTO #Product_Category_Mappings VALUES (101, 1, 11)
INSERT INTO #Product_Category_Mappings VALUES (102, 1, 12)
INSERT INTO #Product_Category_Mappings VALUES (103, 2, 10)
INSERT INTO #Product_Category_Mappings VALUES (104, 2, 11)
INSERT INTO #Product_Category_Mappings VALUES (105, 2, 12)
INSERT INTO #Product_Category_Mappings VALUES (106, 3, 14)
INSERT INTO #Product_Category_Mappings VALUES (107, 3, 15)
INSERT INTO #Product_Category_Mappings VALUES (108, 3, 16)
INSERT INTO #Product_Category_Mappings VALUES (109, 4, 14)
INSERT INTO #Product_Category_Mappings VALUES (110, 4, 17)
INSERT INTO #Product_Category_Mappings VALUES (111, 4, 18)
SELECT *
FROM #Products P
INNER JOIN #Product_Category_Mappings M ON M.ProductId = P.ProductId
INNER JOIN #Categories C ON C.CategoId = M.CategoryId
WHERE M.ProductId = P.ProductId
ORDER BY P.ProductId, C.DisplayOrder
Result of the above script. How I get highlighted rows?
For each ProductId, you want the row with highest DisplayOrder. You can use window functions:
SELECT *
FROM (
SELECT *, ROW_NUMBER() OVER(PARTITION BY P.ProductId ORDER BY C.DisplayOrder DESC) rn
FROM #Products P
INNER JOIN #Product_Category_Mappings M ON M.ProductId = P.ProductId
INNER JOIN #Categories C ON C.CategoId = M.CategoryId
WHERE M.ProductId = P.ProductId
) t
WHERE rn = 1
ORDER BY P.ProductId, C.DisplayOrder

Getting a single result on one table from criteria on multiple rows in another table

Imagine a Student table with the name and id of students at a school, and a Grades table that has grades on the form:
grade_id | student_id.
What I want to do is find all the students that match an arbitrary criteria of say "find all students that have grade A, grade B, but not C or D".
In a school situation a student could have several A's and B's, but for my particular problem they will allways have one or none of each grade.
Also, the tables i'm working on are huge (several million rows in each), but i only need to find say 10-20 on each query (the purpose of this is to find test data).
Thanks!
Change the table variables to your physical tables and this should help?
DECLARE #Students TABLE (
StudentId INT,
StudentName VARCHAR(50));
INSERT INTO #Students VALUES (1, 'Tom');
INSERT INTO #Students VALUES (2, 'Dick');
INSERT INTO #Students VALUES (3, 'Harry');
DECLARE #StudentGrades TABLE (
StudentId INT,
GradeId INT);
INSERT INTO #StudentGrades VALUES (1, 1);
INSERT INTO #StudentGrades VALUES (1, 1);
INSERT INTO #StudentGrades VALUES (1, 2);
INSERT INTO #StudentGrades VALUES (1, 3);
INSERT INTO #StudentGrades VALUES (2, 1);
INSERT INTO #StudentGrades VALUES (2, 2);
INSERT INTO #StudentGrades VALUES (3, 1);
INSERT INTO #StudentGrades VALUES (3, 1);
INSERT INTO #StudentGrades VALUES (3, 3);
INSERT INTO #StudentGrades VALUES (3, 4);
INSERT INTO #StudentGrades VALUES (3, 4);
DECLARE #Grades TABLE (
GradeId INT,
GradeName VARCHAR(10));
INSERT INTO #Grades VALUES (1, 'A');
INSERT INTO #Grades VALUES (2, 'B');
INSERT INTO #Grades VALUES (3, 'C');
INSERT INTO #Grades VALUES (4, 'D');
--Student/ Grade Summary
SELECT
s.StudentId,
s.StudentName,
g.GradeName,
COUNT(sg.GradeId) AS GradeCount
FROM
#Students s
CROSS JOIN #Grades g
LEFT JOIN #StudentGrades sg ON sg.StudentId = s.StudentId AND sg.GradeId = g.GradeId
GROUP BY
s.StudentId,
s.StudentName,
g.GradeName;
--Find ten students with A and B but not C or D
SELECT TOP 10
*
FROM
#Students s
WHERE
EXISTS (SELECT * FROM #StudentGrades sg WHERE sg.StudentId = s.StudentId AND sg.GradeId = 1) --Got an A
AND EXISTS (SELECT * FROM #StudentGrades sg WHERE sg.StudentId = s.StudentId AND sg.GradeId = 2) --Got a B
AND NOT EXISTS (SELECT * FROM #StudentGrades sg WHERE sg.StudentId = s.StudentId AND sg.GradeId IN (3, 4)); --Didn't get a C or D
Make sure all your id fields are indexed.
select *
from students s
where exists
(
select *
from grades g
where g.grade_id in (1, 2)
and g.student_id = s.student_id
)

How do you join tables sharing the same column?

I made an SQL Fiddle and what I would like to do is join these two queries by using the departmentid.
What I would like to show is the departmentname and not_approved_manager.
Would it be best to use a union or join in this case?
Tables
create table cserepux
(
status int,
comment varchar(25),
departmentid int,
approveddate datetime
);
insert into cserepux (status, comment, departmentid, approveddate)
values (1, 'testing1', 1, NULL), (1, 'testing2', 1, NULL),
(1, 'testing2', 2, NULL), (0, 'testing2', 1, NULL),
(0, 'tesitng2', 1, NULL), (0, 'testing2', 1, NULL),
(0, 'tesitng2', 1, NULL), (0, 'testing3', 2, NULL),
(0, 'testing3', 3, NULL);
create table cseDept
(
departmentid int,
department_name varchar(25)
);
insert into cseDept (departmentid,department_name)
values (1, 'department one'), (2, 'department two'),
(3, 'department three'), (4, 'department four');
Query
select
departmentid,
COUNT(*) AS 'not_approved_manager'
from
cserepux
where
approveddate is null
group by
departmentid
SELECT * FROM cseDept
You need to do a join. A union will not get you what you want.
select d.department_name, COUNT(*) AS 'not_approved_manager'
from cserepux c
inner join cseDept d on c.departmentid = d.departmentid
where approveddate is null
group by d.department_name
Do you need just a join and a correct group by
select dep.department_name, COUNT(*) AS 'not_approved_manager'
from cseDept dep
join cserepux cs on cs.departmentid = dep.departmentid
where approveddate is null
group by dep.department_name
Fiddle: http://sqlfiddle.com/#!3/5cf4e/30
Since joins and group by are really basic things in SQL I can suggest you do take a look on some tutorials to get a bit more proficiency whit it. You can try SQL Server Central stairway articles series

Select rows with duplicate values in 2 columns

This is my table:
CREATE TABLE [Test].[dbo].[MyTest]
(
[Id] BIGINT NOT NULL,
[FId] BIGINT NOT NULL,
[SId] BIGINT NOT NULL
);
And some data:
INSERT INTO [Test].[dbo].[MyTest] ([Id], [FId], [SId]) VALUES (1, 100, 11);
INSERT INTO [Test].[dbo].[MyTest] ([Id], [FId], [SId]) VALUES (2, 200, 12);
INSERT INTO [Test].[dbo].[MyTest] ([Id], [FId], [SId]) VALUES (3, 100, 21);
INSERT INTO [Test].[dbo].[MyTest] ([Id], [FId], [SId]) VALUES (4, 200, 22);
INSERT INTO [Test].[dbo].[MyTest] ([Id], [FId], [SId]) VALUES (5, 300, 13);
INSERT INTO [Test].[dbo].[MyTest] ([Id], [FId], [SId]) VALUES (6, 200, 12);
So I need 2 select query,
First Select FId, SId that like a distinct in both column so the result is:
100, 11
200, 12
100, 21
200, 22
300, 13
As you see the values of 200, 12 returned once.
Second query is the Id's of that columns whose duplicated in both FId, SId So the result is:
2
6
Does any one have any idea about it?
Standard SQL
SELECT
M.ID
FROM
( -- note all duplicate FID, SID pairs
SELECT FID, SID
FROM MyTable
GROUP BY FID, SID
HAVING COUNT(*) > 1
) T
JOIN -- back onto main table using these duplicate FID, SID pairs
MyTable M ON T.FID = M.FID AND T.SID = M.SID
Using windowing:
SELECT
T.ID
FROM
(
SELECT
ID,
COUNT(*) OVER (PARTITION BY FID, SID) AS CountPerPair
FROM
MyTable
) T
WHERE
T.CountPerPair > 1
First query:
SELECT DISTINCT Fid,SId
FROM MyTest
Second query:
SELECT DISTINCT a1.Id
FROM MyTest a1 INNER JOIN MyTest a2
ON a1.Fid = a2.Fid
AND a1.SId = a2.SId
AND a1.Id <> a2.Id
I cannot test them, but I think they should work...
first:
select distinct FId,SId from [Test].[dbo].[MyTest]
second query
select distinct t.Id
from [Test].[dbo].[MyTest] t
inner join [Test].[dbo].[MyTest] t2
on t.Id<>t2.Id and t.FId=t2.FId and t.SId=t2.SId
Part 1 is as mentioned above distinct.
This will resolve second part.
select id from [Test].[dbo].[MyTest] a
where exists(select 1 from [Test].[dbo].[MyTest] where a.[SId] = [SId] and a.[FId] = [FId] and a.id <> id)

SQL CTE counting childs recursion

I'd like (using cte) to count children in table in that way to have at parent level number of all children including theirs children. Is there any sample available?
CREATE TABLE t_parent (id INT NOT NULL PRIMARY KEY, parentID INT NOT NULL)
INSERT
INTO t_parent
VALUES (1, 0)
INSERT
INTO t_parent
VALUES (2, 1)
INSERT
INTO t_parent
VALUES (3, 1)
INSERT
INTO t_parent
VALUES (4, 2)
INSERT
INTO t_parent
VALUES (5, 1)
INSERT
INTO t_parent
VALUES (6, 5)
INSERT
INTO t_parent
VALUES (7, 5);
WITH q AS
(
SELECT id, parentId
FROM t_parent
UNION ALL
SELECT p.id, p.parentID
FROM q
JOIN t_parent p
ON p.id = q.parentID
)
SELECT id, COUNT(*)
FROM q
GROUP BY
id