Not able to perform Query Grouping - sql

i want to rotate my table horizontally -
The tables are as follows:
Master_Choicecode
ChoiceCode MainCourseId CourseLevelId InstituteId
Master_MainCourse
MainCourseId MainCourseName CourseLevelId CourseProgram
11 x 1 abc
12 y 2 xyz
Master_CourseLevel
CourseLevelId CourseLevelName
1 deg
2 Dip
Master_Institute
Instituteid InstituteName Statusid
1001 Insti1 100
1002 Insti2 200
Master_InstituteStatus
StatusId StatusName
100 Status1
200 Status2
Now using all these tables i want to show this:
CourseProgram CourseLevelName Status1(from Master_InstituteStatus) Status2(from Master_InstituteStatus)
abc Deg Count of institutes belonging to status1 Count of institutes belonging to status2
Now this is what i have tried:
SELECT B.CourseProgram,C.CourseLevelName,
case when E.InstituteStatusName =' Status1' then COUNT(*) else null end as Status1,
case when E.InstituteStatusName =' Status2' then COUNT(*) else null end as Status2,
FROM Master_ChoiceCode A
inner join Master_MainCourse B on A.MainCourseID=B.MainCourseID
inner join Master_CourseLevel C on A.CourseLevelID=C.CourseLevelID
inner join Master_Institute D on A.InstituteID=D.InstituteID
inner join Master_InstituteStatus1 E on D.InstituteStatusID1=E.InstituteStatusID
where B.CourseLevelID IN(1,2)
GROUP BY B.CourseProgram,A.CourseLevelID,C.CourseLevelName,E.InstituteStatusName
order by B.CourseProgram,C.CourseLevelName;
But with this i get the output like this:
CourseProgram CourseLevelName Status1(from Master_InstituteStatus) Status2(from Master_InstituteStatus)
abc Deg Count of institutes belonging to status1
abc Deg Null Count of institutes belonging to status1
i got the solution to this is to use pivot...but i dont knw how shal i use Pivot with my current query.Please help me..

You're close, but grouping on E.InstituteStatusName means you will not be able to combine the data onto one row. Rather than use the case statement with a count, you could use a conditional sum.
SELECT B.CourseProgram,C.CourseLevelName,
sum(case when E.InstituteStatusName =' Status1' then 1 else 0 end) as Status1,
sum(case when E.InstituteStatusName =' Status2' then 1 else 0 end) as Status2,
FROM Master_ChoiceCode A
inner join Master_MainCourse B on A.MainCourseID=B.MainCourseID
inner join Master_CourseLevel C on A.CourseLevelID=C.CourseLevelID
inner join Master_Institute D on A.InstituteID=D.InstituteID
inner join Master_InstituteStatus1 E on D.InstituteStatusID1=E.InstituteStatusID
where B.CourseLevelID IN(1,2)
GROUP BY B.CourseProgram,A.CourseLevelID,C.CourseLevelName
order by B.CourseProgram,C.CourseLevelName;
This will effectively count the rows matching that status without the need for grouping on that field.

You could use the built in PIVOT function in SQL-Server:
WITH CTE AS
( SELECT B.CourseProgram,
C.CourseLevelName,
E.InstituteStatusName,
b.CourseLevelID
FROM Master_ChoiceCode A
INNER JOIN Master_MainCourse B
ON A.MainCourseID=B.MainCourseID
INNER JOIN Master_CourseLevel C
ON A.CourseLevelID=C.CourseLevelID
INNER JOIN Master_Institute D
ON A.InstituteID=D.InstituteID
INNER JOIN Master_InstituteStatus1 E
ON D.InstituteStatusID1=E.InstituteStatusID
WHERE B.CourseLevelID IN (1,2)
)
SELECT CourseProgram, CourseLevelName, [Status1], [Status2]
FROM CTE
PIVOT
( COUNT(CourseLevelID)
FOR InstituteStatusName IN ([Status1], [Status2])
) pvt
ORDER BY CourseProgram, CourseLevelName;

Related

Optimizing SQL query having DISTINCT keyword and functions

I have this query that generates about 40,000 records and the execution time of this query is about 1 minute 30 seconds.
SELECT DISTINCT
a.ID,
a.NAME,
a.DIV,
a.UID,
(select NAME from EMPLOYEE where UID= a.UID and UID<>'') as boss_id,
(select DATE(MAX(create_time)) from XYZ where XYZ_ID= 1 and id = a.ID) as TERM1,
(select DATE(MAX(create_time)) from XYZ where XYZ_ID= 2 and id = a.ID) as TERM2,
(select DATE(MAX(create_time)) from XYZ where XYZ_ID= 3 and id = a.ID) as TERM3,
(select DATE(MAX(create_time)) from XYZ where XYZ_ID= 4 and id = a.ID) as TERM4,
(select DATE(MAX(create_time)) from XYZ where XYZ_ID= 5 and id = a.ID) as TERM5,
(select DATE(MAX(create_time)) from XYZ where XYZ_ID= 6 and id = a.ID) as TERM6,
(select DATE(MAX(create_time)) from XYZ where XYZ_ID= 7 and id = a.ID) as TERM7,
(select DATE(MAX(create_time)) from XYZ where XYZ_ID= 8 and id = a.ID) as TERM8
FROM EMPLOYEE a
WHERE ID LIKE 'D%'
I tried using group by, different kinds of join to improve the execution time but couldn't succeed.Both the tables ABC and XYZ are indexed.
Also, I think that the root cause of this problem is either the DISTINCT keyword or the MAX function.
How can I optimize the above query to bring down the execution time to at least less than a minute?
Any help is appreciated.
Query is not tested, this is just an idea on how you could get this done in two different ways.
(SQL Server solutions here)
Using LEFT JOIN for each ID should look something like this:
SELECT a.ID,
a.NAME,
a.DIV,
a.UID,
b.Name as boss_id,
MAX(xyz1.create_time) as TERM1,
MAX(xyz2.create_time) as TERM2,
MAX(xyz3.create_time) as TERM3,
MAX(xyz4.create_time) as TERM4,
MAX(xyz5.create_time) as TERM5,
MAX(xyz6.create_time) as TERM6,
MAX(xyz7.create_time) as TERM7,
MAX(xyz8.create_time) as TERM8
FROM EMPLOYEE a
JOIN EMPLOYEE b on a.UID = b.UID and b.UID <> ''
LEFT JOIN XYZ xyz1 on a.ID = xyz1.ID and xyz1.XYZ_ID = 1
LEFT JOIN XYZ xyz2 on a.ID = xyz2.ID and xyz1.XYZ_ID = 2
LEFT JOIN XYZ xyz3 on a.ID = xyz3.ID and xyz1.XYZ_ID = 3
LEFT JOIN XYZ xyz4 on a.ID = xyz4.ID and xyz1.XYZ_ID = 4
LEFT JOIN XYZ xyz5 on a.ID = xyz5.ID and xyz1.XYZ_ID = 5
LEFT JOIN XYZ xyz6 on a.ID = xyz6.ID and xyz1.XYZ_ID = 6
LEFT JOIN XYZ xyz7 on a.ID = xyz7.ID and xyz1.XYZ_ID = 7
LEFT JOIN XYZ xyz8 on a.ID = xyz8.ID and xyz1.XYZ_ID = 8
WHERE a.ID LIKE 'D%'
GROUP BY a.ID, a.NAME, a.DIV, a.UID, b.Name
Using PIVOT would look something like this:
select * from (
SELECT DISTINCT
a.ID,
a.NAME,
a.DIV,
a.UID,
b.NAME as boss_id,
xyz.xyz_id,
xyz.create_time
FROM EMPLOYEE a
JOIN EMPLOYEE b on a.UID = b.UID and b.UID <> ''
LEFT JOIN (SELECT DATE(MAX(create_time)) create_time, XYZ_ID, ID
from XYZ
where XYZ_ID between 1 and 8
group by XYZ_ID, ID) xyz on a.ID = xyz1.ID
WHERE a.ID LIKE 'D%') src
PIVOT (
max(create_time) for xyz_id IN (['1'], ['2'], ['3'], ['4'],
['5'], ['6'], ['7'], ['8'])
) PIV
Give it a shot
I would recommend group by and conditional aggregation:
SELECT e.ID, e.NAME, e.DIV, e.UID,
DATE(MAX(CASE WHEN XYZ_ID = 1 THEN create_time END)) as term1,
DATE(MAX(CASE WHEN XYZ_ID = 2 THEN create_time END)) as term2,
DATE(MAX(CASE WHEN XYZ_ID = 3 THEN create_time END)) as term3,
DATE(MAX(CASE WHEN XYZ_ID = 4 THEN create_time END)) as term4,
DATE(MAX(CASE WHEN XYZ_ID = 5 THEN create_time END)) as term5,
DATE(MAX(CASE WHEN XYZ_ID = 6 THEN create_time END)) as term6,
DATE(MAX(CASE WHEN XYZ_ID = 7 THEN create_time END)) as term7,
DATE(MAX(CASE WHEN XYZ_ID = 8 THEN create_time END)) as term8
FROM EMPLOYEE e LEFT JOIN
XYZ
ON xyz.ID = e.id
WHERE e.ID LIKE 'D%'
GROUP BY e.ID, e.NAME, e.DIV, e.UID;
I don't understand the logic for boss_id, so I left that out. This should improve the performance significantly.

SQL - SUM within subquery

I have the following code that looks at the SalesVol of different products and groups it by transaction_week
SELECT a.transaction_week,
SUM(CASE WHEN record_type IN (6,37,13) THEN quantity ELSE 0 END) as SalesVol
FROM table 1 a
LEFT JOIN table 2 b ON b.Date = a.transaction_date
LEFT JOIN table 3 c ON c.sku = a.product
WHERE series in (62,236,501,52)
GROUP BY a.transaction_week
ORDER BY a.transaction_week
| tw | SalesVol |
| 1 | 4768 |
| 2 | 4567 |
| 3 | 4354 |
| 4 | 4678 |
I want to be able to have multiple subqueries where I change the series numbers for example.
SELECT a.transaction_week,
(SELECT SUM(CASE WHEN record_type IN (6,37,13) THEN quantity ELSE 0 END) as SalesVol
FROM table 1 a
LEFT JOIN table 2 b ON b.Date = a.transaction_date
LEFT JOIN table 3 c ON c.sku = a.product
WHERE series in (62,236,501,52)) as personal care
(SELECT SUM(CASE WHEN record_type IN (6,37,13) THEN quantity ELSE 0 END) as SalesVol
FROM table 1 a
LEFT JOIN table 2 b ON b.Date = a.transaction_date
LEFT JOIN table 3 c ON c.sku = a.product
WHERE series in (37,202,203,456)) as white goods
FROM table 1 a
LEFT JOIN table 2 b ON b.Date = a.transaction_date
LEFT JOIN table 3 c ON c.sku = a.product
GROUP BY a.transaction_week
ORDER BY a.transaction_week
I can't get the subqueries at work as it is giving me the overall sum value and not grouping it by transaction_week
Instead of using subqueries, add series to the condition of the CASE statements:
SELECT a.transaction_week,
sum(CASE WHEN series IN (62,236,501,52) AND record_type IN (6,37,13)
THEN quantity ELSE 0 END) as personal_care,
sum(CASE WHEN series IN (37,202,203,456) AND record_type IN (6,37,13)
THEN quantity ELSE 0 END) as white_goods
FROM table 1 a
LEFT JOIN table 2 b ON b.Date = a.transaction_date
LEFT JOIN table 3 c ON c.sku = a.product
GROUP BY a.transaction_week
ORDER BY a.transaction_week;
You just miss the a.transaction_week in you subquery. The JOIN in outer query is unneccessary.
SELECT a.transaction_week,
(
SELECT SUM(CASE WHEN record_type IN (6,37,13) THEN quantity ELSE 0 END) as SalesVol
FROM table 1 a2
LEFT JOIN table 2 b ON b.Date = a2.transaction_date
LEFT JOIN table 3 c ON c.sku = a2.product
WHERE series in (62,236,501,52) AND a2.transaction_week = a.transaction_week
) as personal care,
(
SELECT SUM(CASE WHEN record_type IN (6,37,13) THEN quantity ELSE 0 END) as SalesVol
FROM table 1 a 2
LEFT JOIN table 2 b ON b.Date = a2.transaction_date
LEFT JOIN table 3 c ON c.sku = a2.product
WHERE series in (37,202,203,456) AND a2.transaction_week = a.transaction_week
) as white goods
FROM table 1 a
GROUP BY a.transaction_week
ORDER BY a.transaction_week
Try this it would work fast as well as up to your requirement:
SELECT a.transaction_week ,
whitegoods.SalesVol AS 'White Goods' ,
personalcare.SalesVol1 AS 'Personal Care'
FROM table1 a
LEFT JOIN table2 b ON b.[Date] = a.transaction_date
LEFT JOIN table3 c ON c.sku = a.product
CROSS APPLY ( SELECT SUM(CASE WHEN record_type IN ( 6, 37, 13 )
THEN quantity
ELSE 0
END) AS SalesVol
FROM table1 a2
WHERE b.[Date] = a2.transaction_date
AND c.sku = a2.product
AND series IN ( 37, 202, 203, 456 )
AND a2.transaction_week = a.transaction_week
) whitegoods
CROSS APPLY ( SELECT SUM(CASE WHEN record_type IN ( 6, 37, 13 )
THEN quantity
ELSE 0
END) AS SalesVol1
FROM table1 a2
WHERE b.[Date] = a2.transaction_date
AND c.sku = a2.product
AND series IN ( 62, 236, 501, 52 )
AND a2.transaction_week = a.transaction_week
) personalcare
GROUP BY a.transaction_week
ORDER BY a.transaction_week
You should use the UNION operator. Please refer to the query below:
select a.transaction_week, SalesVol from
(SELECT a.transaction_week as transaction_week,
SUM(CASE WHEN record_type IN (6,37,13) THEN quantity ELSE 0 END) as SalesVol
FROM table 1 a
LEFT JOIN table 2 b ON b.Date = a.transaction_date
LEFT JOIN table 3 c ON c.sku = a.product
WHERE series in (62,236,501,52)
UNION
SELECT a.transaction_week as transaction_week,
SUM(CASE WHEN record_type IN (6,37,13) THEN quantity ELSE 0 END) as SalesVol
FROM table 1 a
LEFT JOIN table 2 b ON b.Date = a.transaction_date
LEFT JOIN table 3 c ON c.sku = a.product
WHERE series in (37,202,203,456)
) AS tbl1
GROUP BY tbl1.transaction_week
ORDER BY tbl1.transaction_week

Sql Query to find sum from different tables

Hi i require a help in writing a query.
Tables are:
tblStandard1students
tblStandard2students
tblStandard1students
tblDivision
tblCandidateinfo
tblStandard1students,tblStandard2students,tblStandard1studentstbl contain information about students enrolled in standard 1,2 and 3
tblStandars1students
Candid admitted
1 Y
2 N
3 Y
tblDivision contains only 2 columns
ID Division
1 A
2 B
3 C
tblCandidateinfo
Candid gender Division
1 M 1
2 F 2
and so on...
Now I want the table like this
Division Students(Standard1) Students(Standard2) Students(Standard3)
M F M F M F
------------------------------------------------------------------------
A 1 0 0 0 0 1
B 2 2 3 3 4 4
C 1 0 0 0 0 0
I tried this following query:
SELECT Division,
( SELECT count(*)
FROM tblStandard1students A
INNER JOIN tblCandidateinfo B ON A.Candid=B.Candid
INNER JOIN tblDivision C ON C.ID=B.Division) AS Students(Standard1),
( SELECT count(*)
FROM tblStandard2students A
INNER JOIN tblCandidateinfo B ON A.Candid=B.Candid
INNER JOIN tblDivision C ON C.ID=B.Division) AS Students(Standard2),
( SELECT count(*)
FROM tblStandard3students A
INNER JOIN tblCandidateinfo B ON A.Candid=B.Candid
INNER JOIN tblDivision C ON C.ID=B.Division ) AS Students(Standard3)
FROM tblDivision Z
but this is only half the query i din segregate it gender wise...help me to complete it.
;WITH combined AS
(
SELECT ci.Division, 'Students(Standard1) ' + ci.gender AS grp
FROM tblCandidateInfo ci
INNER JOIN tblStandard1students s ON ci.Candid = s.Candid
UNION ALL
SELECT ci.Division, 'Students(Standard2) ' + ci.gender AS grp
FROM tblCandidateInfo ci
INNER JOIN tblStandard2students s ON ci.Candid = s.Candid
UNION ALL
SELECT ci.Division, 'Students(Standard3) ' + ci.gender AS grp
FROM tblCandidateInfo ci
INNER JOIN tblStandard1studentstbl s ON ci.Candid = s.Candid
)
SELECT Division,
[Students(Standard1) M], [Students(Standard1) F],
[Students(Standard2) M], [Students(Standard2) F],
[Students(Standard3) M], [Students(Standard3) F]
FROM
(
SELECT d.Division, grp
FROM tblDivision d
LEFT OUTER JOIN combined c ON d.ID = c.Division
) x
PIVOT
(
COUNT(grp)
FOR grp IN ([Students(Standard1) M], [Students(Standard1) F],
[Students(Standard2) M], [Students(Standard2) F],
[Students(Standard3) M], [Students(Standard3) F])
) y
ORDER BY Division
SELECT divison.Division ,IFNULL(stander1.M,0),IFNULL(stander1.F,0) FROM test.tblDivision divison
Left join (SELECT division ,count( case gender when 'M' then 1 else null end) as M,count( case gender when 'F' then 1 else null end) as F
FROM
test.tblCandidateinfo tc inner join test.tblStandars1students ts1
ON tc.Candid=ts1.Candid
group by division) as stander1 on stander1.division= divison.id
group by divison.id
;
Insted of IFNULL use ISNULL and
take left join for all standar tables

Get Total Branch wise in SQL Server

SELECT b.BranchName ,
pm.AgreementValue
FROM dbo.Member AS m
INNER JOIN dbo.PlanMaster AS pm ON ( m.PlanId = pm.PlanId )
INNER JOIN dbo.Branch AS b ON ( b.BranchId = m.BranchId )
this is the result of above query
BranchName AgreementValue
------------------------------
abc 60000.00
abc 36000.00
abc 36000.00
xyz 20000.00
xyz 10000.00
now i want to get to total of AgreementValue BranchName wise..thanks for help
GROUP BY b.BranchName with SUM like so:
SELECT b.BranchName ,
SUM(pm.AgreementValue) TotalValue
FROM dbo.Member AS m
INNER JOIN dbo.PlanMaster AS pm ON ( m.PlanId = pm.PlanId )
INNER JOIN dbo.Branch AS b ON ( b.BranchId = m.BranchId )
GROUP BY b.BranchName;
If you are trying to get the total on each row, then use the window function for sum():
SELECT b.BranchName ,
pm.AgreementValue,
sum(pm.AgreementValue) over (partition by b.BranchName) as BranchTotal
FROM dbo.Member AS m
INNER JOIN dbo.PlanMaster AS pm ON ( m.PlanId = pm.PlanId )
INNER JOIN dbo.Branch AS b ON ( b.BranchId = m.BranchId )

To find the sum of rows based on some condition in sql server

Hi i am not able to find sum of rows in this query -
The tables are as follows:
Master_Choicecode
ChoiceCode MainCourseId CourseLevelId InstituteId
Master_MainCourse
MainCourseId MainCourseName CourseLevelId CourseProgram
11 x 1 abc
12 y 2 xyz
Master_CourseLevel
CourseLevelId CourseLevelName
1 deg
2 Dip
Master_Institute
Instituteid InstituteName Statusid
1001 Insti1 100
1002 Insti2 200
Master_InstituteStatus
StatusId StatusName
100 Status1
200 Status2
Now using all these tables i want to show this:
CourseProgram CourseLevelName Status1(from Master_InstituteStatus) Status2(from Master_InstituteStatus) TotalInstitutes
abc Deg Count(status1institute) Count(status2institute) Total(status1+status2)
Now this is what i have tried:
SELECT B.CourseProgram,C.CourseLevelName,
sum(case when E.InstituteStatusName =' Status1' then 1 else 0 end )as Status1,
sum(case when E.InstituteStatusName =' Status2' then 1 else 0 end ) as Status2,
FROM Master_ChoiceCode A
inner join Master_MainCourse B on A.MainCourseID=B.MainCourseID
inner join Master_CourseLevel C on A.CourseLevelID=C.CourseLevelID
inner join Master_Institute D on A.InstituteID=D.InstituteID
inner join Master_InstituteStatus1 E on D.InstituteStatusID1=E.InstituteStatusID
where B.CourseLevelID IN(1,2)
GROUP BY B.CourseProgram,A.CourseLevelID,C.CourseLevelName,E.InstituteStatusName
order by B.CourseProgram,C.CourseLevelName;
With this i get the output like this:
CourseProgram CourseLevelName Status1(from Master_InstituteStatus) Status2(from Master_InstituteStatus)
abc deg 10(count of status1) 20(count of status2)
Please tell me how should i find the sum of Status1 column and Status2 column so that i can get the last column as Total_No_ of_institutes
SELECT B.CourseProgram,C.CourseLevelName,
sum(case when E.InstituteStatusName =' Status1' then 1 else 0 end )as Status1,
sum(case when E.InstituteStatusName =' Status2' then 1 else 0 end ) as Status2,
sum(case when E.InstituteStatusName IN (' Status1', ' Status2') then 1 else 0 end ) as Total_No_of_institutes,
FROM Master_ChoiceCode A
inner join Master_MainCourse B on A.MainCourseID=B.MainCourseID
inner join Master_CourseLevel C on A.CourseLevelID=C.CourseLevelID
inner join Master_Institute D on A.InstituteID=D.InstituteID
inner join Master_InstituteStatus1 E on D.InstituteStatusID1=E.InstituteStatusID
where B.CourseLevelID IN(1,2)
GROUP BY B.CourseProgram,A.CourseLevelID,C.CourseLevelName,E.InstituteStatusName
order by B.CourseProgram,C.CourseLevelName;
SELECT B.CourseProgram,C.CourseLevelName,
sum(case when E.StatusName=' Status1' then 1 else 0 end )as Status1,
sum(case when E.StatusName=' Status2' then 1 else 0 end ) as Status2,
FROM Master_ChoiceCode A
inner join Master_MainCourse B on A.MainCourseID=B.MainCourseID
inner join Master_CourseLevel C on A.CourseLevelID=C.CourseLevelID
inner join Master_Institute D on A.InstituteID=D.InstituteID
inner join Master_InstituteStatus1 E on D.InstituteStatusID1=E.InstituteStatusID
where B.CourseLevelID IN(1,2)
GROUP BY B.CourseProgram,A.CourseLevelID,C.CourseLevelName,E.InstituteStatusName
order by B.CourseProgram,C.CourseLevelName;