SQL Execute one query of 2 based on condition - sql

I have 2 working query that I need to use to generate a report, I have configured 4 parameters that user can choose and I want that if idarea=0 then first query is executed, else second one. I tried the following but give me an error because of the nested with... any help? Thanks
declare #DataDa as date='20210501';
declare #DataA as date='20210504';
declare #idUtilizzatore as int=0;
declare #idArea as int=0;
SELECT CASE WHEN ( #idArea=0 )
THEN (select dbaree.dbo.StoricoAree.Descr as Lettore,
TIMBACCESSI.CODICEBADGE,
COGNOME as Cognome,NOME as Nome,
AZIENDE.DESCR as Azienda,FORMAT(DATAORA,'HH:mm') as Ora,
FORMAT(dataora,'dd/MM/yyyy') as Data,
iif(verso=0,'U','E') as Flusso from timbaccessi
join badge on badge.codice=timbaccessi.codicebadge
join anagrafico on anagrafico.ID=badge.IDUTILIZZATORE
join profilo on profilo.IDUTILIZZATORE=badge.IDUTILIZZATORE
join aziende on aziende.id=profilo.IDAZIENDA
join terminali on terminali.id=TIMBACCESSI.IDTERMINALE
join comparee on terminali.id=COMPAREE.IDTERMINALE
join dbAree.dbo.StoricoAree on storicoaree.idArea=COMPAREE.IDAREA
where (DATAORA>=#DataDa and DATAORA<#DataA) and (DATAORA >= dataDa and(DATAORA <= dataA or dataa is null)) and (badge.DATAFINE is null or badge.DATAFINE>=#DataA) AND (badge.idUtilizzatore = #IdUtilizzatore OR 0 = #IdUtilizzatore) AND (PROFILO.IDAZIENDA=#idAzienda OR 0 = #idAzienda) and (dbaree.dbo.StoricoAree.id=#idArea OR 0=#idArea)
group by dataora,cognome,nome,dbaree.dbo.StoricoAree.Descr,timbaccessi.CODICEBADGE,aziende.DESCR,timbaccessi.VERSO
ORDER BY Azienda asc, cognome asc, nome asc, Data asc, ora asc)
ELSE (with area as(
select t.CODICEBADGE as 'Numero tessera',
COGNOME as Cognome,NOME as Nome,
AZIENDE.DESCR as Azienda,FORMAT(DATAORA,'HH:mm') as Ora,
FORMAT(dataora,'dd/MM/yyyy') as Data,
iif(verso=0,'U','E') as Flusso, t.IDTERMINALE,t.DATAORA from timbaccessi t
join badge on badge.codice=t.codicebadge
join anagrafico on anagrafico.ID=badge.IDUTILIZZATORE
join profilo on profilo.IDUTILIZZATORE=badge.IDUTILIZZATORE
join aziende on aziende.id=profilo.IDAZIENDA
join terminali on terminali.id=t.IDTERMINALE
where (DATAORA>=#DataDa and DATAORA<#DataA)
and (badge.DATAFINE is null or badge.DATAFINE>=#DataA) AND (badge.idUtilizzatore = #IdUtilizzatore OR 0 = #IdUtilizzatore)
group by dataora,cognome,nome,t.CODICEBADGE,aziende.DESCR,t.VERSO,t.IDTERMINALE)
select a.[Numero tessera],a.Cognome,a.Nome,a.Azienda,a.Ora,a.Data,a.Flusso,s.Descr from area a join dbAree.dbo.StoricoAree s on s.id=#idArea where a.IDTERMINALE in (select idterminale from dbAree.dbo.termArea t join dbaree.dbo.StoricoAree s on s.id=t.idArea where t.idArea=#idArea and (#DataDa >= s.datada and (s.dataA is null or #DataDa < s.dataA)))
order by a.DATAORA )
END;

Just use IF:
IF (#idArea = 0)
BEGIN
<query1>;
END;
ELSE
<query 2>;

There is another way besides if that I've found useful. You can use union all and appropriately filter in your where statements.
It only makes sense when your output columns are the same number, order, and type. In your case, that's not true, but a very small modification can make it so:
declare #DataDa as date='20210501';
declare #DataA as date='20210504';
declare #idUtilizzatore as int=0;
declare #idArea as int=0;
with area as(
select t.CODICEBADGE as 'Numero tessera',
COGNOME as Cognome,NOME as Nome,
AZIENDE.DESCR as Azienda,
FORMAT(DATAORA,'HH:mm') as Ora,
FORMAT(dataora,'dd/MM/yyyy') as Data,
iif(verso=0,'U','E') as Flusso,
t.IDTERMINALE,t.DATAORA
from timbaccessi t
join badge on badge.codice=t.codicebadge
join anagrafico on anagrafico.ID=badge.IDUTILIZZATORE
join profilo on profilo.IDUTILIZZATORE=badge.IDUTILIZZATORE
join aziende on aziende.id=profilo.IDAZIENDA
join terminali on terminali.id=t.IDTERMINALE
where (DATAORA>=#DataDa and DATAORA<#DataA)
and (badge.DATAFINE is null or badge.DATAFINE>=#DataA)
AND (badge.idUtilizzatore = #IdUtilizzatore OR 0 = #IdUtilizzatore)
group by dataora,cognome,nome,t.CODICEBADGE,aziende.DESCR,t.VERSO,t.IDTERMINALE
)
select dbaree.dbo.StoricoAree.Descr as Lettore,
TIMBACCESSI.CODICEBADGE,
null as [Numero tessera], // not relevant unless #idArea <> 0
COGNOME as Cognome,
NOME as Nome,
AZIENDE.DESCR as Azienda,
FORMAT(DATAORA,'HH:mm') as Ora,
FORMAT(dataora,'dd/MM/yyyy') as Data,
iif(verso=0,'U','E') as Flusso,
null as Desc // not relevant unless #idArea <> 0
from timbaccessi
join badge on badge.codice=timbaccessi.codicebadge
join anagrafico on anagrafico.ID=badge.IDUTILIZZATORE
join profilo on profilo.IDUTILIZZATORE=badge.IDUTILIZZATORE
join aziende on aziende.id=profilo.IDAZIENDA
join terminali on terminali.id=TIMBACCESSI.IDTERMINALE
join comparee on terminali.id=COMPAREE.IDTERMINALE
join dbAree.dbo.StoricoAree on storicoaree.idArea=COMPAREE.IDAREA
where (DATAORA>=#DataDa and DATAORA<#DataA)
and (DATAORA >= dataDa and(DATAORA <= dataA or dataa is null))
and (badge.DATAFINE is null or badge.DATAFINE>=#DataA)
AND (badge.idUtilizzatore = #IdUtilizzatore OR 0 = #IdUtilizzatore)
AND (PROFILO.IDAZIENDA=#idAzienda OR 0 = #idAzienda)
and (dbaree.dbo.StoricoAree.id=#idArea OR 0=#idArea)
and #idArea = 0 // your case ////////////////////////////////////////
group by dataora,cognome,nome,dbaree.dbo.StoricoAree.Descr,
timbaccessi.CODICEBADGE,aziende.DESCR,timbaccessi.VERSO
union all
select null as Lettore, // not relevant unless #idArea = 0
null as CODICEBADGE, // not relevant unless #idArea = 0
a.Cognome,
a.Nome,
a.Azienda,
a.Ora,
a.Data,
a.Flusso,
s.Descr
from area a
join dbAree.dbo.StoricoAree s on s.id=#idArea
where a.IDTERMINALE in (
select idterminale
from dbAree.dbo.termArea t
join dbaree.dbo.StoricoAree s on s.id=t.idArea
where t.idArea=#idArea
and (
#DataDa >= s.datada and
(s.dataA is null or #DataDa < s.dataA)
)
order by a.DATAORA
)
and (#idArea <> 0 or #idArea is null) // your else /////////////////
ORDER BY Azienda asc, cognome asc, nome asc, Data asc, ora asc;
This has the advantage of keeping the result set predictable. If it's predictable, it can have some advantages. For instance, if desired, you could turn this into a table valued function. Or you could otherwise have other code that depends on it and won't break if the output has an unexpected structure.

Related

SQL Stored Procedure Optional Parameter

I am trying to create a Stored Procedure as follows -
CREATE PROCEDURE [dbo].[Chargeable_Time] #DateFrom DATE,
#DateTo DATE,
#Allocated INT = NULL
AS
SELECT t.CaseID,
c.DisplayNo,
c.CaseName,
c.[Category],
t.[Date],
t.FeeEarner,
u.Username,
t.ChargeCode,
t.[Hours],
t.Fees,
t.GroupID,
( CASE
WHEN t.GroupID IS NOT NULL THEN 1
WHEN t.GroupID IS NULL THEN 0
END ) AS Allocated
FROM Timesheet AS t
LEFT JOIN [User] AS u
ON t.FeeEarner = u.ID
LEFT JOIN [Case] AS c
ON t.CaseID = c.ID
WHERE t.Active = 'True'
AND t.CaseID IS NOT NULL
AND t.Rate > 0
AND t.[Date] >= #DateFrom
AND t.[Date] <= #DateTo
AND Allocated = #Allocated
However I have two problems -
The Allocated field is not recognised as it is created by the procedure rather than being a field in the underlying tables, and
I want to be able to return all data if the Allocated field is left NULL. As it currently stands anyone calling the procedure can enter a 0 or 1 to filter the results, but I want them to also be able to leave the parameter blank and get the full set without the filter. I am not sure how to do this.
Any help would be greatly appreciated.
You can change the last part of the code as follows
Create PROCEDURE [dbo].[Chargeable_Time]
#DateFrom DATE,
#DateTo DATE,
#Allocated INT = NULL
AS
Begin
SELECT t.CaseID,
c.DisplayNo,
c.CaseName,
c.[Category],
t.[Date],
t.FeeEarner,
u.Username,
t.ChargeCode,
t.[Hours],
t.Fees,
t.GroupID,
( CASE
WHEN t.GroupID IS NOT NULL THEN 1
WHEN t.GroupID IS NULL THEN 0
END ) AS Allocated
FROM Timesheet AS t
LEFT JOIN [User] AS u
ON t.FeeEarner = u.ID
LEFT JOIN [Case] AS c
ON t.CaseID = c.ID
WHERE t.Active = 'True'
AND t.CaseID IS NOT NULL
AND t.Rate > 0
AND t.[Date] >= #DateFrom
AND t.[Date] <= #DateTo
AND (( CASE
WHEN t.GroupID IS NOT NULL THEN 1
WHEN t.GroupID IS NULL THEN 0
END ) = #Allocated OR #Allocated is null )
END
GO
This should do the trick. However this can be a bad solution when there is lot's of data. You should test that. Some reading about this kind of solution:
https://www.sqlskills.com/blogs/kimberly/high-performance-procedures/
CREATE PROCEDURE [dbo].[Chargeable_Time]
#DateFrom DATE,
#DateTo DATE,
#Allocated INT = NULL
AS
SELECT
t.CaseID,
c.DisplayNo,
c.CaseName,
c.[Category],
t.[Date],
t.FeeEarner,
u.Username,
t.ChargeCode,
t.[Hours],
t.Fees,
t.GroupID,
(CASE WHEN t.GroupID IS NOT NULL THEN 1
WHEN t.GroupID IS NULL THEN 0
END
) AS Allocated
FROM Timesheet AS t
LEFT JOIN [User] AS u ON t.FeeEarner = u.ID
LEFT JOIN [Case] AS c ON t.CaseID = c.ID
WHERE t.Active = 'True'
AND t.CaseID IS NOT NULL
AND t.Rate > 0
AND t.[Date] >= #DateFrom
AND t.[Date] <= #DateTo
AND (CASE WHEN t.GroupID IS NOT NULL THEN 1
WHEN t.GroupID IS NULL THEN 0
END
) = #Allocated
OR #Allocated IS NULL;
You can simplify your code by using CROSS APPLY:
CREATE PROCEDURE [dbo].[Chargeable_Time]
#DateFrom DATE,
#DateTo DATE,
#Allocated INT = NULL
AS
Begin
SELECT t.CaseID,
c.DisplayNo,
c.CaseName,
c.[Category],
t.[Date],
t.FeeEarner,
u.Username,
t.ChargeCode,
t.[Hours],
t.Fees,
t.GroupID,
a.Allocated
FROM Timesheet AS t
CROSS APPLY (VALUES (CASE WHEN t.GroupID IS NOT NULL THEN 1
WHEN t.GroupID IS NULL THEN 0
END )) As a(Allocated)
LEFT JOIN [User] AS u
ON t.FeeEarner = u.ID
LEFT JOIN [Case] AS c
ON t.CaseID = c.ID
WHERE t.Active = 'True'
AND t.CaseID IS NOT NULL
AND t.Rate > 0
AND t.[Date] >= #DateFrom
AND t.[Date] <= #DateTo
AND (#Allocated IS NULL OR a.Allocated = #Allocated)
END
GO

Query is working fine in second execution but taking too much time in first execution

I am writing a query which is accepting a comma separated string and calculating the sum of transaction. which is working fine as result wise but taking too much time to execute in first attempt. I understand its need tuning but didn't find out the exact reason can any one point me whats wrong with my query.
Declare #IDs nvarchar(max)='1,4,5,6,8,9,43,183'
SELECT isnull(isnull(SUM(FT.PaidAmt),0) - isnull(SUM(CT.PaidAmt),0),0) [Amount], convert(char(10),FT.TranDate,126) [Date]
from FeeTransaction FT
Inner Join (
Select max(P.Id) [Id], P.TranMainId, isnull(SUM(P.AmtToPay),0) [Amt]
From Patient_Account P
Group By P.TranMainId
) PA ON FT.Id = PA.TranMainId
Inner Join Patient_Account XP ON PA.Id = XP.Id
Inner Join Master_Fee MF ON XP.FeeId = MF.Id
INNER Join Master_Patient MP ON FT.PID = MP.Id
Inner Join Master_FeeType TY ON MF.FeeTypeId = TY.Id
Left JOIN FeeTransaction CT on FT.TransactionId = CT.TransactionId AND CT.TranDate between '2019'+'08'+'01' and '2019'+'08'+'31' and CT.[Status] <> 'A' AND isnull(CT.IsCancel,0) = 1
Where convert(nvarchar,FT.TranDate,112) between '2019'+'08'+'01' and '2019'+'08'+'31' AND FT.[Status] = 'A' AND XP.FeeId in (SELECT val FROM dbo.f_split(#IDs, ','))
AND isnull(FT.IsCancel,0) = 0 AND FT.EntryBy = 'rajan'
Group By convert(char(10),FT.TranDate,126)
I would rephrase the query a bit:
select coalesce(SUM(FT.PaidAmt), 0) - coalesce(SUM(CT.PaidAmt), 0)as [Amount],
convert(char(10),FT.TranDate,126) [Date]
from FeeTransaction FT join
(select xp.*,
coalesce(sum(p.amttopay) over (TranMainId), 0) as amt
from Patient_Account XP ON PA.Id = XP.Id
) xp join
Master_Fee MF
on XP.FeeId = MF.Id join
Master_Patient MP
on FT.PID = MP.Id join
Master_FeeType TY
on MF.FeeTypeId = TY.Id left join
FeeTransaction CT
on FT.TransactionId = CT.TransactionId and
CT.TranDate between '20190801' and '20190831' and
CT.[Status] <> 'A' and
CT.IsCanel = 1
where FT.TranDate >= '20190801' and and
FT.TranDate < '20190901'
FT.[Status] = 'A' AND
XP.FeeId in (SELECT val FROM dbo.f_split(#IDs, ',')) and
(FT.IsCancel = 0 or FT.IsCancel IS NULL) and
FT.EntryBy = 'rajan'
Group By convert(char(10), FT.TranDate, 126)
Then for this version, you specifically an index on FeeTransaction(EntryBy, Status, TranDate, Cancel).
Note the following changes:
You do not need to aggregate Patient_Account as a subquery. Window functions are quite convenient.
Your date comparisons preclude the use of indexes. Converting dates to strings is a bad practice in general.
You have over-used isnull().
I assume that the appropriate indexes are in place for the joins.
I would use STRING_SPLIT and Common Table Expressions and do away with date conversions:
Declare #IDs nvarchar(max)='1,4,5,6,8,9,43,183'
;WITH CTE_ID AS
(
SELECT value AS ID FROM STRING_SPLIT(#IDs, ',');)
),
MaxPatient
AS
(
SELECT MAX(P.Id) [Id], P.TranMainId, isnull(SUM(P.AmtToPay),0) [Amt]
From Patient_Account P
Group By P.TranMainId
)
SELECT isnull(isnull(SUM(FT.PaidAmt),0) - isnull(SUM(CT.PaidAmt),0),0) As [Amount],
convert(char(10),FT.TranDate,126) [Date]
FROM FeeTransaction FT
INNER JOIN MaxPatient PA
ON FT.Id = PA.TranMainId
INNER JOIN Patient_Account XP
ON PA.Id = XP.Id
INNER JOIN Master_Fee MF
ON XP.FeeId = MF.Id
INNER Join Master_Patient MP
ON FT.PID = MP.Id
INNER JOIN Master_FeeType TY
ON MF.FeeTypeId = TY.Id
INNER JOIN CTE_ID
ON XP.FeeId = CTE_ID.ID
LEFT JOIN FeeTransaction CT
ON FT.TransactionId = CT.TransactionId AND
CT.TranDate >= '20190801' AND CT.TranDate < '20190831' AND
CT.[Status] <> 'A' AND isnull(CT.IsCancel,0) = 1
WHERE FT.TranDate >= '20190801' and FT.TranDate < '20190831' AND
FT.[Status] = 'A' AND
ISNULL(FT.IsCancel,0) = 0 AND
FT.EntryBy = 'rajan'
GROUP BY CAST(FT.TranDate AS Date)
Not only is your query slow, but it appear that it is giving incorrect output.
i) When you are not using any column of Patient_Account in your resultset then why are you writing this sub query?
Select max(P.Id) [Id], P.TranMainId, isnull(SUM(P.AmtToPay),0) [Amt]
From Patient_Account P
Group By P.TranMainId
ii) Avoid using <>.So Status must be either 'A' or 'I'
so write this instead CT.[Status] = 'I'
iii) What is the correct data type of TranDate ?Don't use function in where condition. .
iv) No need of isnull(CT.IsCancel,0) = 1,instead write CT.IsCancel = 1
So my script is just outline, but it is easy to understand.
Declare #IDs nvarchar(max)='1,4,5,6,8,9,43,183'
create table #temp(id int)
insert into #temp(id)
SELECT val FROM dbo.f_split(#IDs, ',')
declare #FromDate Datetime='2019-08-01'
declare #toDate Datetime='2019-08-31'
-- mention all column of FeeTransaction that you need in this query along with correct data type
-- Store TranDate in this manner convert(char(10),FT.TranDate,126) in this table
create table #Transaction()
select * from FeeTransaction FT
where FT.TranDate>=#FromDate and FT.TranDate<#toDate
and exists(select 1 from #temp t where t .val=ft.id)
-- mention all column of Patient_Account that you need in this query along with correct data type
create table #Patient_Account()
Select max(P.Id) [Id], P.TranMainId, isnull(SUM(P.AmtToPay),0) [Amt]
From Patient_Account P
where exists(select 1 from #Transaction T where t.id=PA.TranMainId)
Group By P.TranMainId
SELECT isnull(isnull(SUM(FT.PaidAmt),0) - isnull(SUM(CT.PaidAmt),0),0) [Amount], TranDate [Date]
from #Transaction FT
Inner Join #Patient_Account XP ON PA.Id = XP.Id
Inner Join Master_Fee MF ON XP.FeeId = MF.Id
INNER Join Master_Patient MP ON FT.PID = MP.Id
Inner Join Master_FeeType TY ON MF.FeeTypeId = TY.Id
Left JOIN #Transaction CT on FT.TransactionId = CT.TransactionId AND CT.[Status] = 'I' AND CT.IsCancel = 1
Where AND FT.[Status] = 'A' AND XP.FeeId in (SELECT val FROM #temp t)
AND FT.IsCancel = 0 AND FT.EntryBy = 'rajan'
Group By TranDate

There is already an object named '#FutureDatedExclude' in the database

SELECT FutureDatedEmployeeRecordsKey INTO #FutureDatedExclude
FROM dbo.vwRptDimEmployee_FutureDated FD1
WHERE EXISTS (SELECT 1 FROM dbo.vwRptDimEmployeeAll EE1 WITH (NOLOCK)
WHERE FD1.EmployeeID = EE1.EmployeeID AND FD1.EmployeeRecord = EE1.EmployeeRecord
AND FD1.JobEffectiveDate = EE1.JobEffectiveDT AND FD1.JobEffectiveDateSequence = EE1.JobEffectiveDateSequence
AND FD1.ActionCode = EE1.ActionCode AND FD1.ActionReasonCode = EE1.ActionReasonCode)
declare #JobStartDate date = '07/01/2014', #JobEndDate date = '06/30/2015'
SELECT DISTINCT
E.LastName,
E.SecondLastName,
E.FirstName,
E.MiddleName,
E.PreferredName,
E.PreferredFirstName,
E.NameAC,
E.LastNameAC,
E.FirstNameAC,
E.MiddleNameAC,
E.GUI,
E.EmployeeID,
E.LPN,
E.GPN,
E.EmployeeRecord,
E.JobEffectiveDT JobEffectiveDate,
E.JobEffectiveDateSequence,
E.ActionCode,
E.Action,
E.ActionDate,
E.ActionReasonCode,
AR.Description ActionReason,
E.EmployeeStatusCode,
E.EmployeeStatusDesc,
CASE WHEN YEAR(E.LeaveEffectiveDT) > 2100 THEN NULL ELSE E.LeaveEffectiveDT END LeaveEffectiveDate,
CASE WHEN YEAR(E.ExpectedReturnDate) > 2100 THEN NULL ELSE E.ExpectedReturnDate END ExpectedReturnDate,
E.FullPartTime,
E.ShiftCode FWACode,
E.Shift FWAName,
E.TeleWork,
E.StandardHoursFrequency,
E.StandardHours,
E.FTE,
E.PaidFTE,
E.OvertimeEligibility,
E.EmployeeClassCode,
E.EmployeeClass,
E.RegularVersusTemporary RegularTemporary,
E.EmployeeType,
E.PersonnelStatusDesc,
E.PersonOrganizationRelationshipCode,
P.PersonOfInterest,
P.PersonOfInterestDesc,
E.PaygroupCode,
E.EmployeeCategoryCode,
E.EmployeeSubcategoryCode,
P.EmploymentCategory,
E.NonEmployeeNonWorkTypeCD NonEmployeeNonWorkTypeCode,
P.NonEmployeeNonWorkTypeDesc,
A.GlobalAssignmentProgramCD GlobalAssignmentProgramCode,
A.GlobalAssignmentProgramDesc,
CASE WHEN YEAR(E.GlobalAssignmentStartDT) > 2100 THEN NULL ELSE E.GlobalAssignmentStartDT END GlobalAssignmentStartDate,
CASE WHEN YEAR(E.GlobalAssignmentEndDT) > 2100 THEN NULL ELSE E.GlobalAssignmentEndDT END GlobalAssignmentEndDate,
E.InPatExPatStatus,
E.HomeCountry,
E.HomeHostCountry HostCountry,
CASE WHEN YEAR(E.EYStartDate) > 2100 THEN NULL ELSE E.EYStartDate END EYStartDate,
CASE WHEN YEAR(E.LastRehireDate) > 2100 THEN NULL ELSE E.LastRehireDate END LastRehireDate,
CASE WHEN YEAR(E.SeniorityDate) > 2100 THEN NULL ELSE E.SeniorityDate END SeniorityDate,
CASE WHEN YEAR(E.EmployeeEffectiveDate) > 2100 THEN NULL ELSE E.EmployeeEffectiveDate END CurrentEmploymentDate,
CASE WHEN YEAR(E.PartnerAdmissionDate) > 2100 THEN NULL ELSE E.PartnerAdmissionDate END PartnerAdmissionDate,
R.RankCDName RankCodeName,
E.Rank,
R.RankDesc,
E.BusinessTitle,--NEW
R.RankGroup1,--NEW
E.GFISRank,
E.ExperienceLevel,
E.GlobalGrade,
E.JobCode,--NEW
E.JobCDDesc JobCodeDesc,--NEW
E.DepartmentCode,
E.DepartmentName,
E.CompanyCode,
C.Description Company,
C.DescrAc CompanyAC,
E.ManagerialCountryCD ManagerialCountry,
O.CodeBlock,
O.BUCD BU,
O.OUCD OU,
O.MUCD MU,
O.SMUCD SMU,
O.BUName,
O.OUName,
O.MUName,
O.SMUName,
O.UserDefSLHierarchy1 ServiceLine,
O.UserDefSLHierarchy2 SubSL1,
O.UserDefSLHierarchy3 SubSL2,
O.AlternateServiceLine,
O.UserDefAreaHierarchy1 BULevel1,
O.UserDefAreaHierarchy2 BULevel2,
O.UserDefAreaHierarchy3 BULevel3,
L.Location LocationCode,
L.City LocationCity,
L.State LocationStateProv,
L.Country LocationCountry,
L.UserDefinedHRGeo1 GeoLevel1,
L.UserDefinedHRGeo2 GeoLevel2,
L.UserDefinedHRGeo3 GeoLevel3,
L.UserDefinedHRGeo4 GeoLevel4,
L.UserDefinedHRGeo5 GeoLevel5,
E.CounselorGUI,--NEW
E.CounselorName,--NEW
E.BillRate,
E.Source,
--**** confidential fields ****
E.GenderCode,
E.TermCD TermCode,
E.TerminationReasonCode,
E.CompensationCurrency,
E.CompensationRate,
E.CompensationFrequency,
E.MonthlyCompensationRate,
E.AnnualCompensationRate,
CASE WHEN YEAR(P.SalaryEffectiveDT) > 2100 THEN NULL ELSE P.SalaryEffectiveDT END SalaryEffectiveDate,
E.SalaryAdminPlanCode,
E.SalaryAdminPlan,
E.SalaryGrade,
CASE WHEN YEAR(E.SalaryGradeEntryDate) > 2100 THEN NULL ELSE E.SalaryGradeEntryDate END SalaryGradeEntryDate,
NULL JobKEY,
NULL RowOrder
FROM dbo.vwRptFactEmployee F WITH (NOLOCK)
INNER JOIN dbo.vwRptDimEmployee E WITH (NOLOCK) ON (F.DimEmployeeKey = E.DimEmployeeKey)
INNER JOIN dbo.vwRptDimRank R WITH (NOLOCK) ON (F.DimRankKey = R.DimRankKey)
INNER JOIN dbo.vwRptDimOrganization O WITH (NOLOCK) ON (F.DimOrganizationKey = O.DimOrganizationKey)
INNER JOIN dbo.vwRptDimLocation L WITH (NOLOCK) ON (F.DimLocationKey = L.DimLocationKey)
INNER JOIN dbo.vwRptDimAssignment A WITH (NOLOCK) ON (F.DimAssignmentKey = A.DimAssignmentKey)
--INNER JOIN dbo.vwRptDimDate D WITH (NOLOCK) ON (F.TransEffectiveDateKey = D.DimDateKey)
INNER JOIN dbo.vwRptDimEmployeeV2 P WITH (NOLOCK) ON (F.DimEmployeeKey = P.DimEmployeeKey)
LEFT OUTER JOIN (SELECT ActionCode, ActionReasonCode, Description, row_number() over (partition by ActionCode, ActionReasonCode order by EffectiveDate DESC) as RowOrder
FROM PISupport.vwRptSetfActionReason WITH (NOLOCK)) AR
ON (AR.ActionCode = E.ActionCode AND AR.ActionReasonCode = E.ActionReasonCode AND AR.RowOrder = 1)
LEFT OUTER JOIN (SELECT DISTINCT C1.*, ROW_NUMBER() OVER (PARTITION BY CompanyCode ORDER BY EffectiveDate DESC) as RowOrder
FROM PISupport.vwRptSetfCompany C1 WITH (NOLOCK)) C
ON (C.CompanyCode = E.CompanyCode AND C.RowOrder = 1)
WHERE (E.JobEffectiveDT BETWEEN #JobStartDate AND #JobEndDate)
-- AND (E.ActionCode in ('ADD','DTA','HIR','POI','REH','PER','TER'))
--AND (O.BUCD+O.OUCD+O.MUCD+O.SMUCD LIKE '%'+#CodeBlock+'%' OR #CodeBlock IS NULL)
--AND (E.GPN = #GPN OR #GPN IS NULL)
--AND (E.GUI = #GUI OR #GUI IS NULL)
--AND (L.UserDefinedHRGeo1 in (#GeoArea) )
AND (L.UserDefinedHRGeo2 in ('UK and Ireland'))
--AND (L.UserDefinedHRGeo3 in (#Country) )
--AND (O.UserDefAreaHierarchy1 in (#Area) )
--AND (O.UserDefAreaHierarchy2 in (#Region) )
--AND (O.UserDefSLHierarchy1 in (#ServiceLine) )
--AND (O.UserDefSLHierarchy2 in (#SubServiceLine) )
--AND (R.RankCD in (#RankCode) )
UNION
SELECT DISTINCT * FROM (
SELECT DISTINCT
ISNULL(E.LastName,N.LastName) LastName,
ISNULL(E.SecondLastName,N.SecondLastName) SecondLastName,
ISNULL(E.FirstName,N.FirstName) FirstName,
ISNULL(E.MiddleName,N.MiddleName) MiddleName,
E.PreferredName,
ISNULL(E.PreferredFirstName,N.PreferredFirstName) PreferredFirstName,
ISNULL(E.NameAC,N.NameAlternateCharacter) NameAC,
E.LastNameAC,
E.FirstNameAC,
E.MiddleNameAC,
E.GUI,
FD.EmployeeID,
FD.LPN,
FD.GPN,
FD.EmployeeRecord,
FD.JobEffectiveDate,
FD.JobEffectiveDateSequence,
FD.ActionCode,
FD.Action,
FD.ActionDate,
FD.ActionReasonCode,
FD.ActionReason,
FD.EmployeeStatusCode,
FD.EmployeeStatus,
NULL LeaveEffectiveDate,
CASE WHEN YEAR(FD.ExpectedReturnDate) > 2100 THEN NULL ELSE FD.ExpectedReturnDate END ExpectedReturnDate,
FD.FullPartTime,
FD.ShiftCode FWACode,
FD.Shift FWAName,
NULL Telework,
FD.StandardHoursFrequency,
NULL StandardHours,
FD.FTE,
FD.PaidFTE,
NULL OvertimeEligibility,
FD.EmployeeClassCode,
FD.EmployeeClass,
FD.RegularVersusTemporary RegularTemporary,
FD.EmployeeType,
NULL PersonnelStatusDesc,
FD.PersonOrganizationRelationshipCode,
NULL PersonOfInterest,
NULL PersonOfInterestDesc,
FD.PaygroupCode,
FD.EmployeeCategoryCode,
FD.EmployeeSubcategoryCode,
NULL EmploymentCategory,
NULL NonEmployeeNonWorkTypeCode,
NULL NonEmployeeNonWorkTypeDesc,
NULL GlobalAssignmentProgramCode,
NULL GlobalAssignmentProgramDesc,
NULL GlobalAssignmentStartDate,
NULL GlobalAssignmentEndDate,
NULL InPatExPatStatus,
NULL HomeCountry,
NULL HostCountry,
NULL EYStartDate,
NULL LastRehireDate,
NULL SeniorityDate,
NULL CurrentEmploymentDate,
NULL PartnerAdmissionDate,
R.RankCDName RankCodeName,
FD.Rank,
R.RankDesc,
FD.BusinessTitle,--NEW
R.RankGroup1,--NEW
FD.GFISRank,
NULL ExperienceLevel,
NULL GlobalGrade,
FD.JobCode,--NEW
NULL JobCodeDesc,--NEW
FD.DepartmentCode,
NULL DepartmentName,
FD.CompanyCode,
C.Description Company,
C.DescrAc CompanyAC,
NULL ManagerialCountry,
O.CodeBlock,
O.BUCD BU,
O.OUCD OU,
O.MUCD MU,
O.SMUCD SMU,
O.BUName,
O.OUName,
O.MUName,
O.SMUName,
O.UserDefSLHierarchy1 ServiceLine,
O.UserDefSLHierarchy2 SubSL1,
O.UserDefSLHierarchy3 SubSL2,
O.AlternateServiceLine,
O.UserDefAreaHierarchy1 BULevel1,
O.UserDefAreaHierarchy2 BULevel2,
O.UserDefAreaHierarchy3 BULevel3,
L.Location LocationCode,
L.City LocationCity,
L.State LocationStateProv,
L.Country LocationCountry,
L.UserDefinedHRGeo1 GeoLevel1,
L.UserDefinedHRGeo2 GeoLevel2,
L.UserDefinedHRGeo3 GeoLevel3,
L.UserDefinedHRGeo4 GeoLevel4,
L.UserDefinedHRGeo5 GeoLevel5,
NULL CounselorGUI,--NEW
NULL CounselorName,--NEW
NULL BillRate,
FD.Source,
--**** confidential fields ****
NULL GenderCode,
NULL TermCode,
FD.TerminationReasonCode,
FD.CompensationCurrency,
FD.CompensationRate,
FD.CompensationFrequency,
FD.MonthlyCompensationRate,
FD.AnnualCompensationRate,
NULL SalaryEffectiveDate,
FD.SalaryAdminPlanCode,
FD.SalaryAdminPlan,
FD.SalaryGrade,
CASE WHEN YEAR(FD.SalaryGradeEntryDate) > 2100 THEN NULL ELSE FD.SalaryGradeEntryDate END SalaryGradeEntryDate,
FD.Job_KEY,
row_number() over (partition by FD.EmployeeID, FD.EmployeeRecord, FD.JobEffectiveDate,
FD.JobEffectiveDateSequence, FD.ActionCode, FD.ActionReasonCode order by FD.Job_KEY DESC) as RowOrder
FROM dbo.vwRptDimEmployee_FutureDated FD
INNER JOIN dbo.vwRptDimOrganization O WITH (NOLOCK) ON (FD.DimOrganizationKey = O.DimOrganizationKey)
INNER JOIN dbo.vwRptDimLocation L WITH (NOLOCK) ON (FD.DimLocationKey = L.DimLocationKey)
LEFT OUTER JOIN dbo.vwRptDimRank R WITH (NOLOCK) ON (FD.Rank = R.RankCD)
LEFT OUTER JOIN dbo.vwRptDimEmployeeAll E WITH (NOLOCK) ON (FD.GPN = E.GPN AND FD.GPN <> '' AND E.RowIsCurrent = 'Y')
LEFT OUTER JOIN (SELECT *, ROW_NUMBER() OVER (PARTITION BY EmployeeID, NameType ORDER BY EffectiveDate DESC) AS RowOrder
FROM PISupport.vwRptPersonACNames WITH (NOLOCK)
) N ON (N.EmployeeID = FD.EmployeeID AND N.NameType = 'PRI' AND N.CountryNameFormat = FD.SetIDLaborAgreement AND N.RowOrder = 1)
LEFT OUTER JOIN (SELECT DISTINCT C1.*, ROW_NUMBER() OVER (PARTITION BY CompanyCode ORDER BY EffectiveDate DESC) as RowOrder
FROM PISupport.vwRptSetfCompany C1 WITH (NOLOCK)) C
ON (C.CompanyCode = FD.CompanyCode AND C.RowOrder = 1)
WHERE
FD.JobEffectiveDate BETWEEN #JobStartDate AND #JobEndDate
AND FD.EDWIsCurrentRecord = 1
AND FD.EmployeeID IS NOT NULL
--AND (E.ActionCode in ('ADD','DTA','HIR','POI','REH','PER','TER'))
--AND (O.BUCD+O.OUCD+O.MUCD+O.SMUCD LIKE '%'+#CodeBlock+'%' OR #CodeBlock IS NULL)
--AND (FD.GPN = #GPN OR #GPN IS NULL)
--AND (L.UserDefinedHRGeo1 in (#GeoArea) )
AND (L.UserDefinedHRGeo2 in ('UK and Ireland'))
--AND (L.UserDefinedHRGeo3 in (#Country) )
--AND (O.UserDefAreaHierarchy1 in (#Area) )
--AND (O.UserDefAreaHierarchy2 in (#Region) )
--AND (O.UserDefSLHierarchy1 in (#ServiceLine) )
--AND (O.UserDefSLHierarchy2 in (#SubServiceLine) )
--AND (FD.Rank in (#RankCode) )
AND FD.FutureDatedEmployeeRecordsKey NOT IN (SELECT FutureDatedEmployeeRecordsKey FROM #FutureDatedExclude)
) X
WHERE RowOrder = 1
DROP TABLE #FutureDatedExclude
You most likely ran this code before you added the DROP TABLE at the bottom of your code. Therefore, the table is created and yet to be dropped. Instead of dropping it at the end, or in addition to dropping it if you want, place this at the very top of your script:
IF OBJECT_ID('tempdb..#FutureDatedExclude') IS NOT NULL DROP TABLE #FutureDatedExclude

Getting unique records for each group using sql

I have an sql Log table with two columns like this:
RegistrantID compnayID Creation date
1 1 .....
1 1
2 1
3 1
1 2
2 2
2 2
3 2 .....
I have a stored procedure which brings first record based creation date. It brings one record if two companies has same registrant id so for registrantid 1, It will being first row and not the 5th row, and for registrantId 2, it will bring row 3 but not 6th. I want to get row 1 and 5 for registrantid 1 and 3rd and 6th for registrantid 2, row 4 and 8 for registrant id 3. Here is my stored procedure.
ALTER PROCEDURE [dbo].[hr_ActionLog_GetList]
#Action INT = NULL,
#DateFrom DATETIME = NULL,
#DateTo DATETIME = NULL,
#CompanyID INT = NULL,
#RegistrantID INT = NULL,
#VacancyID INT = NULL,
#Language INT = 1
AS
BEGIN
WITH CTE AS
(
SELECT AL.*,
RV.Forename,
RV.Surname,
RV.Username AS RegistrantUsername,
E.Forename AS EmployeeForename,
E.Surname AS EmployeeSurname,
U.Username,
CASE
WHEN #Language = 2 THEN C.NameLang2
ELSE C.NameLang1
END AS CompanyName,
CASE
WHEN #Language = 2 THEN V.JobTitleLang2
ELSE V.JobTitleLang1
END AS JobTitle
, ROW_NUMBER() OVER(PARTITION BY AL.RegistrantID
ORDER BY ActionDate ASC) AS RN
FROM dbo.hr_ActionLog AL LEFT OUTER JOIN dbo.RegistrantsListView RV ON AL.RegistrantID = RV.RegistrantID
LEFT OUTER JOIN dbo.hr_Employees E ON AL.EmployeeID = E.EmployeeID
LEFT OUTER JOIN dbo.hr_Users U ON AL.UserID = U.UserID
LEFT OUTER JOIN dbo.hr_Companies C ON AL.CompanyID = C.CompanyID
LEFT OUTER JOIN dbo.hr_Vacancies V ON AL.VacancyID = V.VacancyID
LEFT OUTER JOIN dbo.hr_Companies VC ON V.CompanyID = VC.CompanyID
WHERE (#Action IS NULL OR AL.Action = #Action)
AND (#DateFrom IS NULL OR dbo.DateOnly(AL.ActionDate) >= dbo.DateOnly(#DateFrom))
AND (#DateTo IS NULL OR dbo.DateOnly(AL.ActionDate) <= dbo.DateOnly(#DateTo))
AND (#CompanyID IS NULL OR AL.CompanyID = #CompanyID)
AND (#RegistrantID IS NULL OR AL.RegistrantID = #RegistrantID)
AND (#VacancyID IS NULL OR AL.VacancyID = #VacancyID)
--ORDER BY AL.ActionDate DESC
)
SELECT *
FROM CTE
WHERE RN = 1;
END
Please suggest how to change this stored procedure ?
You need to partition by both RegistrantId and CompanyID
ROW_NUMBER() OVER(PARTITION BY AL.RegistrantID, AL.CompanyID
ORDER BY ActionDate ASC)
Removing where clause RN=1 and adding a distinct should be enough.

Ordering the results of CTE in SQL

I have following SQL:
;WITH CTE
AS (SELECT AL.*,
RV.FORENAME,
RV.SURNAME,
RV.USERNAME AS RegistrantUsername,
E.FORENAME AS EmployeeForename,
E.SURNAME AS EmployeeSurname,
U.USERNAME,
CASE
WHEN #Language = 2 THEN C.NAMELANG2
ELSE C.NAMELANG1
END AS CompanyName,
CASE
WHEN VC.COMPANYID IS NULL THEN
CASE
WHEN #Language = 2 THEN V.COMPANYNAMELANG2
ELSE V.COMPANYNAMELANG1
END
ELSE
CASE
WHEN #Language = 2 THEN VC.NAMELANG2
ELSE VC.NAMELANG1
END
END AS VacancyCompanyName,
CASE
WHEN #Language = 2 THEN V.JOBTITLELANG2
ELSE V.JOBTITLELANG1
END AS JobTitle,
ROW_NUMBER()
OVER(
PARTITION BY AL.REGISTRANTID, AL.COMPANYID
ORDER BY ACTIONDATE ASC) AS RN
FROM DBO.HR_ACTIONLOG AL
LEFT OUTER JOIN DBO.REGISTRANTSLISTVIEW RV
ON AL.REGISTRANTID = RV.REGISTRANTID
LEFT OUTER JOIN DBO.HR_EMPLOYEES E
ON AL.EMPLOYEEID = E.EMPLOYEEID
LEFT OUTER JOIN DBO.HR_USERS U
ON AL.USERID = U.USERID
LEFT OUTER JOIN DBO.HR_COMPANIES C
ON AL.COMPANYID = C.COMPANYID
LEFT OUTER JOIN DBO.HR_VACANCIES V
ON AL.VACANCYID = V.VACANCYID
LEFT OUTER JOIN DBO.HR_COMPANIES VC
ON V.COMPANYID = VC.COMPANYID
WHERE ( #Action IS NULL
OR AL.ACTION = #Action )
AND ( #DateFrom IS NULL
OR DBO.DATEONLY(AL.ACTIONDATE) >=
DBO.DATEONLY(#DateFrom) )
AND ( #DateTo IS NULL
OR DBO.DATEONLY(AL.ACTIONDATE) <= DBO.DATEONLY(#DateTo) )
AND ( #CompanyID IS NULL
OR AL.COMPANYID = #CompanyID )
AND ( #RegistrantID IS NULL
OR AL.REGISTRANTID = #RegistrantID )
AND ( #VacancyID IS NULL
OR AL.VACANCYID = #VacancyID )
--ORDER BY AL.ActionDate DESC
)
SELECT *
FROM CTE
WHERE RN = 1;
It returns first element from the group based on actiondate which is fine but the returned result is not ordered by date means returns each groups first record but the this collection of first records is not ordered by action date. I tried ORDER BY AL.ActionDate DESC in CTE but it gives error:
The ORDER BY clause is invalid in views, inline functions, derived tables, subqueries, and common table expressions, unless TOP or FOR XML is also specified.
Try this one -
;WITH CTE AS
(
SELECT ...
FROM dbo.hr_ActionLog AL
LEFT JOIN ...
WHERE AL.[Action] = ISNULL(#Action, AL.[Action])
AND dbo.DateOnly(AL.ActionDate) BETWEEN
dbo.DateOnly(ISNULL(#DateFrom, AL.ActionDate))
AND
dbo.DateOnly(ISNULL(#DateTo, '30000101'))
AND AL.CompanyID = ISNULL(#CompanyID, AL.CompanyID)
AND AL.RegistrantID = ISNULL(#RegistrantID, AL.RegistrantID)
AND AL.VacancyID = ISNULL(#VacancyID, AL.VacancyID)
)
SELECT *
FROM CTE
WHERE RN = 1
ORDER BY AL.ActionDate DESC;
Move order by clause outside of cte:
WITH CTE AS
(
...
)
SELECT *
FROM CTE
WHERE RN = 1;
ORDER BY ActionDate DESC
you do the ordering outside CTE
WITH CTE
AS
{
....
}
SELECT *
FROM CTE
WHERE RN = 1
ORDER BY ActionDate DESC
Yes, the only ORDER BY that affects the order in which the results are returned is one placed on the outermost SELECT:
SELECT *
FROM CTE
WHERE RN = 1
ORDER BY ActionDate DESC
You have a ROW_NUMBER function inside the CTE and the query only takes the row with RN=1.
If I understand the problem correctly, just change the ORDER BY clause in the ROW_NUMBER function and you should get the results you're looking for.