Remove additional delimeter from records - sql-server

I am using STUFF function to retrieve multiple row values to a column.
ALTER PROCEDURE [dbo].[PrintOutlettoOutletTransfer]
#TransferCode varchar(20),
#token nvarchar(50)
AS
BEGIN
DECLARE #Isprint AS int
DECLARE #user AS VARCHAR(50)
DECLARE #PcIp AS VARCHAR(50)
DECLARE #PcName AS VARCHAR(50)
DECLARE #AditUserName AS VARCHAR(100)
DECLARE #outlet AS VARCHAR(100)
DECLARE #ServerName AS VARCHAR(200)
SET #user = ''
-- Retrive data from user login history from Tocken
SELECT
#user = UserLogInHistory.UserId,
#PcIp = LogInIp,
#outlet=OutletCode,
#ServerName = ServerId,
#AditUserName = Users.UserFullName
from UserLogInHistory
inner join Users on Users.UserId = UserLogInHistory.UserId
where UserTokenId = #token;
SELECT
ISNULL(InventoryTransferHeader.TransferCode,'') AS 'TransferId',
ISNULL(InventoryTransferHeader.TransferDate,'') AS 'TransferDate',
ISNULL(InventoryTransferLine.ItemCode,'') AS 'ItemCode',
ISNULL(InventoryTransferLine.ItemDescription,'') AS 'ItemDescription',
ISNULL(InventoryTransferLine.Qty,'') AS 'Qty',
STUFF((SELECT InventoryTransferSerials.SerialNo + ' / '
FROM InventoryTransferSerials
WHERE InventoryTransferSerials.ItemCode = InventoryTransferLine.ItemCode
AND InventoryTransferSerials.TransferCode = InventoryTransferHeader.TransferCode
FOR XML PATH('')),1,1,'') AS 'SN',
ISNULL(InventoryStatus.Description,'') AS 'InventoryStatus',
ISNULL(InventoryTransferHeader.FromOutlet,'') AS 'outletFrom' ,
ISNULL(F.OutletDesc,'') AS 'fromOutletsDesc',
ISNULL(InventoryTransferHeader.TOOutlet,'') AS 'outletTO' ,
ISNULL(T.OutletDesc,'') AS 'toOutletsDesc'
FROM InventoryTransferHeader
INNER JOIN
Outlets F ON InventoryTransferHeader.FromOutlet = F.OutletCode
INNER JOIN
Outlets T ON InventoryTransferHeader.ToOutlet = T.OutletCode
LEFT OUTER JOIN
InventoryStatus on InventoryStatus.StatusCode = InventoryTransferHeader.InventoryStatus
LEFT OUTER JOIN
InventoryTransferLine ON InventoryTransferLine.TransferCode = InventoryTransferHeader.TransferCode
WHERE (InventoryTransferHeader.TransferCode = #TransferCode OR #TransferCode = '') AND (InventoryStatus.StatusCode = 1 OR InventoryStatus.StatusCode = 2)
--- Insert Values to Audit table
INSERT INTO PrintHistory
([DocType],[Reference],[AuditOutlet],[AuditUser],[AuditDate],[AuditType],[AuditIp],[AuditPc],[AuditUserName])
values
('OutletToOutletInventoryTransfer',#TransferCode,#outlet,#user,GETDATE(),'1',#PcIp,#ServerName,#AditUserName);
UPDATE InventoryTransferHeader
SET InventoryTransferHeader.[Print] = 1,
InventoryTransferHeader.[PrintUser] = #user,
InventoryTransferHeader.[PrintDate] = GETDATE()
WHERE InventoryTransferHeader.TransferCode = #TransferCode AND InventoryTransferHeader.[Print] = 0;
END
It gives an output as below.
0000227 / 10000228 / 10000229 / 10000230 / 10000231 /
I want to remove the additional "/" from records.
Please help.

One option to remove a single trailing forward slash at the end is to wrap your current SELECT in a subquery and use LEFT:
SELECT LEFT(t.SN, LEN(t.SN) - 1)
FROM
(
SELECT STUFF((SELECT InventoryTransferSerials.SerialNo + ' /'
FROM InventoryTransferSerials
WHERE InventoryTransferSerials.ItemCode =
InventoryTransferLine.ItemCode AND
InventoryTransferSerials.TransferCode =
InventoryTransferHeader.TransferCode
FOR XML PATH('')), 1, 1, '') AS SN
FROM yourTable
) t

Related

SQL Server: Dynamically Pivot Temp Table (Dynamic SQL)

I want to Dynamically pivot my #Tempproj table. This table consists of multiple CTE's that I insert into this #Tempproj. Now I want to Pivot this temptable, however I currently can only do it now when I create a Physical table instead of a Temporary one.
When I run it with the following query it says:
Must declare the table variable "#Tempproj"
I don't know how to mix this SQL with Dynamic SQL correctly.
First I declare a temp table, quite a long statement, and all the way below is the Dynamic SQL Pivot query I use.
CREATE TABLE #Tempproj
(
[ProjKey] nvarchar(250) NULL
,[Lvl3] nvarchar(250) NULL
,[Lvl2] nvarchar(250) NULL
,[Lvl1] nvarchar(250) NULL
,[Element ID] nvarchar(250) NULL
,[Activiteit] nvarchar(250) NULL
,[Doel] nvarchar(250) NULL
,[Activiteitsnummer] nvarchar(250) NULL
,[Begindatum/-tijd] date NULL
,[Einddatum/-tijd] date NULL
,[Status projectfase] nvarchar(250) NULL
,[Afsluitingsdatum/-tijd] date NULL
,[Projectactiviteit] nvarchar(250) NULL
,[VerantwoordelijkeId] nvarchar(250) NULL
,[Verantwoordelijke] nvarchar(250) NULL
,[Categorie] nvarchar(250) NULL
,[Prioriteit] nvarchar(250) NULL
,[DataAreaID] nvarchar(250) NULL
);
WITH cteP AS (
SELECT
[NAME]
,ELEMENTNUMBER
,PARENTELEMENTNUMBER
,PathID = CAST(ELEMENTNUMBER AS VARCHAR(MAX))
,DATAAREAID
FROM HIERARCHYTREETABLE
WHERE NULLIF(PARENTELEMENTNUMBER, '') IS NULL
UNION All
SELECT
[NAME] = r.[NAME]
,ELEMENTNUMBER = r.ELEMENTNUMBER
,PARENTELEMENTNUMBER = r.PARENTELEMENTNUMBER
,PathID = p.PathID+CONCAT(',',CAST(r.ELEMENTNUMBER AS VARCHAR(MAX)))
,DATAAREAID = r.DATAAREAID
FROM HIERARCHYTREETABLE r
JOIN cteP p ON r.PARENTELEMENTNUMBER = p.ELEMENTNUMBER
AND r.DATAAREAID = p.DATAAREAID
)
,
cteP2 AS (
SELECT
B.Lvl3
,B.Lvl2
,B.Lvl1
,HIERARCHYTREETABLE.ELEMENTNUMBER AS 'Element ID'
,HIERARCHYTREETABLE.[NAME] AS 'Activiteit'
,SMMACTIVITIES.PURPOSE AS 'Doel'
,SMMACTIVITIES.ACTIVITYNUMBER AS 'Activiteitsnummer'
,SMMACTIVITIES.STARTDATETIME AS 'Begindatum/-tijd'
,NULLIF(SMMACTIVITIES.ENDDATETIME, '') AS 'Einddatum/-tijd'
,CASE WHEN SMMACTIVITIES.CLOSED = 1 THEN 'Gesloten' ELSE 'Open' END AS 'Status projectfase'
,NULLIF(SMMACTIVITIES.ACTUALENDDATETIME, '') AS 'Afsluitingsdatum/-tijd'
,SMMACTIVITIES.PROJACTID AS 'Projectactiviteit'
,SMMACTIVITIES.RESPONSIBLEEMPLOYEE AS 'VerantwoordelijkeId'
,DIRPARTYTABLE.[NAME] AS 'Verantwoordelijke'
,CASE SMMACTIVITIES.CATEGORY
WHEN 0 THEN 'Afspraak'
WHEN 1 THEN 'Taak'
WHEN 2 THEN 'Actie'
WHEN 3 THEN 'Melding'
END AS Categorie
,CASE SMMACTIVITIES.TASKPRIORITY
WHEN 0 THEN 'Normaal'
WHEN 1 THEN 'Laag'
WHEN 2 THEN 'Hoog'
END AS Prioriteit
,HIERARCHYTREETABLE.DATAAREAID
FROM HIERARCHYTREETABLE
LEFT JOIN cteP
ON cteP.ELEMENTNUMBER = HIERARCHYTREETABLE.ELEMENTNUMBER
AND cteP.DATAAREAID = HIERARCHYTREETABLE.DATAAREAID
CROSS Apply (
SELECT Lvl1 = xDim.value('/x[3]','varchar(50)')
,Lvl2 = xDim.value('/x[2]','varchar(50)')
,Lvl3 = xDim.value('/x[1]','varchar(50)')
,Lvl4 = xDim.value('/x[4]','varchar(50)')
FROM ( VALUES (CAST('<x>' + REPLACE(PathID,',','</x><x>')+'</x>' AS xml))) B(xDim)
) B
LEFT JOIN SMMACTIVITIES
ON SMMACTIVITIES.RECID = HIERARCHYTREETABLE.REFRECID
AND SMMACTIVITIES.DATAAREAID = HIERARCHYTREETABLE.DATAAREAID
LEFT JOIN EMPLTABLE
ON SMMACTIVITIES.RESPONSIBLEEMPLOYEE = EMPLTABLE.EMPLID
LEFT JOIN DIRPARTYTABLE
ON DIRPARTYTABLE.PARTYID = EMPLTABLE.PARTYID
AND DIRPARTYTABLE.DATAAREAID = EMPLTABLE.DATAAREAID
)
INSERT INTO #Tempproj(
[ProjKey],Lvl1,Lvl2,Lvl3,[Element ID]
,Activiteit,Doel,Activiteitsnummer,[Begindatum/-tijd],[Einddatum/-tijd],[Status projectfase]
,[Afsluitingsdatum/-tijd],Projectactiviteit,VerantwoordelijkeId
,Verantwoordelijke,Categorie,Prioriteit,DataAreaID)
SELECT
PROJTABLE.PROJID + '-' + PROJTABLE.DATAAREAID AS 'ProjKey'
,cteP2.*
FROM CteP2
LEFT JOIN HIERARCHYTREETABLE
ON HIERARCHYTREETABLE.ELEMENTNUMBER = CteP2.Lvl3
AND HIERARCHYTREETABLE.DATAAREAID = CteP2.DataAreaId
LEFT JOIN PROJTABLE
ON PROJTABLE.PROJID = HIERARCHYTREETABLE.[NAME]
AND PROJTABLE.DATAAREAID = HIERARCHYTREETABLE.DATAAREAID
WHERE Activiteit NOT LIKE 'MLD%'
AND Activiteit NOT LIKE 'O-%'
AND Activiteit NOT LIKE 'OTR%'
AND SUBSTRING(Activiteit, 1, 1) NOT IN ('1','2','3','4','5','6','7','8','9');
DECLARE #Columns as VARCHAR(MAX)
SELECT
#Columns = COALESCE(#Columns + ', ','') + QUOTENAME(Activiteit)
FROM
(SELECT DISTINCT Activiteit FROM #Tempproj
) AS B
ORDER BY B.Activiteit
DECLARE #SQL as VARCHAR(MAX)
SET #SQL = 'SELECT ProjKey, ' + #Columns + '
FROM
(
SELECT T.ProjKey,
T.Activiteit,
T.[Begindatum/-tijd]
FROM #Tempproj T
) as PivotData
PIVOT
(
max([Begindatum/-tijd])
FOR Activiteit IN (' + #Columns + ')
) AS PivotResult
ORDER BY ProjKey'
EXEC (#SQL)
Then when I run the query, it says that I haven't declared my #Tempproj table yet.
Now I see that I mix up SQL with Dynamic SQL, but I don't know how to fix this giant statement into this.
Any suggestions what I could do here?
EDIT: Table Variable replaced with an actual Temporary Table.

Increase Open Query performance

I have a small problem with this query. It returns me around 13k rows and takes around 12seconds or more.
SELECT
hla.Id,
hc.Nome AS Colaborador,
hla.UAP,
hra.Referencia,
hra.QtdAbastecimento,
hra.QtdPecasPorCaixa,
hra.QtdCaixas,
A.Etiqueta,
A.Localizacao
FROM
hListasAbastecimento hla
INNER JOIN hColaboradores hc
ON hc.Id = hla.ColaboradorId
INNER JOIN
hReferenciasAbastecimento hra
ON hla.Id = hra.ListaAbastecimentoId
LEFT JOIN OPENQUERY(MACPAC,
'SELECT
FET001.ET0102 AS Referencia,
FET001.ET0101 AS Etiqueta,
FET001.ET0109 AS Localizacao
FROM
AUTO.D805DATPOR.FET001 FET001
WHERE FET001.ET0104 = ''POE''
AND FET001.ET0105 = ''DIS''
ORDER BY FET001.ET0101 ASC ') A
ON A.Referencia = hra.Referencia
WHERE hla.Id = #Id
I'm not sure what can i do to increase the performance of this. I am already using the only primary key to link both tables. I also have no control over the linked server so i cannot create new indexes
UPDATE
I found out through a co-worker that there was a table on linked server that could help me get results by "UAP" which takes only 4 seconds just to get around 1700 rows which is what i needed
ALTER PROCEDURE GenerateListaAbastecimento
#Id INT,
#UAP NVARCHAR(20)
AS
BEGIN
CREATE TABLE #tempTable
(
Id INT PRIMARY KEY,
Referencia NVARCHAR(15),
Etiqueta INT,
Localizacao NVARCHAR(20)
UNIQUE(Id, Etiqueta)
)
DECLARE #SQL NVARCHAR(MAX)
SELECT #SQL ='INSERT INTO #tempTable
SELECT
Id,
Referencia,
Etiqueta,
Localizacao
FROM OPENQUERY(MACPAC,
''SELECT
ROW_NUMBER() OVER(ORDER BY FET001.ET0101 ASC) AS Id,
A.RH6001 as Referencia,
FET001.ET0101 AS Etiqueta,
FET001.ET0109 AS Localizacao
FROM AUTO.D805DATPOR.TRP060H AS A
LEFT JOIN AUTO.D805DATPOR.FET001 FET001
ON FET001.ET0102 = A.RH6001
AND FET001.ET0104 = ''''POE''''
AND FET001.ET0105 = ''''DIS''''
AND A.RH6002 = N''' + QUOTENAME(#UAP,N'''') + N''' '')'
EXEC sp_executesql #SQL
SELECT
hla.Id,
hc.Nome AS Colaborador,
hla.UAP,
hra.Referencia,
hra.QtdAbastecimento,
hra.QtdPecasPorCaixa,
hra.QtdCaixas,
A.Etiqueta,
A.Localizacao
FROM
hListasAbastecimento hla
INNER JOIN hColaboradores hc
ON hc.Id = hla.ColaboradorId
INNER JOIN
hReferenciasAbastecimento hra
ON hla.Id = hra.ListaAbastecimentoId
LEFT JOIN #tempTable A
ON A.Referencia = hra.Referencia
WHERE hla.Id = #Id
ORDER BY A.Etiqueta ASC
DROP TABLE #tempTable
END

How to optimise this stored procedure?

I have the stored procedure below and I am having two issues with it
It is running very slowly, and
It is returning a blank result set
The idea for the stored procedure is to do a sequence check to find if any account numbers have not been assigned.
Step 1 loops through all the branches and builds up the tempdetails table.
After that it creates the list of all the numbers that have been used and uses that to delete everything that exists to leave a list of those accountnumbers that do not exist,
But een though I know there are missing account numbers, aside from the exceptionally long tun time it is returning a blank result set.
Anyone have any ideas what is going wrong with it?
Thanks
ALTER PROCEDURE [dbo].[PracticeFindMissingSequenceDetail]
#pracId VARCHAR(128),
#Prefix VARCHAR(256)
AS
BEGIN
DECLARE #TempDetails TABLE (SequenceCheck VARCHAR(24),
Prefix VARCHAR(4),
BranchName VARCHAR(256),
RisStatus VARCHAR(256),
Rislink VARCHAR(256)
);
DECLARE #Branchlist TABLE (BranchId INTEGER,
BranchName VARCHAR(256),
BranchPrefix VARCHAR(4),
PrefixLength INT,
SequenceLength INT
);
DECLARE #TempPatNo TABLE (Patno VARCHAR(24));
DECLARE #BranchName VARCHAR(256),
#BranchPrefix VARCHAR(256),
#PrefixLength INT,
#BranchId INT,
#SequenceLength INT,
#rangestart INTEGER,
#rangeend INTEGER,
#rangenow INTEGER,
#startDate DATETIME,
#Patno VARCHAR(128),
#FormatZeroes VARCHAR(3),
#CurrentLength INT,
#RangeString VARCHAR(256);
INSERT INTO #Branchlist (BranchId, BranchName, BranchPrefix, PrefixLength, SequenceLength)
SELECT
b.id, b.name, b.prefix, PrefixLength, SequenceLength
FROM
Branch b
INNER JOIN
Practice pr ON pr.id = b.practiceid
INNER JOIN
[Sequence] s ON s.id = b.id
WHERE
pr.APIKey = #pracID
AND b.inactive = 0
AND b.prefix = #Prefix
/* insert values for each branch into table*/
DECLARE BranchPointer CURSOR FOR
SELECT BranchID FROM #Branchlist
OPEN BranchPointer
FETCH NEXT FROM BranchPointer INTO #BranchId
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT #BranchPrefix = (SELECT BranchPrefix
FROM #Branchlist
WHERE BranchId = #BranchId)
SELECT #PrefixLength = (SELECT PrefixLength
FROM #Branchlist
WHERE BranchId = #BranchId)
SELECT #SequenceLength = (SELEct SequenceLength
FROM #Branchlist
WHERE BranchId = #BranchId)
/* Set the starting date from the sequence */
SELECT #startDate = (SELECT MIN(MinimumSequenceDate)
FROM [Sequence] s
WHERE s.id = #BranchId)
/*get the earliest number in the sequence from the startdate*/
SELECT #rangestart = (SELECT MIN(SUBSTRING(v.bookingnumber, 3, LEN(bookingnumber)))
FROM Visit v
INNER join Branch b ON b.id = v.branchid
INNER join Practice pr ON pr.id = b.practiceid
WHERE pr.APIKey = #pracId
AND LEFT(v.bookingnumber, 2) = #Prefix
AND v.date >= #startDate
AND v.branchid = #BranchId);
/*get the latest number in the sequence from the startdate*/
SELECT #rangeend = (SELECT MAX(SUBSTRING(v.bookingnumber, 3, LEN(bookingnumber)))
FROM Visit v
INNER JOIN Branch b ON b.id = v.branchid
INNER JOIN Practice pr ON pr.id = b.practiceid
WHERE pr.APIKey = #pracId
AND LEFT(v.bookingnumber, 2) = #Prefix
AND v.date >= #startDate
AND v.branchid = #BranchId);
SET #RangeNow = #rangestart
WHILE #rangenow < #rangeend
BEGIN
/*check if leading zeroes are needed in the number and add them if needed*/
SET #RangeString = CAST(#RangeNow AS VARCHAR(256))
SET #CurrentLength = LEN(#rangenow)
IF #prefixlength + #currentlength < #SequenceLength
WHILE #CurrentLength + #PrefixLength < #SequenceLength
BEGIN
SET #RangeString = '0' + #RangeString;
SET #currentlength = LEN(#RangeString);
END;
/*Insert full sequence into temporary table*/
INSERT INTO #TempDetails (SequenceCheck, Prefix, BranchName)
SELECT #Prefix + #RangeString, #Prefix, #BranchName
SET #rangenow =#rangenow+1
END;
FETCH NEXT FROM BranchPointer INTO #BranchName
END
CLOSE BranchPointer
DEALLOCATE BranchPointer
/*delete existing sequence numbers from table*/
INSERT INTO #TempPatNo (PatNo)
SELECT BookingNumber
FROM Visit v1
INNER JOIN Branch b1 ON b1.id = v1.branchid
INNER JOIN Practice pr1 ON pr1.id = b1.practiceid
WHERE pr1.APIKey = #pracId
DELETE #TempDetails
WHERE sequencecheck IN (SELECT patNo FROM #TempPatNo)
/*Insert the status and link for error messages*/
UPDATE #tempDetails
SET RisStatus = (SELECT Status
FROM RISErrors r
INNER JOIN Practice pr ON pr.id = r.PracticeId
WHERE pr.APIKey = #pracId
AND VisitNumber = SequenceCheck
AND r.id = (SELECT MAX(r1.id)
FROM RISErrors r1
INNER JOIN Practice pr1 ON pr1.id = r1.PracticeId
WHERE pr1.APIKey = #pracId
AND VisitNumber = SequenceCheck)),
RisLink = 'http://billing.cryanic.co.za/Clinton/RISErrors?searchquery=' + SequenceCheck
/*return missing numbers into sequence control callong procedure*/
SELECT DISTINCT SequenceCheck, RisStatus, Rislink
FROM #TempDetails
END
After reading your procedure, I made some assumptions:
there is a one-to-one relationsship between sequence and branch
the visits are the details of a sequence
the BranchName and BranchPrefix aren't needed.
I recommend to not do lookups by BranchID but instead retrieve the values from the cursor query. The table BranchList isn't needed, you can base the cursor on the query directly. Also, the TempPatNo table can be avoided.
Here's what I have come up with:
ALTER PROCEDURE [dbo].[PracticeFindMissingSequenceDetail]
#pracId VARCHAR(128),
#Prefix VARCHAR(256)
AS
BEGIN
DECLARE #TempDetails TABLE (
BranchID INT,
SequenceCheck VARCHAR(24),
RisStatus VARCHAR(256),
Rislink VARCHAR(256)
);
DECLARE
#PrefixLength INT,
#BranchId INT,
#SequenceLength INT,
#rangestart INTEGER,
#rangeend INTEGER,
#rangenow INTEGER,
#startDate DATETIME;
/* insert values for each branch into table*/
DECLARE BranchPointer CURSOR FOR
SELECT b.id, PrefixLength, SequenceLength, s.MinimumSequenceDate
FROM Branch b
INNER JOIN Practice pr ON pr.id = b.practiceid
INNER JOIN [Sequence] s ON s.id = b.id
WHERE pr.APIKey = #pracID
AND b.prefix = #Prefix
AND b.inactive = 0
OPEN BranchPointer
FETCH NEXT FROM BranchPointer INTO #BranchId, #PrefixLength, #SequenceLength, #startDate
WHILE ##FETCH_STATUS = 0
BEGIN
/*get the earliest and latest number in the sequence from the startdate*/
SELECT
#rangestart = MIN(SUBSTRING(v.bookingnumber, 3, LEN(v.bookingnumber))),
#rangeend = MAX(SUBSTRING(v.bookingnumber, 3, LEN(v.bookingnumber)))
FROM Visit v
WHERE v.branchid = #BranchId
AND v.date >= #startDate
AND LEFT(v.bookingnumber, 2) = #Prefix;
SET #RangeNow = #rangestart
WHILE #rangenow < #rangeend
BEGIN
/*Insert full sequence into temporary table*/
INSERT INTO #TempDetails (BranchID, SequenceCheck)
SELECT #BranchId,
#Prefix + REPLICATE('0', #SequenceLength-#PrefixLength-LEN(#rangenow)) + CAST(#RangeNow AS VARCHAR(256));
SET #rangenow =#rangenow+1
END;
FETCH NEXT FROM BranchPointer INTO #BranchId, #PrefixLength, #SequenceLength, #startDate
END
CLOSE BranchPointer
DEALLOCATE BranchPointer
/*delete existing sequence numbers from table*/
DELETE FROM #TempDetails
FROM #TempDetails t
INNER JOIN Visit v ON t.BranchID = v.branchid
WHERE t.SequenceCheck = v.BookingNumber
/*Insert the status and link for error messages*/
UPDATE #tempDetails
SET RisStatus = (SELECT Status
FROM RISErrors r
INNER JOIN Practice pr ON pr.id = r.PracticeId
WHERE pr.APIKey = #pracId
AND VisitNumber = SequenceCheck
AND r.id = (SELECT MAX(r1.id)
FROM RISErrors r1
INNER JOIN Practice pr1 ON pr1.id = r1.PracticeId
WHERE pr1.APIKey = #pracId
AND VisitNumber = SequenceCheck)),
RisLink = 'http://billing.cryanic.co.za/Clinton/RISErrors?searchquery=' + SequenceCheck
/*return missing numbers into sequence control callong procedure*/
SELECT DISTINCT SequenceCheck, RisStatus, Rislink
FROM #TempDetails
END

How to replace a SQL While Loop with a Select

We have a procedure that uses the below While..Loop to calculate the total number of Days and Weeks paid for absences in our PayRoll System:
EDITED
Declare #Absences Table (slno int identity (1,1),AbsenceId int, ToDate datetime)
INSERT INTO #Absences (AbsenceID,ToDate)
Select AbsenceID, AB.ToDate
from t_Absence AB with (nolock)
Inner Join t_AbsenceCategory AB_CAT with (nolock) ON (AB.AbsenceCategoryID = AB_CAT.AbsenceCategoryID)
where (AB_CAT.IsSSP =1)
and ClientID = #ClientID
and AB.FromDate >= #SSPYearStart --D7830 SJH 21/10/2015
order BY AB.ToDate desc
Declare #AbsenceID INT, #iCtr INT, #maxRows int
Declare #FromDate datetime
SELECT #iCtr = 1, #maxRows = MAX(slno) FROM #Absences
Select #SSPDaysPaid = 0, #SSPweeksPaid = 0, #QualifyingDaysInWeek = 0
If IsNull(#maxRows,0) > 0 select #FromDate = FromDate from t_Absence where AbsenceID = (SELECT AbsenceID FROM #Absences WHERE slno = 1)
WHILE ( #ictr <= #maxRows )
BEGIN
SELECT #AbsenceID = AbsenceID
FROM #Absences
WHERE slno = #iCtr
--Print #AbsenceID
If Exists (Select TOP 1 1 from t_Absence where ToDate > DATEADD(dd,-56, #FromDate))
BEGIN
SELECT #SSPDaysPaid = #SSPDaysPaid + IsNull(A.SSPDays,0),
#FromDate = A.FromDate
from t_Absence A
where A.AbsenceID = #AbsenceID
print '#SSPDaysPaid=' + CAST(#SSPDaysPaid AS Varchar(3)) + ' in Absence ' + cast(#AbsenceID as varchar(6))
DECLARE #Monday int, #Tuesday int, #Wednesday int, #Thursday int, #Friday int, #Saturday int, #Sunday int
SELECT #Monday = QD.Monday, #Tuesday = QD.Tuesday, #Wednesday =QD.Wednesday, #Thursday =QD.Thursday,
#Friday = QD.Friday, #Saturday = QD.Saturday, #Sunday = QD.Sunday
from t_PayrollEmployeeSSPQualifyingDays QD
inner JOIN t_Absence A on A.ClientID = QD.ClientID and A.FromDate = QD.DateFrom AND A.ToDate = QD.DateTo
where A.ClientID = #ClientId
SET #QualifyingDaysInWeek = #Monday + #Tuesday + #Wednesday + #Thursday + #Friday + #Saturday + #Sunday
print '#QualifyingDaysInWeek = ' + cast(#QualifyingDaysInWeek as char(2))
END
SET #iCtr = #iCtr + 1
END
if #QualifyingDaysInWeek <> 0 Set #SSPWeeksPaid = #SSPDaysPaid/#QualifyingDaysInWeek Else Set #SSPWeeksPaid = 0
print '#SSPWeeksPaid=' + cast(#SSPWeeksPaid as varchar(2))
Select
BradfordFactor
, CSPFDEntitlement
, CSPHDEntitlement
, CSPDaysTaken
, HasContract
, CSPFullDaysTaken
, CSPHalfDaysTaken -- DevTask 112703 06/11/2012 SWB Start
, IsNull(#SSPDaysPaid,0) as 'SSPDaysPaid'
, IsNull(#SSPWeeksPaid,0) as 'SSPWeeksPaid'
from
fn_GetEmployeeBradfordFactor(#ClientID,DEFAULT,0, DEFAULT)
END
As I need to find out this information for several different persons I will have to execute this stored proc and loop once for each client Id (#ClientId) identified in the calling procedure ...
Is there an alternative to this loop and would it be worth it in terms of performance?
Based on the OP comment, no other value is needed from the loop, but #QualifyingDaysInWeek
#RicardoC : Well I don't need the prints (they're there for debugging and analysis) but I do need to sum the values #QualifyingDaysInWeek in for each record in the table #Absences
There appears to be no need for the loop at all.
Declare #Absences Table (slno int identity (1,1),AbsenceId int, ToDate datetime)
INSERT INTO #Absences (AbsenceID,ToDate)
Select AbsenceID, AB.ToDate
from t_Absence AB with (nolock)
Inner Join t_AbsenceCategory AB_CAT with (nolock) ON (AB.AbsenceCategoryID = AB_CAT.AbsenceCategoryID)
where (AB_CAT.IsSSP =1)
and ClientID = #ClientID
and AB.FromDate >= #SSPYearStart --D7830 SJH 21/10/2015
order BY AB.ToDate desc
DECLARE #QualifyingDaysInWeek INT
SELECT #QualifyingDaysInWeek = SUM(QD.Monday + QD.Tuesday + QD.Wednesday + QD.Thursday + QD.Friday + QD.Saturday + QD.Sunday)
FROM t_PayrollEmployeeSSPQualifyingDays QD
INNER JOIN t_Absence A ON A.ClientID = QD.ClientID
AND A.FromDate = QD.DateFrom
AND A.ToDate = QD.DateTo
WHERE A.ClientID = #ClientId;

Not Exists not playing well with multiple columns

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.

Resources