Group by clause not returning the desired result - sql

I have the following query that will pull the status of the courses enrolled for each user. I need to display a single record for that user that will represent the overall status of the courses. As you can see in the query below, it uses case statement to decide the prioritisation of the status. I have tried to use a group by clause but it still shows all the courses in the resultset. Could someone let me know what am I doing wrong in the query
DECLARE #Rep1 INT;
SET #Rep1 = 13119;
SELECT
cr.[CourseID]
, cr.[UserID]
,u.[Code]
,u.[DisplayName]
,t.[Name]
,cr.[CourseResultStatusID] AS [CourseResultStatusID]
,case
when min(case when crs.[Description] = 'Complete' then 1 else 0 end) = 1
then 'Complete'
when max(case when crs.[Description] = 'Fail' then 1 else 0 end) = 1
then 'Fail'
when max(case when crs.[Description] = 'Expired' then 1 else 0 end) = 1
then 'Expired'
when max(case when crs.[Description] = 'In Progress' then 1 else 0 end) = 1
then 'In Progress'
end as [CourseResultStatusDescription]
,c.[PointsRequired]
,cr.[ExpiryDateTime]
FROM [training].[CourseResult] cr
INNER JOIN [training].[Course] c
ON cr.[CourseID] = c.[ID] and c.[IsOptional] = 0 -- and cr.ExpiryDateTime > GetDate() and cr.ExpiryDateTime <= dateadd(dd,30,getdate())
INNER JOIN [training].[CourseResultStatus] crs
ON cr.[CourseResultStatusID] = crs.[ID]
INNER JOIN org.RepresentativeTierHistory rth on rth.RepresentativeID = cr.[UserID] and GetDate() between rth.StartDate and rth.EndDate
INNER JOIN org.tier t on t.ID = rth.TierID
LEFT JOIN [org].[User] u
ON u.[ID] = cr.[UserID]
WHERE cr.[UserID] IN (
SELECT hd.DescendantId FROM org.HierarchyDescendant hd WHERE hd.RepresentativeId = #Rep1 UNION ALL SELECT #Rep1 -- for management exchange info
)
group by cr.[CourseID], cr.[UserID], u.[Code], u.[DisplayName], t.[Name], [CourseResultStatusID],c.[PointsRequired],cr.[ExpiryDateTime]
The result of the query is below. As you can see there are 24 records. It is currently showing all the 6 courses per user. It should ideally only only 4 records.
REsultset that I am looking for

Related

How to join two sub queries and find the difference of attribute values in SQL

From the below query, I want to know how to combine these two sub-queries and also find the difference of values of the attributes specified for two different years.
I had tried joining them using joins, but I do not have a common attribute on which I can combine them as they are for two different years. I need to find the difference in order to compute the change as seen in the attached picture. Can anyone help?
(select (CONVERT(INT,SUBSTRING(ACADEMIC_PERIOD,1,4)))-1 as 'Year1',
SUM(CASE WHEN dh.HOME = 'f' THEN 1 else 0 END) as 'foriegn1',
SUM(CASE WHEN dh.HOME = 'o' THEN 1 else 0 END) AS 'outofstate1',
SUM(CASE WHEN dh.HOME = 'i' THEN 1 else 0 END) AS 'texas1',
SUM(CASE WHEN dh.HOME = 'u' THEN 1 else 0 END) AS 'Unknown1',
COUNT(*) as Totalenrollment
FROM dw_enrollment_F d
inner join dim_Time t
on d.TIME_KEY = t.TIME_KEY
inner join dim_Home dh
on d.HOME_KEY = dh.HOME_KEY
inner join dim_Student_Level sl
on d.STUDENT_LEVEL_KEY = sl.STUDENT_LEVEL_KEY
where t.ACADEMIC_PERIOD_all =20162
and t.ACADEMIC_PERIOD_ALL not in('20157','20165','20168','20169','20167')
GROUP BY (CONVERT(INT,SUBSTRING(ACADEMIC_PERIOD,1,4)) ) -1)
(select (CONVERT(INT,SUBSTRING(ACADEMIC_PERIOD,1,4)))-1 as 'Year2',
SUM(CASE WHEN dh.HOME = 'f' THEN 1 else 0 END) as 'foriegn2',
SUM(CASE WHEN dh.HOME = 'o' THEN 1 else 0 END) AS 'outofstate2',
SUM(CASE WHEN dh.HOME = 'i' THEN 1 else 0 END) AS 'texas2',
SUM(CASE WHEN dh.HOME = 'u' THEN 1 else 0 END) AS 'Unknown2',
COUNT(*) as Totalenrollment2
FROM dw_enrollment_F d
inner join dim_Time t
on d.TIME_KEY = t.TIME_KEY
inner join dim_Home dh
on d.HOME_KEY = dh.HOME_KEY
inner join dim_Student_Level sl
on d.STUDENT_LEVEL_KEY = sl.STUDENT_LEVEL_KEY
where t.ACADEMIC_PERIOD_all = 20172
and t.ACADEMIC_PERIOD_ALL not in('20157','20165','20168','20169','20167')
GROUP BY (CONVERT(INT,SUBSTRING(ACADEMIC_PERIOD,1,4)) ) -1)

Sum values are inflated when I join another table

I have two queries that return the result sets I want, it is one row per user per day. The problem is that when I try to join the two queries, by userid, I get inflated results because the user is in the table multiple times for each day. How do I join them and avoid having the inflated results?
**Query 1**
SELECT AAL.UserID
, SUM(AAL.Dur)/60 AS 'LIM'
, SUM(CASE When AAL.DUR = 'av' then AAL.Dur/60
Else 0 END) AS 'AVAIL'
FROM WG
INNER JOIN AAL
on WG.UserID=AAL.UserID
and WG.SiteID=AAL.SiteID
WHERE WG.WG = 'OP'
AND DATEDIFF(day,AAL.Date,GETDATE()) = 1
GROUP BY AAL.UserID
**Query 2**
SELECT R.UserID
, SUM(CASE When R.StID = 4 then 1
Else 0 End) AS 'Rf Ct'
FROM R
INNER JOIN WG
on R.UserID = WG.UserID
WHERE WG.WG = 'OP'
AND DATEDIFF(day,R.Date,GETDATE()) = 1
GROUP BY R.UserID
**JOIN ATTEMPT**
SELECT AAL.UserID
, SUM(AAL.Dur)/60 AS 'LIM'
, SUM(CASE When AAL.DUR = 'av' then AAL.Dur/60
Else 0 END) AS 'AVAIL'
, SUM(CASE When R.StID = 4 then 1
Else 0 End) AS 'Rf Ct'
FROM WG
INNER JOIN AAL
on WG.UserID=AAL.UserID
and WG.SiteID=AAL.SiteID
INNER JOIN R
on AAL.UserID=R.UserID
WHERE WG.WG = 'OP'
AND DATEDIFF(day,AAL.Date,GETDATE()) = 1
GROUP BY AAL.UserID
You are not using WB in the query. So, just use exists or in:
SELECT R.UserID, SUM(CASE When R.StID = 4 then 1 Else 0 End) AS 'Rf Ct'
FROM R
WHERE DATEDIFF(day, R.Date, GETDATE()) = 1 AND
EXISTS (SELECT 1 FROM WB WHERE R.UserID = WG.UserID AND WG.WG = 'OP')
GROUP BY R.UserID;
You should understand the reason why the number gets multiplied -- multiple rows in WG correspond to a single R.ID.

Using sub query in in group by sql server

Hello everyone I would like to use sub query as code bellow but it get error I would like to to ask you that I how can i do this. Thanks!
SELECT HR_EMPMAST.DEPT,HR_DEPARTMENT.DESCRIPTION AS DEPARTMENT,HR_JOBFUNCTION.CODE,HR_JOBFUNCTION.DESCRIPTION AS POSITION,
COUNT(HR_EMPMAST.EMPCODE) ACTUAL,
SUM(CASE WHEN HR_EMPMAST.SEX = 'M' THEN 1 ELSE 0 END) AS M,
SUM(CASE WHEN HR_EMPMAST.SEX = 'F' THEN 1 ELSE 0 END) AS F,SUM(CASE WHEN HR_EMPMAST.EMPTYPE='LOCAL' THEN 1 ELSE 0 END) AS LOCALEMP,
SUM(CASE WHEN HR_EMPMAST.EMPTYPE='EXPAT' THEN 1 ELSE 0 END) AS EXPATEMP--,
(SELECT EMPNO FROM HR_HEADCOUNT WHERE POSITION=HR_EMPMAST.JOBCODE AND INMONTH=1 AND INYEAR=2017) AS EMPNO
FROM HR_EMPMAST
LEFT JOIN HR_DEPARTMENT
ON HR_EMPMAST.DEPT = HR_DEPARTMENT.CODE
LEFT JOIN HR_JOBFUNCTION
ON HR_EMPMAST.JOBCODE=HR_JOBFUNCTION.CODE
WHERE HR_EMPMAST.CAREERDESC <> 'TERMIMATE'
GROUP BY HR_EMPMAST.DEPT,HR_DEPARTMENT.DESCRIPTION,HR_JOBFUNCTION.CODE,HR_JOBFUNCTION.DESCRIPTION
At first, please, use table aliases.
Second: you need to add ALL columns from SELECT to GROUP BY (but not ones in SUM and COUNT functions).
Third: you got strange EMPNO select. Maybe better way is to use JOIN?
Try to use this one:
SELECT e.DEPT,
d.DESCRIPTION AS DEPARTMENT,
jf.CODE,
jf.DESCRIPTION AS POSITION,
COUNT(e.EMPCODE) ACTUAL,
SUM(CASE WHEN e.SEX = 'M' THEN 1 ELSE 0 END) AS M,
SUM(CASE WHEN e.SEX = 'F' THEN 1 ELSE 0 END) AS F,
SUM(CASE WHEN e.EMPTYPE='LOCAL' THEN 1 ELSE 0 END) AS LOCALEMP,
SUM(CASE WHEN e.EMPTYPE='EXPAT' THEN 1 ELSE 0 END) AS EXPATEMP,
hc.EMPNO
FROM HR_EMPMAST e
LEFT JOIN HR_DEPARTMENT d
ON e.DEPT = d.CODE
LEFT JOIN HR_JOBFUNCTION jf
ON e.JOBCODE = jf.CODE
LEFT JOIN HR_HEADCOUNT hc
ON hc.POSITION = e.JOBCODE AND hc.INMONTH=1 AND hc.INYEAR=2017
WHERE e.CAREERDESC <> 'TERMIMATE'
GROUP BY e.DEPT,
d.DESCRIPTION,
jf.CODE,
jf.DESCRIPTION,
hc.EMPNO
Try this below query or include HR_EMPMAST.JOBCODE in group by clause...
SELECT HR_EMPMAST.DEPT,HR_DEPARTMENT.DESCRIPTION AS DEPARTMENT,HR_JOBFUNCTION.CODE,HR_JOBFUNCTION.DESCRIPTION AS POSITION,
COUNT(HR_EMPMAST.EMPCODE) ACTUAL,
SUM(CASE WHEN HR_EMPMAST.SEX = 'M' THEN 1 ELSE 0 END) AS M,
SUM(CASE WHEN HR_EMPMAST.SEX = 'F' THEN 1 ELSE 0 END) AS F,SUM(CASE WHEN HR_EMPMAST.EMPTYPE='LOCAL' THEN 1 ELSE 0 END) AS LOCALEMP,
SUM(CASE WHEN HR_EMPMAST.EMPTYPE='EXPAT' THEN 1 ELSE 0 END) AS EXPATEMP,
em.EMPNO AS EMPNO
FROM HR_EMPMAST
LEFT JOIN (SELECT POSITION, EMPNO FROM HR_HEADCOUNT WHERE INMONTH=1 AND INYEAR=2017) em on em.POSITION=HR_EMPMAST.JOBCODE
LEFT JOIN HR_DEPARTMENT
ON HR_EMPMAST.DEPT = HR_DEPARTMENT.CODE
LEFT JOIN HR_JOBFUNCTION
ON HR_EMPMAST.JOBCODE=HR_JOBFUNCTION.CODE
WHERE HR_EMPMAST.CAREERDESC <> 'TERMIMATE'
GROUP BY HR_EMPMAST.DEPT,HR_DEPARTMENT.DESCRIPTION,HR_JOBFUNCTION.CODE,HR_JOBFUNCTION.DESCRIPTION

SQL Server Converting Rows to Columns

I am currently extracting data using 3 different tables, and below is the output.
Current Result:
Query Used:
SELECT
dbo.TableB.TrackingID, dbo.TableA.FinancialID,
dbo.TableA.ParcelCode, dbo.TableA.TotalAmount,
dbo.TableB.FinanceType, dbo.TableB.TransactionType,
dbo.TableC.CustID
FROM
dbo.TableA
INNER JOIN
dbo.TableB ON dbo.TableA.FinancialID = dbo.TableB.FinancialID
INNER JOIN
dbo.TableC ON dbo.TableB.TrackingID = dbo.TableC.TrackingID
WHERE
(dbo.TableB.TrackingID = '17006218AU')
I would like to have the following output:
Desired Output:
You can get the output you desire with grouping and some CASE statements inside SUM aggregate functions:
SELECT
dbo.TableB.TrackingID,
dbo.TableA.ParcelCode,
dbo.TableC.CustID,
SUM(CASE WHEN dbo.TableB.FinanceType = 'Invoice' THEN dbo.TableA.TotalAmount ELSE 0 END) AS TotalAmount,
SUM(CASE WHEN dbo.TableB.FinanceType = 'Invoice' AND TransType='Card' THEN dbo.TableA.TotalAmount ELSE 0 END) AS CardInvoice,
SUM(CASE WHEN dbo.TableB.FinanceType = 'Invoice' AND TransType='Cash' THEN dbo.TableA.TotalAmount ELSE 0 END) AS CashInvoice,
SUM(CASE WHEN dbo.TableB.FinanceType = 'PaymentRecepit' THEN dbo.TableA.TotalAmount ELSE 0 END) AS CardPaymentRecepit
FROM dbo.TableA
INNER JOIN dbo.TableB ON dbo.TableA.FinancialID = dbo.TableB.FinancialID
INNER JOIN dbo.TableC ON dbo.TableB.TrackingID = dbo.TableC.TrackingID
WHERE
dbo.TableB.TrackingID = '17006218AU'
GROUP BY
dbo.TableB.TrackingID,
dbo.TableA.ParcelCode,
dbo.TableC.CustID

How can I add a sub-query to this cursor?

I tried to add a sub query (StoCount) to the following cursor:
DECLARE trans_cur CURSOR FOR
SELECT b.TransportNumber,
SUM(CASE WHEN a.DeliveryItemStatus = 'C' OR a.DeliveryItemStatus = 'V' THEN 1 ELSE 0 END) AS Completed,
COUNT(*) AS Total,
SUM(CASE WHEN a.DeliveryItemStatus = 'F' THEN 1 ELSE 0 END) AS Missing,
(SELECT COUNT(*) FROM StorageTransportOrderItem WHERE DeliveryNumber = a.DeliveryNumber AND DeliveryItemNumber = a.DeliveryItemNumber) As StoCount
FROM DeliveryItem a
INNER JOIN TransportItem b on a.DeliveryNumber = b.DeliveryNumber
INNER JOIN Material c on a.MaterialNumber = c.MaterialNumber
INNER JOIN Transport d on b.TransportNumber = d.TransportNumber
WHERE a.StorageLocationNumber IS NOT NULL
AND a.Deleted <> 1
AND c.CommissioningArea LIKE #commissioningArea
AND d.TransportStatus < 70
GROUP BY b.TransportNumber
but when I always get the error message:
Msg 8120, Level 16, State 1, Procedure sp_CalculateTransportProgress,
Line 41 Column 'DeliveryItem.DeliveryNumber' is invalid in the select
list because it is not contained in either an aggregate function or
the GROUP BY clause. Msg 8120, Level 16, State 1, Procedure
sp_CalculateTransportProgress, Line 41 Column
'DeliveryItem.DeliveryItemNumber' is invalid in the select list
because it is not contained in either an aggregate function or the
GROUP BY clause.
My goal is to add this single column (StoCount) to the cursor without modifying the query too much.
Is that possible?
You could move the correlated subquery to a cross apply
SELECT b.TransportNumber,
SUM(CASE WHEN a.DeliveryItemStatus = 'C' OR a.DeliveryItemStatus = 'V' THEN 1 ELSE 0 END) AS Completed,
COUNT(*) AS Total,
SUM(CASE WHEN a.DeliveryItemStatus = 'F' THEN 1 ELSE 0 END) AS Missing,
MAX(e.Freq) AS StoCount
FROM DeliveryItem a
INNER JOIN TransportItem b on a.DeliveryNumber = b.DeliveryNumber
INNER JOIN Material c on a.MaterialNumber = c.MaterialNumber
INNER JOIN Transport d on b.TransportNumber = d.TransportNumber
CROSS APPLY (
SELECT COUNT(*) freq FROM StorageTransportOrderItem s
WHERE s.DeliveryNumber = a.DeliveryNumber AND s.DeliveryItemNumber = a.DeliveryItemNumber
) e
WHERE a.StorageLocationNumber IS NOT NULL
AND a.Deleted <> 1
AND c.CommissioningArea LIKE #commissioningArea
AND d.TransportStatus < 70
GROUP BY b.TransportNumber
Edit by xsl:
I had to modify the query a bit, so that it returned the correct results for my database:
SELECT b.TransportNumber,
SUM(CASE WHEN a.DeliveryItemStatus = 'C' OR a.DeliveryItemStatus = 'V' THEN 1 ELSE 0 END) AS Completed,
COUNT(*) AS Total,
SUM(CASE WHEN a.DeliveryItemStatus = 'F' THEN 1 ELSE 0 END) AS Missing,
SUM(e.Freq) AS StoCount
FROM DeliveryItem a
INNER JOIN TransportItem b on a.DeliveryNumber = b.DeliveryNumber
INNER JOIN Material c on a.MaterialNumber = c.MaterialNumber
INNER JOIN Transport d on b.TransportNumber = d.TransportNumber
CROSS APPLY (
SELECT COUNT(1) freq FROM StorageTransportOrderItem s
WHERE
s.DeliveryNumber = a.DeliveryNumber
AND s.DeliveryItemNumber = a.DeliveryItemNumber
AND s.MaterialNumber = a.MaterialNumber
) e
WHERE a.StorageLocationNumber IS NOT NULL
AND a.Deleted <> 1
AND c.CommissioningArea LIKE #commissioningArea
AND d.TransportStatus < 70
GROUP BY b.TransportNumber