I am using the AdventureWorks 2008 schema.
I am writing a procedure that returns the total sales by Sales Territory for sales people. There will be multiple sales people, so they need to be concatenated together in a comma separated list to show all sales people per record.
I have a solution below using cursors but I am wondering if there are any other ways of doing this (ie: cross apply, CTE's, etc).
if OBJECT_ID('tempdb..#tmpTerritorySalesPeople') is not null
drop table #tmpTerritorySalesPeople
create table #tmpTerritorySalesPeople (TerritoryID int, TerritoryName varchar(50), TerritorySalesPeople varchar(2000))
GO
declare #territoryID int,
#territoryName varchar(50),
#salesPersonName varchar(100),
#salesPersonList varchar(2000)
--cursor for territy and tname
declare c_territory cursor for
select st.territoryid, st.name
from sales.SalesTerritory st
order by st.territoryid
open c_territory
FETCH c_territory
INTO #territoryid, #territoryname
while ##FETCH_STATUS = 0
BEGIN
declare c_territorySalesPeople cursor for
-- cursor to hold list of sales people per territory
select [SalesPersonName] = p.FirstName + ' ' + p.LastName
FROM
sales.SalesPerson SP
JOIN Person.person P
ON SP.BusinessEntityID = P.BusinessEntityID
JOIN Sales.SalesTerritory ST
ON SP.territoryID = ST.TerritoryID
where sp.territoryid = #territoryid
order by st.territoryid
set #salesPersonList = ''
open c_territorySalesPeople
fetch next from c_territorySalesPeople into #salesPersonName;
while ##FETCH_STATUS = 0
begin
if #salesPersonList = ''
set #salesPersonList = #salesPersonName
else
set #salesPersonList = #salesPersonList + ', ' + #salesPersonName
fetch next from c_territorySalesPeople into #salesPersonName;
end
close c_territorySalesPeople
deallocate c_territorySalesPeople
insert into #tmpTerritorySalesPeople
select #territoryid, #territoryname, #salesPersonList
fetch next from c_territory
into #territoryid, #territoryname
end
close c_territory
deallocate c_territory
go
select
[Sales Territory] = tsp.TerritoryName,
[Sales Person] = tsp.TerritorySalesPeople,
[TotalSalesAmount] = '$'+CONVERT(VARCHAR,sum(SOH.TotalDue),1)
FROM
sales.SalesOrderHeader SOH
JOIN #tmpTerritorySalesPeople tsp
ON SOH.TerritoryID = tsp.TerritoryID
where soh.TerritoryID is not null
group by tsp.TerritoryName, tsp.TerritorySalesPeople
order by tsp.TerritoryName
go
Source of data is Adventureworks.
Results of data:
Sales Territory,Sales Person,TotalSalesAmount
Australia,Lynn Tsoflias,"$11,814,376.10"
Canada,"Garrett Vargas, José Saraiva","$18,398,929.19"
Central,Jillian Carson,"$8,913,299.25"
France,Ranjit Varkey Chudukatil,"$8,119,749.35"
Germany,Rachel Valdez,"$5,479,819.58"
Northeast,Michael Blythe,"$7,820,209.63"
Northwest,"Pamela Ansman-Wolfe, David Campbell, Tete Mensa-Annan","$18,061,660.37"
Southeast,Tsvi Reiter,"$8,884,099.37"
Southwest,"Linda Mitchell, Shu Ito","$27,150,594.59"
United Kingdom,Jae Pak,"$8,574,048.71"
You need to use STUFF() string function and FOR XML PATH to get CSV value column of territory wise sales persons.
You need to use CROSS apply to get territory wise total sales amount.
You need to use ISNULL() also for NULL value handle in any field.
Please check below query.
SELECT
ST.Name AS [Sales Territory],
STUFF(
(
SELECT
',' + ISNULL(PER.FirstName,'') + ' ' + ISNULL(PER.LastName,'')
---CONCAT(',',PER.FirstName,' ',PER.LastName) /*SQL SERVER 2012 Onwards*/
FROM sales.SalesPerson SSP
JOIN Person.person PER ON SSP.BusinessEntityID = PER.BusinessEntityID
AND SSP.territoryid = SP.territoryid
FOR XML PATH('')
),
1,1,'') AS [Sales Person],
TSalesAmt.[TotalSalesAmount]
FROM sales.SalesPerson SP
JOIN Person.person P ON SP.BusinessEntityID = P.BusinessEntityID
JOIN Sales.SalesTerritory ST ON SP.territoryID = ST.TerritoryID
CROSS APPLY
(
SELECT
SOH.TerritoryID,
'$'+CONVERT(VARCHAR,sum(SOH.TotalDue)) AS TotalSalesAmount
FROM sales.SalesOrderHeader SOH
Where SOH.TerritoryID = ST.TerritoryID
GROUP BY SOH.TerritoryID
) TSalesAmt
order by st.name
Related
I am trying to insert this data into a temp table
When I add the piece to insert into my temp table with the second result set #Temp
I recieve an incorrect syntax error
Any Ideas?
sql
-- =============================================
-- Author: <Kaven>
-- Create date: <August 2020>
-- Description: Summary Report of [spRPWipManufactureProcessLogR1]
-- Owner : RpData.dll
-- =============================================
ALTER PROCEDURE [dbo].[spRPWipManufactureProcessLogR2]
#FromDate DATE = null,
#ToDate DATE = null,
#Location NVARCHAR(15) =null, --Dropdown --CostCentre
#Operation NVARCHAR(15) = NULL, -- Dropdown --WorkCentre
#JobNumber nvarchar(15) = NULL
AS
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
BEGIN
--EXEC [spRPWipManufactureProcessLogR2] '20200201', '20200301'
IF #Operation = ''
BEGIN
SET #Operation = null
END
IF #JobNumber = ''
BEGIN
SET #JobNumber = NULL
END
IF OBJECT_ID(N'tempdb..#Temppp_WarehouseList') IS NOT NULL
BEGIN DROP TABLE #Temppp_WarehouseList END
SELECT StockCode
INTO #Temppp_WarehouseList
FROM CompanyA..InvMaster
DECLARE
#wh_List NVARCHAR(1000),
#wh_List2 NVARCHAR(1000)
SELECT #wh_List = STUFF((SELECT ',['+l.Warehouse+' QtyOnHand]' FROM CompanyA..InvWhControl l WHERE l.Warehouse IS NOT NULL FOR XML PATH('')),1,1,'')
SELECT #wh_List2 = STUFF((SELECT ',['+l.Warehouse+' QtyOnOrder]' FROM CompanyA..InvWhControl l WHERE l.Warehouse IS NOT NULL FOR XML PATH('')),1,1,'')
--SELECT #wh_List
--SELECT #wh_List2
SELECT ProductID,
FromDate,
ToDate,
QtyBUoM
INTO #TempBuOM
FROM Cetus.dbo.SMDemandForcastDailyHierarchy df
JOIN dbo.SMDemandSourceTypeDef st ON st.pkDFSourceTypeID = df.fkSourceType
WHERE
df.DemandSource = st.pkDFSourceTypeID
AND
df.Parent = st.pkDFSourceTypeID
AND FromDate <= GETDATE()
AND ToDate >= GETDATE()
SELECT * FROM #TempBuOM
SELECT * FROM #Temppp_WarehouseList t
INNER join
(SELECT * FROM
(SELECT Warehouse+' QtyOnHand' 'WarehouseOnHand', Warehouse+' QtyOnOrder' 'WarehouseOnOrder', QtyOnHand,QtyOnOrder,
b.CostCentre AS 'Location',
--b.WorkCentre AS [Operation],
-- arc.Job AS [Job Number],
--FORMAT(CAST(CAST(s.Event AS XML).value('(/OperationTrackingLog/TimeStamp)[1]', 'varchar(max)') AS DATETIME),'HH:mm:ss') AS [Operation Compeleted Time],
-- CONVERT(VARCHAR, FORMAT(CAST(CAST(s.Event AS XML).value('(/OperationTrackingLog/TimeStamp)[1]', 'varchar(max)') AS DATETIME),'yyyy/MM/dd'),100) AS [Operation Compeleted Date],
-- CONVERT(Date, CONVERT(date, arc.DTComplete))AS [Compeleted Date],
df.ProductID AS 'Product ID',
m.Description AS 'Stock Code DESCRIPTION',
arc.QtyToMake AS 'Quantity To Make',
arc.QtyManufactured 'Quantity Manufactured',
df.QtyBUoM AS 'Daily Demand',
inv.QtyOnOrder AS 'Quantity On Order',
inv.QtyOnHand AS 'Stock Levels'
INTO #TEMP -- Error comes here
FROM Cetus..WIPManufactureProcessLog s
LEFT JOIN WIP..WipMaster_Arc arc ON s.JobId = arc.Job
JOIN CompanyA..InvMaster m ON arc.StockCode = m.StockCode
JOIN CompanyA..InvWarehouse inv ON m.StockCode = inv.StockCode
JOIN CompanyA.dbo.BomOperations bo ON m.StockCode = bo.StockCode AND bo.Operation = s.Operation
JOIN CompanyA..BomWorkCentre b ON bo.WorkCentre = b.WorkCentre
JOIN [dbo].SMDemandForcastDailyHierarchy df ON m.StockCode = df.ProductID
JOIN dbo.SMDemandSourceTypeDef st ON st.pkDFSourceTypeID = df.fkSourceType
--EXEC [spRPWipManufactureProcessLogR2] '20190101', '20200201'
Where CONVERT(VARCHAR, FORMAT(CAST(CAST(s.Event AS XML).value('(/OperationTrackingLog/TimeStamp)[1]', 'varchar(max)') AS DATETIME),'yyyy/MM/dd'),100)
BETWEEN ISNULL(#FromDate,CONVERT(VARCHAR, FORMAT(CAST(CAST(s.Event AS XML).value('(/OperationTrackingLog/TimeStamp)[1]', 'varchar(max)') AS DATETIME),'yyyy/MM/dd'),100))
AND ISNULL(#ToDate,CONVERT(VARCHAR, FORMAT(CAST(CAST(s.Event AS XML).value('(/OperationTrackingLog/TimeStamp)[1]', 'varchar(max)') AS DATETIME),'yyyy/MM/dd'),100))
--WHERE FORMAT(CAST(CAST(s.Event AS XML).value('(/OperationTrackingLog/TimeStamp)[1]', 'varchar(max)') AS DATETIME),'HH:mm:ss') >= '2020-02-01'
--AND FORMAT(CAST(CAST(s.Event AS XML).value('(/OperationTrackingLog/TimeStamp)[1]', 'varchar(max)') AS DATETIME),'HH:mm:ss') <= '2020-03-01 '
AND m.WarehouseToUse <>'AA'
AND b.WorkCentre = ISNULL(#Location,b.WorkCentre )
AND b.CostCentre = ISNULL(#Operation,b.CostCentre )
AND arc.Job = ISNULL(#JobNumber,arc.Job) )p
PIVOT (
SUM(QtyOnHand) FOR WarehouseOnHand IN
([BB QtyOnHand],
[16 QtyOnHand],
[IS QtyOnHand],
[MS QtyOnHand],
[MH QtyOnHand],
[AA QtyOnHand],
[BC QtyOnHand],
[PH QtyOnHand],
[P4 QtyOnHand],
[PL QtyOnHand],
[CC QtyOnHand],
[SR QtyOnHand],
[TS QtyOnHand],
[WW QtyOnHand])) AS pvt
PIVOT
(SUM(QtyOnOrder) FOR WarehouseOnOrder IN
([BB QtyOnOrder],
[16 QtyOnOrder],
[IS QtyOnOrder],
[MS QtyOnOrder],
[MH QtyOnOrder],
[AA QtyOnOrder],
[BC QtyOnOrder],
[PH QtyOnOrder],
[P4 QtyOnOrder],
[PL QtyOnOrder],
[CC QtyOnOrder],
[SR QtyOnOrder],
[TS QtyOnOrder],
[WW QtyOnOrder])) AS pvt2 GROUP BY )
AS z on t.StockCode = z.[Product ID]
```sql```
Edit: Added answers in case the #TEMP table is supposed to have the inner query rather than the final pivot results.
Note that the 'code' provided is there to show structure - you'll need to add the slabs of text from above and bug-fix those as normal. The below is to demonstrate how to fix the issue.
That INTO #TEMP is in the middle of a FROM statement (it's not actually the final select) and therefore doesn't work.
If want the final results (e.g., from the pivot) to be stored in the temp table, remove the current 'INTO #TEMP' and put it into the outer query e.g.,
SELECT *
INTO #TEMP
FROM (your whole pivot statement) AS a
or alternatively using a CTE
; WITH CTE AS
(your whole pivot statement)
SELECT *
INTO #Temp
FROM CTE
If, on the other hand, you want to store the results of the inner query (not the pivot) in a temp table, then
run that first (e.g., take out the inner query you have there, into its own statement)
use the #temp table in the bigger query
-- Initial creation of temp table (SQL extracted from your current code)
SELECT Warehouse+' QtyOnHand' (... and rest of fields)
INTO #Temp
FROM Cetus..WIPManufactureProcessLog s
(... and rest of from, and where clauses)
-- And then use that in your pivot
SELECT *
FROM
(SELECT *
FROM #Temppp_WarehouseList t
INNER join #TEMP ON (... matching fields)
) p
PIVOT
(pivot commands etc)
I am not sure if this is the way to go though but I would like to know if there is someway to have a stored procedure and pass in a queries to get the values to pivot. I'm not sure if this is a good idea, so sorry if this is a stupid question, but it would be great to pass in a query instead of hard coding every single pivot you want. I have an example of the pivot Stored Procedure that I have coded. This also includes the grand totals for rows and columns.
Don't know if I should add the code as well?
Hope this makes sense.
Please see my stored procedure code below:
CREATE PROCEDURE [dbo].[PivotNoAgentPerc_SP]
AS
DECLARE #columnHeaders VARCHAR (MAX)
SELECT #columnHeaders = COALESCE(#columnHeaders + ', ','')+ QUOTENAME(granteddate)
FROM
(
SELECT DISTINCT EOMONTH(BondSales.granteddate,0) AS granteddate
FROM ccbsm_ccbsm_salemanagement AS BondSales
LEFT OUTER JOIN ccbsm_ccbsm_salemanagement_cstm AS BondSalesCSTM ON BondSales.id = BondSalesCSTM.id_c
LEFT OUTER JOIN CapcubedInternalDB.dbo.ProvincialArea AS ProvincialArea ON BondSalesCstm.provincial_level_c = ProvincialArea.ID
LEFT OUTER JOIN accounts AS accounts_1 ON BondSales.account_id1_c = accounts_1.id AND accounts_1.deleted = 0
LEFT OUTER JOIN users AS ConsultantUser ON BondSales.Assigned_user_id = ConsultantUser.id AND ConsultantUser.deleted = 0
LEFT OUTER JOIN CapcubedInternalDB.dbo.BondSaleApplicationStatus AS BondSalesStatus ON BondSalesStatus.ID = BondSales.applicationstatus
WHERE BondSales.deleted = 0
AND ProvincialArea.SAD_Province = 'Coastal'
AND BondSalesStatus.AuditedStatus = 1
AND EOMONTH(BondSales.granteddate,0) BETWEEN EOMONTH(GETDATE(),-12) AND EOMONTH(GETDATE(),-1)
) AS B
ORDER BY B.granteddate
/* GRAND TOTAL COLUMN */
DECLARE #GrandTotalCol NVARCHAR (MAX)
SELECT #GrandTotalCol =
COALESCE (#GrandTotalCol + 'ISNULL ([' + CAST (granteddate AS VARCHAR) +'],0) + ', 'ISNULL([' + CAST(granteddate AS VARCHAR)+ '],0) + ')
FROM
(
SELECT DISTINCT EOMONTH(BondSales.granteddate,0) AS granteddate
FROM ccbsm_ccbsm_salemanagement AS BondSales
LEFT OUTER JOIN ccbsm_ccbsm_salemanagement_cstm AS BondSalesCSTM ON BondSales.id = BondSalesCSTM.id_c
LEFT OUTER JOIN CapcubedInternalDB.dbo.ProvincialArea AS ProvincialArea ON BondSalesCstm.provincial_level_c = ProvincialArea.ID
LEFT OUTER JOIN accounts AS accounts_1 ON BondSales.account_id1_c = accounts_1.id AND accounts_1.deleted = 0
LEFT OUTER JOIN users AS ConsultantUser ON BondSales.Assigned_user_id = ConsultantUser.id AND ConsultantUser.deleted = 0
LEFT OUTER JOIN CapcubedInternalDB.dbo.BondSaleApplicationStatus AS BondSalesStatus ON BondSalesStatus.ID = BondSales.applicationstatus
WHERE BondSales.deleted = 0
AND ProvincialArea.SAD_Province = 'Coastal'
AND BondSalesStatus.AuditedStatus = 1
AND EOMONTH(BondSales.granteddate,0) BETWEEN EOMONTH(GETDATE(),-12) AND EOMONTH(GETDATE(),-1)
) AS B
ORDER BY B.granteddate
SET #GrandTotalCol = LEFT (#GrandTotalCol, LEN (#GrandTotalCol)-1)
/* GRAND TOTAL ROW */
DECLARE #GrandTotalRow NVARCHAR(MAX)
SELECT #GrandTotalRow =
COALESCE(#GrandTotalRow + ',ISNULL(SUM([' + CAST(granteddate AS VARCHAR)+']),0)', 'ISNULL(SUM([' + CAST(granteddate AS VARCHAR)+']),0)')
FROM
(
SELECT DISTINCT EOMONTH(BondSales.granteddate,0) AS granteddate
FROM ccbsm_ccbsm_salemanagement AS BondSales
LEFT OUTER JOIN ccbsm_ccbsm_salemanagement_cstm AS BondSalesCSTM ON BondSales.id = BondSalesCSTM.id_c
LEFT OUTER JOIN CapcubedInternalDB.dbo.ProvincialArea AS ProvincialArea ON BondSalesCstm.provincial_level_c = ProvincialArea.ID
LEFT OUTER JOIN accounts AS accounts_1 ON BondSales.account_id1_c = accounts_1.id AND accounts_1.deleted = 0
LEFT OUTER JOIN users AS ConsultantUser ON BondSales.Assigned_user_id = ConsultantUser.id AND ConsultantUser.deleted = 0
LEFT OUTER JOIN CapcubedInternalDB.dbo.BondSaleApplicationStatus AS BondSalesStatus ON BondSalesStatus.ID = BondSales.applicationstatus
WHERE BondSales.deleted = 0
AND ProvincialArea.SAD_Province = 'Coastal'
AND BondSalesStatus.AuditedStatus = 1
AND EOMONTH(BondSales.granteddate,0) BETWEEN EOMONTH(GETDATE(),-12) AND EOMONTH(GETDATE(),-1)
)AS B
ORDER BY B.granteddate
/* MAIN QUERY */
DECLARE #FinalQuery NVARCHAR (MAX)
SET #FinalQuery = 'SELECT *, ('+ #GrandTotalCol + ')
AS [Grand Total] INTO #temp_MatchesTotal
FROM
(SELECT
ISNULL(ConsultantUser.user_name,''Total'') AS [Consultant],
EOMONTH(BondSales.granteddate,0) AS [Month Granted],
COALESCE(CAST(CAST(SUM(CASE WHEN accounts_1.name = ''No Agent Channel'' THEN 1 ELSE 0 END) AS DECIMAL)/COUNT(BondSales.name) AS decimal(5,2)), 0) AS NoAgentPerc
FROM ccbsm_ccbsm_salemanagement AS BondSales
LEFT OUTER JOIN ccbsm_ccbsm_salemanagement_cstm AS BondSalesCSTM ON BondSales.id = BondSalesCSTM.id_c
LEFT OUTER JOIN CapcubedInternalDB.dbo.ProvincialArea AS ProvincialArea ON BondSalesCstm.provincial_level_c = ProvincialArea.ID
LEFT OUTER JOIN accounts AS accounts_1 ON BondSales.account_id1_c = accounts_1.id AND accounts_1.deleted = 0
LEFT OUTER JOIN users AS ConsultantUser ON BondSales.Assigned_user_id = ConsultantUser.id AND ConsultantUser.deleted = 0
LEFT OUTER JOIN CapcubedInternalDB.dbo.BondSaleApplicationStatus AS BondSalesStatus ON BondSalesStatus.ID = BondSales.applicationstatus
WHERE BondSales.deleted = 0
AND ProvincialArea.SAD_Province = ''Coastal''
AND BondSalesStatus.AuditedStatus = 1
AND EOMONTH(BondSales.granteddate,0) BETWEEN EOMONTH(GETDATE(),-12) AND EOMONTH(GETDATE(),-1)
GROUP BY EOMONTH(BondSales.granteddate, 0), ConsultantUser.user_name
) A
PIVOT
(
SUM(NoAgentPerc)
FOR [Month Granted]
IN ('+#columnHeaders +')
) B
ORDER BY [Consultant]
SELECT * FROM #temp_MatchesTotal
UNION ALL
SELECT ''Grand Total'','+ #GrandTotalRow +', ISNULL (SUM([Grand Total]),0)
FROM #temp_MatchesTotal
DROP TABLE #temp_MatchesTotal'
EXECUTE(#FinalQuery)
Sorry if the code is long but I just wanted to show you exactly what I an talking about
I have done this is in procedure and it is working fine. Compare your example to below code
You have to write Dynamic sql query
Example :
declare #s date
declare #e date
declare #dates varchar(MAX)
set #s=DATEADD(DAY,-1,#startdate)
set #e=#enddate
IF OBJECT_ID('tempdb.#DATES') IS NOT NULL
DROP TABLE #DATES
CREATE TABLE #DATES
(
ID INT PRIMARY KEY IDENTITY(1,1),
Date VARCHAR(MAX)
)
WHILE (#s<#e)
BEGIN
INSERT INTO #DATES (Date)
select CONCAT( '[',DATEADD(DAY ,1, #s),']')
set #s=DATEADD(DAY ,1, #s)
END
SELECT #dates= STUFF((
SELECT ',' + SPACE(1) + Date
FROM #DATES
FOR XML PATH(''), TYPE).value('.', 'VARCHAR(MAX)'), 1, 1, '')
--AS [Dates]
------------------------Getting RDW Datewise--------------------------------------
declare #queryRDW VARCHAR(MAX)
set #queryRDW='
select * from (
SELECT SUM(RecordValue)/60 RecordValue,RecordType ,Computationdate ,t2.ProjectResourceId,t1.ResourceCode,t1.ResourceName
FROM tblProjectResource t1 INNER JOIN tblDeploymentWorkbook t2 on t1.ProjectResourceId=t2.ProjectResourceId
WHERE
RecordType=''RDW'' and t2.SubCategoryId IN (1)
GROUP BY
RecordType, Computationdate,t2.ProjectResourceId,t1.resourceCode,t1.ResourceName
HAVING
Computationdate >= '''+Cast(#startdate as varchar)+''' and Computationdate <= '''+Cast(#enddate as varchar)+'''
)tab
PIVOT(
SUM(RecordValue) for ComputationDate in ( '+#dates+')
)t
Order By ResourceName
'
exec (#queryRDW)
This has to be something simple that I have just missed...
I've got a temp table say this:
CREATE TABLE #tsa
(
AttendeeID int,
SurveyID int,
TrainingAttendeeID int
)
I get a single record using TOP 1 with something similar to this:
SELECT
TOP 1
#AttendeeID=ta.AttendeeID,
#SurveyID=ts.SurveyID,
#TrainingAttendeeID = ta.TrainingAttendeeID
FROM
TrainingAttendee ta
INNER JOIN
[User] u
ON
u.UserID= ta.AttendeeID
INNER JOIN
[Training] t
ON
t.TrainingID = ta.TrainingID
INNER JOIN
[TrainingSet] ts
ON
ts.TrainingSetID = t.TrainingSetID
WHERE
ta.AttendedTraining = 1
AND ta.ConfirmAttendedEmailOn IS NOT NULL
--only get people who didn't fill out the survey
AND NOT EXISTS (SELECT * FROM SurveyTaken st WHERE st.AddedByUserID = ta.AttendeeID AND st.SurveyID = ts.SurveyID)
ORDER BY
ta.AttendeeID,
ts.SurveyID
As soon as I get this one record I store it into my temp table as such:
--insert into our temp table
INSERT INTO #tsa(AttendeeID, SurveyID, TrainingAttendeeID)
VALUES(#AttendeeID, #SurveyID, #TrainingAttendeeID)
Then I need to go through this whole procedure of checking some data and sending an email...as soon as that email is sent I need to pick up the next record not including the record I had previously...So without showing too much code:
WHILE SomeCondition
BEGIN
--do some thing...
--pick up next one
--grab next one
SELECT
TOP 1
#AttendeeID = ta.AttendeeID,
#SurveyID=ts.SurveyID,
#TrainingAttendeeID=ta.TrainingAttendeeID
FROM
TrainingAttendee ta
INNER JOIN
[User] u
ON
u.UserID= ta.AttendeeID
INNER JOIN
[Training] t
ON
t.TrainingID = ta.TrainingID
INNER JOIN
[TrainingSet] ts
ON
ts.TrainingSetID = t.TrainingSetID
WHERE
(
--same where as original
ta.AttendedTraining = 1
AND (ta.ConfirmAttendedEmailOn IS NOT NULL)
AND (NOT EXISTS (SELECT * FROM SurveyTaken st WHERE st.AddedByUserID = ta.AttendeeID AND st.SurveyID = ts.SurveyID))
--adding the piece such that we compare against the temp table...
AND (NOT EXISTS (SELECT * FROM #tsa tempS WHERE tempS.AttendeeID = ta.AttendeeID AND tempS.SurveyID = ts.SurveyID AND tempS.TrainingAttendeeID = ta.TrainingAttendeeID))
)
ORDER BY
ta.AttendeeID,
ts.SurveyID
--insert into our temp table
INSERT INTO #tsa(AttendeeID, SurveyID, TrainingAttendeeID)
VALUES(#AttendeeID, #SurveyID, #TrainingAttendeeID)
END
Notice the where condition inside of this..I've added one more AND...namely:
--adding the piece such that we compare against the temp table...
AND (NOT EXISTS (SELECT * FROM #tSa tempS WHERE tempS.AttendeeID = ta.AttendeeID AND tempS.SurveyID = ts.SurveyID AND tempS.TrainingAttendeeID = ta.TrainingAttendeeID))
Just to ensure I am not reusing the record I already processed in my temp table...and you'll notice I reinsert into my temp table at the end as well...
--insert into our temp table
INSERT INTO #tsa(AttendeeID, SurveyID, TrainingAttendeeID)
VALUES(#AttendeeID, #SurveyID, #TrainingAttendeeID)
Every time I run this stored procedure it goes on infinitly and so I believe something is wrong with my condition at this point. I'm having a brain fart..or maybe there is just too much noise in the office. What am I missing here? I placed a print statement and it keeps processing the same record...so something tells me this last condition in my where clause is incorrect.
Edit
Here's the entire procedure...My issue is the record set only has one record in it...But the sproc continues to process this same record
PROCEDURE ScriptSendTrainingSurveyReminders
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
DECLARE #AttendeeID int
DECLARE #TrainingAttendeeID int
DECLARE #SurveyID int
DECLARE #Message nvarchar(MAX)
DECLARE #Subject nvarchar(255)
CREATE TABLE #tSa
(
AttendeeID int,
SurveyID int,
TrainingAttendeeID int
)
SELECT
TOP 1
#AttendeeID=ta.AttendeeID,
#SurveyID=ts.SurveyID,
#TrainingAttendeeID = ta.TrainingAttendeeID
FROM
TrainingAttendee ta
INNER JOIN
[User] u
ON
u.UserID= ta.AttendeeID
INNER JOIN
[Training] t
ON
t.TrainingID = ta.TrainingID
INNER JOIN
[TrainingSet] ts
ON
ts.TrainingSetID = t.TrainingSetID
WHERE
ta.AttendedTraining = 1
AND ta.ConfirmAttendedEmailOn IS NOT NULL
--only get people who didn't fill out the survey
AND NOT EXISTS (SELECT * FROM SurveyTaken st WHERE st.AddedByUserID = ta.AttendeeID AND st.SurveyID = ts.SurveyID)
ORDER BY
ta.TrainingAttendeeID,
ta.AttendeeID,
ts.SurveyID
--insert into our temp table
INSERT INTO #tSa(AttendeeID, SurveyID, TrainingAttendeeID)
VALUES(#AttendeeID, #SurveyID, #TrainingAttendeeID)
WHILE #TrainingAttendeeID IS NOT NULL AND #AttendeeID IS NOT NULL AND #SurveyID IS NOT NULL
BEGIN
DECLARE #TrainingID int
DECLARE #Title nvarchar(50)
DECLARE #StartDateTime nvarchar(50)
DECLARE #EndDateTime nvarchar(50)
DECLARE #FullName nvarchar(255)
DECLARE #EmailAddress nvarchar(255)
DECLARE #Description nvarchar(MAX)
--get the one record we are on...
SELECT
#TrainingID = t.TrainingID,
#Title = t.Title,
#StartDateTime = CAST(CONVERT(DATE, t.StartDate) AS VARCHAR(50)) + ' ' + CAST(t.StartTimeHours AS VARCHAR(50)) + ':' + CAST(CASE WHEN LEN(t.StartTimeMinutes)=1 THEN CAST(t.StartTimeMinutes AS VARCHAR(50)) + '0' ELSE CAST(t.StartTimeMinutes AS VARCHAR(50)) END AS VARCHAR(50)) + ' ' + CAST(t.StartTimeAMorPM AS VARCHAR(50)),
#EndDateTime = CAST(CONVERT(DATE, t.EndDate) AS VARCHAR(50)) + ' ' + CAST(t.EndTimeHours AS VARCHAR(50)) + ':' + CAST(CASE WHEN LEN(t.EndTimeMinutes)=1 THEN CAST(t.EndTimeMinutes AS VARCHAR(50)) + '0' ELSE CAST(t.EndTimeMinutes AS VARCHAR(50)) END AS VARCHAR(50)) + ' ' + CAST(t.EndTimeAMorPM AS VARCHAR(50)),
#Description = t.DescriptionHTML,
#FullName = u.FullName,
#EmailAddress = u.EmailAddress
FROM
Training t
INNER JOIN
TrainingAttendee ta
ON
t.TrainingID = ta.TrainingID
INNER JOIN
[User] u
ON
u.UserID = ta.AttendeeID
WHERE
ta.TrainingAttendeeID = #TrainingAttendeeID
IF #EmailAddress IS NOT NULL
BEGIN
--Email goes out here....
END
--grab next one
SELECT
TOP 1
#AttendeeID = ta.AttendeeID,
#SurveyID=ts.SurveyID,
#TrainingAttendeeID=ta.TrainingAttendeeID
FROM
TrainingAttendee ta
INNER JOIN
[User] u
ON
u.UserID= ta.AttendeeID
INNER JOIN
[Training] t
ON
t.TrainingID = ta.TrainingID
INNER JOIN
[TrainingSet] ts
ON
ts.TrainingSetID = t.TrainingSetID
WHERE
(
--same where as original
(ta.AttendedTraining = 1)
AND (ta.ConfirmAttendedEmailOn IS NOT NULL)
AND (NOT EXISTS (SELECT * FROM SurveyTaken st WHERE st.AddedByUserID = ta.AttendeeID AND st.SurveyID = ts.SurveyID))
--adding the piece such that we compare against the temp table...
AND (NOT EXISTS (SELECT * FROM #tSa tempS WHERE tempS.AttendeeID = ta.AttendeeID AND tempS.SurveyID = ts.SurveyID AND tempS.TrainingAttendeeID = ta.TrainingAttendeeID))
)
ORDER BY
ta.TrainingAttendeeID,
ta.AttendeeID,
ts.SurveyID
PRINT CAST('TrainingAttendeeID: ' + CAST(#TrainingAttendeeID as nvarchar(500)) + ' AttendeeID:' + CAST(#AttendeeID as nvarchar(500)) + ' SurveyID: ' + CAST(#SurveyID as nvarchar(500)) AS nvarchar(4000))
--insert into our temp table
INSERT INTO #tSa(AttendeeID, SurveyID, TrainingAttendeeID)
VALUES(#AttendeeID, #SurveyID, #TrainingAttendeeID)
END
END
GO
Variables will not change if select does not return any records. I bet it processes last #AttendeeID round and round.
Another way to test it - add unique constraint to temp table. I assume cycle will fail once there are no more records to select.
One way to fix it - assign NULLs to all variables at the beginning of each iteration (at the top of while body). But I'd recommend to rewrite this code to cursor if possible (not sure what is the logic of several select statements).
Note that declaration of variables within code block makes no "block-scope" sense since it is not perl or python.
Create a stored procedure that passes in the SalesOrderID as a parameter.
This stored procedure will return the SalesOrderID, Date of the transaction, shipping date, city and state. It is not running
Ans:
Create PROCEDURE proc_findProductInfo
#SalesOrderID int,
#SalesOrderOut int OUTPUT,
#OrderDate datetime OUTPUT,
#ShipDate datetime OUTPUT,
#CityState varchar(100) OUTPUT
AS
BEGIN
SET NOCOUNT ON;
SET #SalesOrderOut = #SalesOrderID
SET #OrderDate = (SELECT OrderDate FROM SALES.SalesOrderHeader )
SET #ShipDate = (SELECT ShipDate FROM Sales.SalesOrderHeader)
SET #CityState = (SELECT a.City, st.Name
FROM Sales.SalesOrderHeader s
INNER JOIN Person.Address a ON s.ShipToAddressID = a.AddressID
INNER JOIN Person.StateProvince st ON s.TerritoryID = st.TerritoryID
WHERE SalesOrderID = #SalesOrderID)
END
DECLARE #OrderNum int, #Date datetime, #qty1 int, #Date1 datetime
EXEC proc_findProductInfo 63936,
#SalesOrderOut = #OrderNum OUTPUT,
#OrderDate = #Date OUTPUT,
#ShipDate = #date1,
#CityState = #qty1 output
SELECT #OrderNum, #date, #qty1, #Date1
Error Message:
Msg 116, Level 16, State 1, Procedure proc_findProductInfo, Line 25
Only one expression can be specified in the select list when the
subquery is not introduced with EXISTS
You're making this way harder than it needs to be:
Create PROCEDURE proc_findProductInfo
#SalesOrderID int
AS
BEGIN
SET NOCOUNT ON;
SELECT s.SalesOrderID, s.OrderDate, s.ShipDate, a.City,st.Name
FROM Sales.SalesOrderHeader s
INNER JOIN Person.Address a ON s.ShipToAddressID = a.AddressID
INNER JOIN Person.StateProvince st ON s.TerritoryID=st.TerritoryID
WHERE s.SalesOrderID = #SalesOrderID
END
I'm not even sure you need the StateProvince table here... the question probably allows you to trust the Address record.
It is complaining about the following
SET #CityState = (
SELECT a.City,st.Name
You are selecting both City and State Name and trying to assign it to a variable.
You either need to concatenate or coalesce them into them into a single output or alternatively use the below type of select to assign each one to a variable.
select
#var1 = field1,
#var2 = field2,
...
As below
SET #SalesOrderOut = #SalesOrderID
SELECT #OrderDate = s.OrderDate,
#ShipDate = s.ShipDate,
#CityState = CONCAT(a.City, ", ", st.Name)
FROM Sales.SalesOrderHeader s
inner JOIN Person.Address a
ON s.ShipToAddressID = a.AddressID
inner JOIN Person.StateProvince st
on s.TerritoryID=st.TerritoryID
WHERE SalesOrderID = #SalesOrderID
I have a variable in my report which holds 2 possible values: 'monthly' and 'daily'. How can I put this variable (lets call it #reportModel). I think it should be somewhere in GROUP BY clause, but don't know how should it look like.
DECLARE #reportModel VARCHAR(10)
SET #reportModel = 'monthly'
SELECT P.product, SUM(O.price * O.quantity), O.orderDate
FROM Products AS P
INNER JOIN Orders AS O ON P.ID = O.ID
And what now?
How about a stored procedure to handle this, something like.....
CREATE PROCEDURE rpt_GetData
#reportModel VARCHAR(10)
AS
BEGIN
SET NOCOUNT ON;
DECLARE #Sql NVARCHAR(MAX);
IF (#reportModel = 'daily')
BEGIN
SET #Sql = N' SELECT P.product
, SUM(O.price * O.quantity) AS Total
, O.orderDate
FROM Products AS P
INNER JOIN Orders AS O ON P.ID = O.ID
GROUP BY P.product , O.orderDate'
Exec sp_executesql #Sql
END
ELSE IF (#reportModel = 'monthly')
BEGIN
SET #Sql = N' SELECT P.product
, SUM(O.price * O.quantity) AS Total
, MONTH(O.orderDate) AS [Month]
FROM Products AS P
INNER JOIN Orders AS O ON P.ID = O.ID
GROUP BY P.product, MONTH(O.orderDate)'
Exec sp_executesql #Sql
END
END
Adding this as an answer because I mentioned it in the comments in #M.Ali's answer.
So I would suggest you change thinking slightly with one of these options.
Two reports - Simply make a report for daily and another for monthly. Now you have no worries with complex SQL etc.
Make 2 stored procedures, one with the GROUP BY daily and one monthly. Then in your SSRS dataset, create an expression for you SQL that chooses the procedure based on parameter:
=IIf(Parameters!reportModel.Value = "monthly", "GetMonthlyData", "GetDailyData")
I would put it in the Group On Expression of the table or chart rather than doing it in the query.
=IIF(Parameters!reportModel.Value = "monthly", Month(Fields!orderDate.Value), Fields!orderDate.Value)
If you'd rather do it in the query and don't want to wait for DBAs to get around to deploying a Stored Procedure (not to mention maintaining it whenever there's a change), you could use your parameter in a CASE like:
SELECT P.product, SUM(O.price * O.quantity),
CASE WHEN #reportModel = 'monthly' THEN CAST(MONTH(O.orderDate) AS VARCHAR(12))
ELSE CAST(O.orderDateAS VARCHAR(12)) END AS DT
FROM WORKFLOW_SHARED.MAIN.VW_CLAIMSOVERPAYMENT
WHERE DATECOMPLETED > '7/1/2015'
GROUP BY P.product,
CASE WHEN #reportModel = 'monthly' THEN CAST(MONTH(O.orderDate) AS VARCHAR(12))
ELSE CAST(O.orderDateAS VARCHAR(12)) END
This way you don't have to maintain two separate reports.
How about something simple like this
select
P.product
,Total = sum(O.price * O.quantity)
, O.orderDate
from
Products as P
inner join Orders as O on P.ID = O.ID
where
#reportModel = 'daily'
union all
select
P.product
,Total = sum(O.price * O.quantity)
,[Month] = MONTH(O.orderDate)
from
Products as P
inner JOIN Orders as O on P.ID = O.ID
group by
P.product
,[Month] = MONTH(O.orderDate)
where
#reportModel = 'monthly'