Ordering the results of CTE in SQL - 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.

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'

row number broken when its used with CTE

This is my query the SQL inside CTE works fine and all I want to get first 20 rows for each city name:
WITH taniCte
AS (
SELECT sl.Adi AS [CityName],
mt.ICD10Kodu AS [Tanı],
count(mt.ICD10Kodu) AS sayi,
RowNum = ROW_NUMBER() OVER (
ORDER BY sl.Adi
)
FROM Muayene.Muayene mm WITH (NOLOCK)
INNER JOIN Muayene.Tani mt WITH (NOLOCK) ON mm.ID = mt.MuayeneId
INNER JOIN Ortak.Kurum ok WITH (NOLOCK) ON mm.CreatedKurumKodu = ok.KurumKodu
INNER JOIN Skrs.Il sl WITH (NOLOCK) ON ok.IlKodu = sl.Kodu
GROUP BY sl.Adi,
mt.ICD10Kodu
) --order by [CityName], sayi desc // commentewhen its moved inside cte
SELECT [CityName],
[Tanı],
sayi,
RowNum
FROM taniCte
WHERE RowNum <= 20
ORDER BY [CityName],
sayi DESC
Eventhough main query works fine the query above returns wrong rows..
I couldn't find any solution on internet
If you need 20 rows per city then your row_number OVER clause in cte should be
with taniCte as
(
select sl.Adi as [CityName]
, mt.ICD10Kodu as [Tanı]
, count(mt.ICD10Kodu) as sayi
, RowNum = ROW_NUMBER()OVER(partition By sl.Adi order by count(mt.ICD10Kodu)) -- this change in over clause
from Muayene.Muayene mm with(nolock)
join Muayene.Tani mt with(nolock) on mm.ID = mt.MuayeneId
join Ortak.Kurum ok with(nolock) on mm.CreatedKurumKodu = ok.KurumKodu
join Skrs.Il sl with(nolock) on ok.IlKodu = sl.Kodu
group by sl.Adi, mt.ICD10Kodu
--order by [CityName], sayi desc // commentewhen its moved inside cte
)
select [CityName], [Tanı],sayi, RowNum
from taniCte
where RowNum <= 20
order by [CityName], sayi desc

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

Join two tables returning only one row from the second table

I have this query:
SELECT t_ticket.ticketID, t_ticket.addedDate, t_ticket.question,
t_ticket.code, t_ticket.priority, t_actionTicket.addedDateAction, t_actionTicket.title
FROM t_actionTicket INNER JOIN
t_ticket ON t_actionTicket.ticketID_FK = t_ticket.ticketID INNER JOIN
(SELECT ticketID_FK, MAX(addedDateAction) AS maxDate
FROM t_actionTicket AS t_actionTicket_1
WHERE (t_actionTicket.userID_FK <> #userid)
GROUP BY ticketID_FK) AS b ON t_actionTicket.ticketID_FK = b.ticketID_FK AND t_actionTicket.addedDateAction = b.maxDate
WHERE (t_ticket.supporterID_FK IN
(SELECT supporterID
FROM t_Supporter
WHERE (userID_FK = #userid)))
I want to return just the latest record in t_actionTicket table for each row in t_ticket table that t_actionTicket.userID_FK <> #userid.
but I have this error:
The multi-part identifier "t_actionTicket.userID_FK" could not be
bound.
Problem in your query is
FROM t_actionTicket AS t_actionTicket_1
WHERE t_actionTicket.userID_FK <> #userid -- here
You cannot use t_actionTicket alias name inside inner join select query. You need to use t_actionTicket_1. It is possible only in sub-query
Try this better way of doing it
;WITH cte
AS (SELECT t_ticket.ticketID,
t_ticket.addedDate,
t_ticket.question,
t_ticket.code,
t_ticket.priority,
t_actionTicket.addedDateAction,
t_actionTicket.title,
Row_number()
OVER(
partition BY ticketID_FK
ORDER BY addedDateAction DESC) RN
FROM t_actionTicket
INNER JOIN t_ticket
ON t_actionTicket.ticketID_FK = t_ticket.ticketID
WHERE t_ticket.supporterID_FK IN (SELECT supporterID
FROM t_Supporter
WHERE userID_FK = #userid))
SELECT *
FROM cte
WHERE rn = 1
You can write this logic using row_number() instead of additional nested queries:
SELECT t.ticketID, t.addedDate, t.question, t.code, t.priority,
ta.addedDateAction, ta.title AS Expr1
FROM t_Ticket t INNER JOIN
(SELECT ta.*,
ROW_NUMBER() OVER (PARTITION BY ta.ticketID_FK ORDER BY ta.addedDateAction DESC) as seqnum
FROM t_actionTicket ta
) ta
ON t.ticketId = ta.ticketId_FK and ta.seqnum = 1
WHERE t.supporterID_FK IN (SELECT supporterID
FROM t_Supporter
WHERE userID_FK = #userid
);
Note that table aliases make the query easier to write and to read.
Try this query
SELECT t_ticket.ticketID, t_ticket.addedDate, t_ticket.question,
t_ticket.code, t_ticket.priority, t_actionTicket.addedDateAction, t_actionTicket.title
FROM t_actionTicket
INNER JOIN t_ticket ON t_actionTicket.ticketID_FK = t_ticket.ticketID
INNER JOIN (SELECT ticketID_FK, MAX(addedDateAction) AS maxDate
FROM t_actionTicket AS t_actionTicket_1
WHERE (t_actionTicket_1.userID_FK <> #userid)
GROUP BY ticketID_FK) AS b ON t_actionTicket.ticketID_FK = b.ticketID_FK AND t_actionTicket.addedDateAction = b.maxDate
WHERE (t_ticket.supporterID_FK IN
(SELECT supporterID
FROM t_Supporter
WHERE (userID_FK = #userid)))

Returning rows selected in with statement

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