Returning rows selected in with statement - sql

I have a sql statement looking something like this
WITH [ResultPage] AS (
SELECT sub.id as id, ROW_NUMBER() OVER (ORDER BY [TAG].[Name] ASC, [TAG].[ID] ASC) As [RowID], [sub].[hasPending]
FROM
(SELECT t.[ID], p.[TagID],(CASE WHEN p.[TagID] is not null THEN 1 ELSE 0 END) AS [hasPending]
FROM [Tag] AS t
full outer Join [TagPending] AS p On t.[ID] = p.[TagID]
GROUP BY t.[ID],p.[TagID]
) AS [sub]
inner Join [Tag] On [Tag].[ID] = sub.[ID]
)
SELECT [ResultPage].[RowID], [Tag].[ID] As [Tag^ID], [Tag].[Name] As [Tag^Name]
FROM [ResultPage]
INNER JOIN [Tag] ON ([Tag].[ID] = [ResultPage].[TagID])
INNER JOIN [TagPending] ON ([TagPending].[TagID] = [KbResultPage].[TagID])
WHERE (([ResultPage].[RowID] BETWEEN 1 AND 50)
)
ORDER BY [ResultPage].[RowID] ASC
As can be seen, the select max returns 50 rows at a time, however I need to know the total number of rows found in the WITH statement. I could run it again with just a count, however it could be nice if there were a way to get both in one go.

You can use the COUNT(*) OVER() in the WITH statement
WITH [ResultPage] AS (
SELECT COUNT(*) OVER() AS totalRows,
sub.id as id, ROW_NUMBER() OVER (ORDER BY [TAG].[Name] ASC, [TAG].[ID] ASC) As [RowID], [sub].[hasPending]
FROM
(SELECT t.[ID], p.[TagID],(CASE WHEN p.[TagID] is not null THEN 1 ELSE 0 END) AS [hasPending]
FROM [Tag] AS t
full outer Join [TagPending] AS p On t.[ID] = p.[TagID]
GROUP BY t.[ID],p.[TagID]
) AS [sub]
inner Join [Tag] On [Tag].[ID] = sub.[ID]
)
SELECT
[ResultPage].totalRows,
[ResultPage].[RowID], [Tag].[ID] As [Tag^ID], [Tag].[Name] As [Tag^Name]
FROM [ResultPage]
INNER JOIN [Tag] ON ([Tag].[ID] = [ResultPage].[TagID])
INNER JOIN [TagPending] ON ([TagPending].[TagID] = [KbResultPage].[TagID])
WHERE ([ResultPage].[RowID] BETWEEN 1 AND 50)
ORDER BY [ResultPage].[RowID] ASC

Related

Use CTE in SQL to flag DUPLICATES and reference in sub-query

So I have the following CTE:
with dupeinv AS (
select * from (
select
tci.t_idoc,
tci.t_idat,
ROW_NUMBER() OVER (partition by tci.t_idoc ORDER BY tci.t_idoc, tci.t_idat DESC) as rn
from [ln106].[dbo].tcisli305100 tci
) as t
where t.rn = 1
)
There are duplicates in the above table ([ln106].[dbo].tcisli305100) , hence the CTE to get single values. I want to format just these values in the below query (prefixed with ---)
select 'JCI' as BU,
RTRIM(LTRIM(cl.t_orno)) AS SALES_ORDER_NUMBER
, cl.t_pono AS SALES_ORDER_LINE_NUMBER
, CONCAT(cl.t_shpm, cl.t_pono, cl.t_idoc) AS SHIPPING_RECORD_ID
,CASE WHEN cl.t_dqua = 0 or cl.t_dqua is null THEN cl.t_amti ELSE
cl.t_amti / cl.t_dqua END AS AR_INVOICE_LINE_ITEM_PRICE_LOCAL
, cl.t_line AS AR_INVOICE_LINE_NUMBER
, cl.t_dqua AS AR_INVOICE_LINE_ITEM_QUANTITY
--- , concat(dupeinv.t_idoc,'|',format(dupeinv.t_idat,'MMddyyyy') ---
,ci.t_ccur AS AR_INVOICE_CURRENCY
, ci.t_idat AS AR_INVOICE_DATE
FROM [ln106].[dbo].tcisli310100 cl
LEFT JOIN [ln106].[dbo].tcisli305100 ci ON cl.t_idoc = ci.t_idoc
LEFT JOIN t di on cl.t_doc = di_t_doc
LEFT JOIN (SELECT t_orno,t_pono FROM [ln106].[dbo].ttdsls401100 WHERE t_oltp <> 1 group by t_orno,t_pono) as l --Jed 10162020 Changed the join to prevent duplicate records
ON l.t_orno=cl.t_orno COLLATE SQL_Latin1_General_CP1_CI_AS AND l.t_pono=cl.t_pono
LEFT JOIN dupeinv tci on cl.r_idoc = ci.t_doc
WHERE ci.t_idat > '2017'
Query doesn't like me referencing it in the main query. Can anyone help, or suggest a better idea?
Your final query should look something like this:
WITH dupeinv AS
(SELECT *
FROM
(SELECT tci.t_idoc,
tci.t_idat,
ROW_NUMBER() OVER (PARTITION BY tci.t_idoc
ORDER BY tci.t_idoc,
tci.t_idat DESC) AS rn
FROM [ln106].[dbo].tcisli305100 tci) AS t
WHERE t.rn = 1 )
SELECT 'JCI' AS BU,
RTRIM(LTRIM(cl.t_orno)) AS SALES_ORDER_NUMBER ,
cl.t_pono AS SALES_ORDER_LINE_NUMBER ,
CONCAT(cl.t_shpm, cl.t_pono, cl.t_idoc) AS SHIPPING_RECORD_ID ,
CASE
WHEN cl.t_dqua = 0
OR cl.t_dqua IS NULL THEN cl.t_amti
ELSE cl.t_amti / cl.t_dqua
END AS AR_INVOICE_LINE_ITEM_PRICE_LOCAL ,
cl.t_line AS AR_INVOICE_LINE_NUMBER ,
cl.t_dqua AS AR_INVOICE_LINE_ITEM_QUANTITY ,
concat(dupeinv.t_idoc,
'|',
format(dupeinv.t_idat, 'MMddyyyy')) ,
ci.t_ccur AS AR_INVOICE_CURRENCY ,
ci.t_idat AS AR_INVOICE_DATE
FROM [ln106].[dbo].tcisli310100 cl
LEFT JOIN [ln106].[dbo].tcisli305100 ci ON cl.t_idoc = ci.t_idoc
LEFT JOIN t di ON cl.t_doc = di_t_doc
LEFT JOIN
(SELECT t_orno,
t_pono
FROM [ln106].[dbo].ttdsls401100
WHERE t_oltp <> 1
GROUP BY t_orno,
t_pono) AS l --Jed 10162020 Changed the join to prevent duplicate records
ON l.t_orno=cl.t_orno COLLATE SQL_Latin1_General_CP1_CI_AS
AND l.t_pono=cl.t_pono
LEFT JOIN dupeinv tci ON cl.r_idoc = ci.t_doc
WHERE ci.t_idat > '2017'

How to get different IBAN per type for an employee?

I have the following query which gets me the different IBAN for the EmployeeId=54 :
WITH cte AS ( SELECT TOP 1000 [E].[EmployeeId]
,(CASE WHEN BIT.Label='Salary' THEN [BI].[IBAN] END) AS IBAN_Salary
,(CASE WHEN BIT.Label='Expenses Claims' THEN [BI].[IBAN] END) AS IBAN_Expenses_Claims
,bi.UpdatedDate
, ROW_NUMBER() OVER (PARTITION BY E.EmployeeId,bit.Label ORDER BY BI.UpdatedDate DESC) rn
FROM [Employee] AS [E]
LEFT JOIN [AF_AdminFile] AS [AAF] ON [AAF].[AdminFileId] = [E].[AdminFileId]
LEFT JOIN [AF_BankInformation] AS [AFB] ON [AFB].[AdminFileId] = [AAF].[AdminFileId]
LEFT JOIN [BankInformation] AS [BI] ON [BI].[BankInformationId] = [AFB].[BankInformationId]
LEFT JOIN [BankInformationType] AS [BIT] ON [BIT].[BankInformationTypeId] = [AFB].[BankInformationTypeId]
LEFT JOIN [BankInformationSystem] AS [BIS] ON [BIS].[BankInformationSystemId]=[BI].[BankInformationSystemId]
LEFT JOIN [Currency] AS [Cur] ON [Cur].[ID]=[BI].[CurrencyId]
WHERE BIT.BankInformationTypeId IN (1,2)
AND e.EmployeeId=54
GROUP BY e.EmployeeId, BIT.Label,BI.UpdatedDate,BI.IBAN
ORDER BY e.EmployeeId
)
SELECT * FROM cte WHERE rn =1
The output of the previous query :
How to get seperated column for the IBAN_Salary, IBAN_Expenses_Claims,UpdatedDated_IBAN_Salary,UpdatedDated_IBAN_Expenses_Claims in the same line like below ? :
EmployeeId IBAN_Salary IBAN_Expenses_Claims UpdatedDated_IBAN_Salary UpdatedDated_IBAN_Expenses_Claims
54 lorem ipsum lorem ipsum 2019-08-09 09:38:00.7350889 2018-07-07 17:09:16.7598906
Use the same CASE for dates. Aggregate by the key
WITH cte AS ( SELECT [E].[EmployeeId]
,(CASE WHEN BIT.Label='Salary' THEN [BI].[IBAN] END) AS IBAN_Salary
,(CASE WHEN BIT.Label='Expenses Claims' THEN [BI].[IBAN] END) AS IBAN_Expenses_Claims
,(CASE WHEN BIT.Label='Salary' THEN bi.UpdatedDate END) AS UpdatedDated_IBAN_Salary
,(CASE WHEN BIT.Label='Expenses Claims' THEN bi.UpdatedDate END) AS UpdatedDated_IBAN_Expenses_Claims
, ROW_NUMBER() OVER (PARTITION BY E.EmployeeId,bit.Label ORDER BY BI.UpdatedDate DESC) rn
FROM [Employee] AS [E]
LEFT JOIN [AF_AdminFile] AS [AAF] ON [AAF].[AdminFileId] = [E].[AdminFileId]
LEFT JOIN [AF_BankInformation] AS [AFB] ON [AFB].[AdminFileId] = [AAF].[AdminFileId]
LEFT JOIN [BankInformation] AS [BI] ON [BI].[BankInformationId] = [AFB].[BankInformationId]
LEFT JOIN [BankInformationType] AS [BIT] ON [BIT].[BankInformationTypeId] = [AFB].[BankInformationTypeId]
LEFT JOIN [BankInformationSystem] AS [BIS] ON [BIS].[BankInformationSystemId]=[BI].[BankInformationSystemId]
LEFT JOIN [Currency] AS [Cur] ON [Cur].[ID]=[BI].[CurrencyId]
WHERE BIT.BankInformationTypeId IN (1,2)
AND e.EmployeeId=54
GROUP BY e.EmployeeId, BIT.Label,BI.UpdatedDate,BI.IBAN
ORDER BY e.EmployeeId
)
SELECT [EmployeeId]
, max(IBAN_Salary) IBAN_Salary
, max(IBAN_Expenses_Claims) IBAN_Expenses_Claims
, max(UpdatedDated_IBAN_Salary) UpdatedDated_IBAN_Salary
, max(UpdatedDated_IBAN_Expenses_Claims) UpdatedDated_IBAN_Expenses_Claims
FROM cte WHERE rn =1
GROUP BY [EmployeeId];
Looking at your sample result it probably can be done in a single step.
One approach, perhaps not optimal, would be to just peform another aggregation on the CTE:
SELECT
EmployeeId,
MAX(IBAN_Salary) AS IBAN_Salary,
MAX(IBAN_Expenses_Claims) AS IBAN_Expenses_Claims,
MAX(CASE WHEN IBAN_Salary IS NOT NULL THEN UpdatedDated END) AS UpdatedDated_IBAN_Salary,
MAX(CASE WHEN IBAN_Expenses_Claims IS NOT NULL THEN UpdatedDated END) AS UpdatedDated_IBAN_Expenses_Claims
FROM cte
WHERE
rn = 1 -- uncomment for all employees
GROUP BY
EmployeeId;

Use of Analytical Funtions and Keep clause

I have a lenghtly query that can be shorten with the correct functionally (I beleive). Can we use existing functions such as Max Min Keep to make this query more efficient? My entire query is posted below.
For example: Can we remove the CTEs and use analytical functions such as max and min This would also elimate ranks and several joins
SQL:
WITH LAST_VALUE_BEFORE_START_DT AS (
SELECT DISTINCT * FROM(
SELECT
P.CL_ID,
HISTORYID,
H.MENT_DT,
H.ROLE AS MAX_ROLE,
H.PM_ID AS MAX_P_ID,
DENSE_RANK() OVER (PARTITION BY P.CL_ID ORDER BY H.MENT_DT DESC )AS RNK
FROM MANAGER_HISTORY H
INNER JOIN CP CCP ON H.CLIID = CCP.CLIID
INNER JOIN PROGRAM CP ON PROGRAMID = CP.PROGRAMID
WHERE 1=1
AND CP.TYPEID IN (13,200,11001)
AND H.ROLE = 'RED'
AND H.MENT_DT < START_DT
--AND P.CL_ID = 920917
)LAST_VALUE_BEFORE_START_DT_RNK
WHERE 1=1
AND RNK =1
)
,MIN_VALUE_BETWEEN_PROGRAM AS (
SELECT * FROM(
SELECT DISTINCT
P.CL_ID,
HISTORYID,
TRUNC(H.MENT_DT) AS MENT_DT,
H.ROLE AS MIN_ROLE,
H.PM_ID AS MIN_PM_ID,
DENSE_RANK() OVER (PARTITION BY P.CL_ID ORDER BY H.MENT_DT)AS RNK
FROM MANAGER_HISTORY H
INNER JOIN CP CCP ON H.CLIID = CCP.CLIID
INNER JOIN PROGRAM CP ON PROGRAMID = CP.PROGRAMID
WHERE 1=1
AND CP.TYPEID IN (13,200,11001)
AND H.ROLE = 'RED'
AND H.PM_ID IS NOT NULL
AND TRUNC(H.MENT_DT) BETWEEN TRUNC(START_DT) AND NVL(END_DT,SYSDATE)
--AND P.CL_ID = 920917
) MIN_VALUE_BETWEEN_PROGRAM_RNK
WHERE 1=1
AND RNK =1
)
SELECT * FROM (
SELECT
X.*,
DENSE_RANK() OVER (PARTITION BY CL_ID ORDER BY FIRST_ASSGN_DT,MENT_DT ) AS RNK
FROM(
SELECT DISTINCT
C.CL_ID,
P.CL_ID,
CP.PROGRAM,
START_DT,
END_DT,
H.ROLE,
H.MENT_DT,
H.PM_ID,
LVBS.MAX_ROLE,
LVBS.MAX_P_ID,
MVBP.MIN_ROLE,
MVBP.MIN_PM_ID
,CASE
WHEN H.MENT_DT < START_DT AND LVBS.MAX_ROLE = 'RED' AND LVBS.MAX_P_ID IS NOT NULL THEN TRUNC(START_DT)
WHEN H.MENT_DT BETWEEN START_DT AND NVL(END_DT,SYSDATE) AND H.ROLE = 'RED' AND H.PM_ID IS NOT NULL
THEN MVBP.MENT_DT
ELSE NULL --TESTING PURPOSES
END FIRST_ASSGN_DT
FROM MANAGER_HISTORY H
INNER JOIN CP CCP ON H.CLIID = CCP.CLIID
INNER JOIN CLIENT C ON CCP.CLIID = C.CLIID
INNER JOIN PROGRAM CP ON PROGRAMID = CP.PROGRAMID
LEFT JOIN LAST_VALUE_BEFORE_START_DT LVBS ON P.CL_ID = LVBS.CL_ID
LEFT JOIN MIN_VALUE_BETWEEN_PROGRAM MVBP ON P.CL_ID = MVBP.CL_ID
WHERE 1=1
AND CP.TYPEID IN (13,200,11001)
)X)Z
WHERE 1=1
AND Z.RNK = 1

Show unique value for id, based on latest createby date

I am using the following SQL code to JOIN a few tables, but need to show unique values for the s.incidentid based on the latest s.createdate.
I know I need to use a Sub query with Maxdate, but I am not sure on the correct syntax.
This is my first query with multiple joins and I am struggling to get my head round it.
Here is my code:
SELECT
s.incidentid,
u.internalid as AssignedTo,
u.fullname as AssignedTo_FullName,
s.createby as AssignedBy,
u2.fullname as AssignedBy_FullName,
s.createdate as AssignedTime,
i.[description],
i.fix,
st.[description] as [Status],
(SELECT (CASE WHEN u.internalid = s.createby THEN 'Yes' ELSE 'No' END) as SelfAssigned),
d.d1,
d.d2,
d.d3,
d.d4,
d.d5
FROM dbo.IncidentServiceLevelAgreement s
JOIN dbo.UserAll u on u.userid = s.userid
JOIN dbo.UserAll u2 on u2.internalid = s.createby
JOIN dbo.IncidentAll i on s.incidentid = i.incidentid
JOIN dbo.[Status] st on i.statusid = st.statusid
JOIN dbo.flatdiagnosis d on i.actualdiagnosisid = d.diagnosisid
WHERE (s.groupId = '4954' and s.incidentServiceLevelAgreementTypeID = '9')
ORDER BY AssignedTime DESC
Any help greatly appreciated.
The easiest is to use a CTE and the ROW_NUMBER function:
WITH CTE AS
(
SELECT RN = ROW_NUMBER() OVER ( PARTITION BY incidentid
ORDER BY createdate DESC ),
s.Incidentid,
u.Internalid AS AssignedTo,
u.Fullname AS AssignedTo_FullName,
s.Createby AS AssignedBy,
u2.Fullname AS AssignedBy_FullName,
s.Createdate AS AssignedTime,
i.[Description],
i.Fix,
st.[Description] AS [Status],
SelfAssigned = CASE WHEN u.Internalid = s.Createby
THEN 'Yes' ELSE 'No' END,
d.D1,
d.D2,
d.D3,
d.D4,
d.D5
FROM dbo.Incidentservicelevelagreement s
JOIN dbo.Userall u
ON u.Userid = s.Userid
JOIN dbo.Userall u2
ON u2.Internalid = s.Createby
JOIN dbo.Incidentall i
ON s.Incidentid = i.Incidentid
JOIN dbo.[Status] st
ON i.Statusid = st.Statusid
JOIN dbo.Flatdiagnosis d
ON i.Actualdiagnosisid = d.Diagnosisid
WHERE ( s.Groupid = '4954'
AND s.Incidentservicelevelagreementtypeid = '9' )
)
SELECT * FROM CTE WHERE RN = 1
ORDER BY AssignedTime DESC
(instead of SELECT * list all columns explicitly, I didn't feel like it)

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.