Stored procedure looping error - sql

I'm new to stored procedures and I'm getting an error:
Subquery returned more than 1 value. This is not permitted when the subquery follows
E_R_Main is header table, E_R_Detail is detail table. Both tables are joined using R_ID.
My expected output :
Code:
DECLARE #LALA NVARCHAR(10);
DECLARE #LALA2 NVARCHAR(10);
CREATE TABLE #TEMP
(
EMP_NAME nvarchar(256),
Total_Pending nvarchar(256),
Reschedule_Task nvarchar(256),
New_Task nvarchar(256)
)
--select * from #TEMP
INSERT INTO #TEMP(EMP_NAME, Total_Pending)
SELECT DISTINCT(PIC), COUNT(PIC) AS COUNTTT
FROM E_R_DetailT A
INNER JOIN E_R_MainT b ON a.R_ID = b.R_ID
WHERE PIC IS NOT NULL
GROUP BY T_EMP_ID
SET #LALA = (SELECT COUNT(B.R_ID) AS TEXSST
FROM E_R_MainT A
INNER JOIN E_R_DetailT B ON A.R_ID = B.R_ID
WHERE (b.R_Y = '[Redo]' OR R_Y_N = '[r]')
AND (STAT = 'S_5' OR STAT = 'S_3')
GROUP BY T_EMP_ID )
SET #LALA2 = (SELECT DISTINCT(B.T_EMP_ID)
FROM E_R_MainT A
INNER JOIN E_R_DetailT B ON A.R_ID = B.R_ID
WHERE (b.R_Y = '[Redo]' OR R_Y_N = '[r]')
AND (STAT = 'S_5' OR STAT = 'S_3')
GROUP BY T_EMP_ID)
UPDATE #TEMP
SET Reschedule_Task = #LALA
WHERE #TEMP.EMP_NAME = #LALA2
--SELECT * FROM #TEMP
SELECT
B.Name, a.Total_Pending, New_Task,
CASE
WHEN Reschedule_Task IS NULL
THEN 0
ELSE Reschedule_Task
END AS User_Feedback
FROM
#TEMP A
INNER JOIN
UserInfo B ON A.EMP_NAME = B.EmployeeNo
DROP TABLE #TEMP

Your code seems way more complicated than needed to solve this problem. It looks like a JOIN with conditional aggregation:
select d.pic,
sum(case when m.status in ('S_1', 'S_3', 'S_4') then 1 else 0 end) as Pending,
sum(case when m.status = 'S_1' then 1 else 0 end) as New,
sum(case when m.status = 'S_3' then 1 else 0 end) as Reschedule,
sum(case when m.status = 'S_4' then 1 else 0 end) as Completed
from e_r_main m join
e_r_detail d
on m.r_id = d.r_id
group by d.pic;

Related

Group by clause not returning the desired result

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

what is mean by select * from ... where 1=condition in sql

I have seen in sql procedures: select * from table 1=1
SELECT DISTINCT b.book_number AS book_number,
b.book_title AS b.nook_title,
o.order_summary AS order_summary,
otm.total_amt
FROM books b(nolock)
INNER JOIN books_detail bd (nolock)
ON b.b_id = bd.b_id
INNER JOIN order o(nolock)
ON o.author_id = b.author_id
INNER JOIN order_master otm(nolock)
ON otm.order_number = otm.order_number
WHERE b.status = 'AVAILABLE'
AND ( otm.cnt > 4
OR otm.total > 150
OR 1 = ( CASE
WHEN #order = 'Any' THEN 0
WHEN #order = 'devotional' THEN 1
WHEN #order = 'investigation' THEN 1
ELSE 0
END ) )
my question here is what is the use of
OR 1 = ( CASE
WHEN #order = 'Any' THEN 0
WHEN #order = 'devotional' THEN 1
WHEN #order = 'investigation' THEN 1
ELSE 0
END ) )
If you follow the logical process flow you will see that the result of the case expression is either a 1 or 0 and the where clause is checking to see if 1 is returned.
In this particular instance, the expression is equivalent to:
#order in ('devotional', 'investigation')

SQL Set SUM IN Update with inner join

I'm having issues updating a column of my temp table in sql.
Here is my query.
UPDATE #temp
SET #temp.totalcredit =
SUM(CASE WHEN c.ratetype = 'NRP' THEN d.Credit ELSE d.Credit * f.equivalent END)
from #temp a
inner join theader b
on a.IV = b.docnum
and a.SOANUM is not null
inner join BillRun c
on rtrim(replace(
case
when charindex('-',b.comments,1) <> 6 AND charindex('-',b.comments,1) > 0
then substring(ltrim(rtrim(b.comments)),1, charindex('-',b.comments,1)-1)
when charindex(' ',b.comments,1) > 0
then substring(ltrim(rtrim(b.comments)),1, charindex(' ',b.comments,1)-1)
else left(ltrim( comments),10)
end,'''','')) = c.BillRunCode OR c.BillRunCode = b.runcode
inner join BillRunProposalSummary d
on c.BillRunCode = d.BillRunCode and d.Credit > 0
inner join configforexrates f
on f.date = c.DocDate
Is there a way to get the sum and still update it this way?
Have you tried something like this?
UPDATE A
SET A.TotalCredit = T.Credit
FROM #temp A
CROSS APPLY
(
SELECT SUM(
CASE C.RateType
WHEN 'NRP' THEN D.Credit
ELSE
D.Credit * F.Equivalent
END) AS Credit
FROM Theader B
inner join BillRun c
on rtrim(replace(
case
when charindex('-',b.comments,1) <> 6 AND charindex('-',b.comments,1) > 0
then substring(ltrim(rtrim(b.comments)),1, charindex('-',b.comments,1)-1)
when charindex(' ',b.comments,1) > 0
then substring(ltrim(rtrim(b.comments)),1, charindex(' ',b.comments,1)-1)
else left(ltrim( comments),10)
end,'''','')) = c.BillRunCode OR c.BillRunCode = b.runcode
inner join BillRunProposalSummary d
on c.BillRunCode = d.BillRunCode and d.Credit > 0
inner join configforexrates f
on f.date = c.DocDate
WHERE B.DocNum = A.IV
) T
WHERE A.SOANUM IS NOT NULL
;

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

SQL advanced query - problem with single row aggregate sub query in select clause

I'm trying to execute the following query but I receive a runtime error stating that:
"The column is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause."
Line number two below is the one that fails with the error above. I don't understand why this query doesn't execute as no grouping should be required in line two as the output is just count(*), any clue as to what I need to change to get this working?
SELECT #lessonPlans = COUNT(*)
, #lessonPlanResources = (SELECT COUNT(*) FROM dbo.LessonPlanResource lpr where lpr.LessonPlanId = a.LessonPlanId )
FROM
(
SELECT DISTINCT lpt.LessonPlanId
FROM dbo.LearningTargetBreakout ltb
JOIN dbo.LessonPlanLearningTarget lpt
on lpt.LearningTargetId = ltb.LearningTargetId
WHERE (CASE
WHEN ltb.LearningTargetId = #learningTargetId and #learningTargetId is not null THEN 1
WHEN ltb.CategoryId = #categoryId and #categoryId is not null THEN 1
WHEN ltb.Grade = #grade and #grade is not null THEN 1
WHEN ltb.SubjectId = #subjectId and #subjectId is not null THEN 1
ELSE 0 END) = 1
) a
[Edit]
Using a slight variation of Zeb's solution - here is the revised code that I ended up using, which yields one row with aggregates, which is what I was after.
SELECT #lessonPlans = ISNULL(COUNT(*), 0)
, #lessonPlanResources = ISNULL(SUM(a.ResCount), 0)
FROM
(
SELECT DISTINCT lpt.LessonPlanId, lpr.ResCount
FROM dbo.LearningTargetBreakout ltb
JOIN dbo.LessonPlanLearningTarget lpt
on lpt.LearningTargetId = ltb.LearningTargetId
JOIN (SELECT LessonPlanId, COUNT(*) ResCount FROM dbo.LessonPlanResource lpr GROUP BY LessonPlanId) lpr
ON lpr.LessonPlanId = lpt.LessonPlanId
WHERE (CASE
WHEN ltb.LearningTargetId = #learningTargetId and #learningTargetId is not null THEN 1
WHEN ltb.CategoryId = #categoryId and #categoryId is not null THEN 1
WHEN ltb.GradeId = #grade and #grade is not null THEN 1
WHEN ltb.SubjectId = #subjectId and #subjectId is not null THEN 1
ELSE 0 END) = 1
) a
My guess would be that the #lessonPlanResources is linked to the LessonPlanId, which is not aggregate.
My solution would be to JOIN to that subtable, and have the returned column be a COUNT of it.
SELECT #lessonPlans = COUNT(*)
, #lessonPlanResources = SUM(zlpr.reses)
FROM
(
SELECT DISTINCT lpt.LessonPlanId, zlpr.reses
FROM dbo.LearningTargetBreakout ltb
JOIN dbo.LessonPlanLearningTarget lpt
on lpt.LearningTargetId = ltb.LearningTargetId
JOIN (SELECT LessonPlanId, COUNT(*) reses FROM dbo.LessonPlanResource lpr) zlpr
ON zlpr.LessonPlanId = lpt.LessonPlanId
WHERE (CASE
WHEN ltb.LearningTargetId = #learningTargetId and #learningTargetId is not null THEN 1
WHEN ltb.CategoryId = #categoryId and #categoryId is not null THEN 1
WHEN ltb.Grade = #grade and #grade is not null THEN 1
WHEN ltb.SubjectId = #subjectId and #subjectId is not null THEN 1
ELSE 0 END) = 1
) a
Your count(*) is an aggregate function and the expression for #lessonPlanResources is not (even though it's a query with COUNT(*)). Therefore, the expression must be included in the GROUP BY clause.
You can use common table expressions(CTE), which may help with readability too.
WITH LPR_CTE as
(
SELECT LessonPlanId, COUNT(*) as LessonPlanResourcesCount
FROM dbo.LessonPlanResource
GROUP BY LessonPlanId
),
LP_CTE(
SELECT lpt.LessonPlanId, COUNT(*) as LessonPlansCount
FROM dbo.LearningTargetBreakout ltb
JOIN dbo.LessonPlanLearningTarget lpt
on lpt.LearningTargetId = ltb.LearningTargetId
WHERE (CASE
WHEN ltb.LearningTargetId = #learningTargetId and #learningTargetId is not null THEN 1
WHEN ltb.CategoryId = #categoryId and #categoryId is not null THEN 1
WHEN ltb.Grade = #grade and #grade is not null THEN 1
WHEN ltb.SubjectId = #subjectId and #subjectId is not null THEN 1
ELSE 0 END) = 1
)
SELECT #lessonPlans = LP_CTE.LessonPlansCount
, #lessonPlanResources = LPR_CTE.LessonPlanResourcesCount
FROM LP_CTE
JOIN LPR_CTE on LP_CTE.LessonPlanId = LPR_CTE.LessonPlanId
You would need to GROUP BY a.LessonPlanId and possibly change the first COUNT(*) to COUNT(*) OVER() depending on what exactly you are trying to do.
But that will presumably give multiple rows of results that you are then trying to assign to a single set of scalar variables. What are you trying to do exactly?
The issue is that you are using a grouping function (count) with this subquery:SELECT COUNT(*) FROM dbo.LessonPlanResource lpr where lpr.LessonPlanId = a.LessonPlanId
However the reference to a.LessonPlanId is a non-grouped field. Group your query and you can reference that field.
Try this:
SELECT #lessonPlans = COUNT(*)
, #lessonPlanResources = (SELECT COUNT(*) FROM dbo.LessonPlanResource lpr where lpr.LessonPlanId = a.LessonPlanId )
FROM
(
SELECT DISTINCT lpt.LessonPlanId
FROM dbo.LearningTargetBreakout ltb
JOIN dbo.LessonPlanLearningTarget lpt
on lpt.LearningTargetId = ltb.LearningTargetId
WHERE (CASE
WHEN ltb.LearningTargetId = #learningTargetId and #learningTargetId is not null THEN 1
WHEN ltb.CategoryId = #categoryId and #categoryId is not null THEN 1
WHEN ltb.Grade = #grade and #grade is not null THEN 1
WHEN ltb.SubjectId = #subjectId and #subjectId is not null THEN 1
ELSE 0 END) = 1
) a
GROUP BY a.LessonPlanID
Removing the COUNT from your outer select.
Using COUNT DISTINCT in your inner select
SELECT
#lessonPlans = a.B,
#lessonPlanResources = (SELECT COUNT(*) FROM dbo.LessonPlanResource lpr where lpr.LessonPlanId = a.LessonPlanId )
FROM
(
SELECT COUNT (DISTINCT, lpt.LessonPlanId) AS B
FROM dbo.LearningTargetBreakout ltb
JOIN dbo.LessonPlanLearningTarget lpt
on lpt.LearningTargetId = ltb.LearningTargetId
WHERE (CASE
WHEN ltb.LearningTargetId = #learningTargetId and #learningTargetId is not null THEN 1
WHEN ltb.CategoryId = #categoryId and #categoryId is not null THEN 1
WHEN ltb.Grade = #grade and #grade is not null THEN 1
WHEN ltb.SubjectId = #subjectId and #subjectId is not null THEN 1
ELSE 0 END) = 1
) a