Select distinct on foreign key, inner join another table - sql

Basically what I want to do is, select the 3 last actions made from the user. But no duplicates on RelationId, and also innerjoin permissions just to make sure the user has permission still to make the same action.
The only thing I want from permission is RelationId.
By no duplicated I mean if there is two rows of Action with the same RelationId the one closes to the top ( Ordered by TimeStamp ) should be picked.
What I've came up with so far:
SELECT DISTINCT a.*, p.RelationId
FROM [Action] [a]
INNER JOIN [Permission] p
ON ([p].[RelationId] = [a].[RelationId]
AND [p].[RelationType] = [a].[RelationType]
AND [p].[UserId] = [a].[UserId]
AND [p].[Deleted] = 0)
WHERE [a].[ActionType] = 'Clicked'
AND [a].[RelationType] = 'Direct'
AND [a].[UserId] = 5
AND [a].[Deleted] = 0
ORDER BY [a].[TimeStamp] DESC
OFFSET 0 ROWS
FETCH NEXT 3 ROWS ONLY
It need to use OFFSET X ROWS and FETCH NEXT 3 ROWS ONLY for paging
This doesn't work for some reason, because I get duplicates on RelationId.
No errors.
example data:
action (
id INTEGER PRIMARY KEY,
ActionType VARCHAR(50) not null,
RelationId INTEGER ForeignKey,
Deleted Bit not null,
TimeStamp DATE not null,
UserId INTEGER ForeignKey
);
Desired result: 3 last actions made by one user that has permissions for it distinct by RelationId.

From your definition of last 3 per user (if last 3 then it can't be a single row per relationId):
with data as (
SELECT *,
row_number() over (partition by relationId order by [timeStamp] desc) as rNo
from action
where [ActionType] = 'Clicked' AND
[RelationType] = 'Direct' AND
[UserId] = 5 AND
[Deleted] = 0
)
select a.id,a.ActionType,a.RelationId,a.Deleted,a.TimeStamp,a.UserId,
p.RelationId as pRelId
FROM [data] [a]
INNER JOIN [Permission] p ON ([p].[RelationId] = [a].[RelationId] AND [p].[RelationType] = [a].[RelationType] AND [p].[UserId] = [a].[UserId])
WHERE p.[Deleted] = 0 and a.rNo <= 3
ORDER BY [a].[TimeStamp] DESC;

If I get it, you need for each Permission only the latest action, and from all these keep most recent three rows. Here:
select top 3 p.RelationId,top_action.*
from
[Permission] p
cross apply
(
select top 1 *
from [Action] [a]
where
[p].[RelationId] = [a].[RelationId] AND [p].[RelationType] = [a].[RelationType] AND [p].[UserId] = [a].[UserId]
and [a].[ActionType] = 'Clicked' AND [a].[RelationType] = 'Direct' AND [a].[UserId] = 5 AND [a].[Deleted] = 0
order by [a].[TimeStamp] DESC
)top_action
where [p].[Deleted] = 0
order by top_action.[TimeStamp] DESC

Distinct will always distinct all parameters. Selecting * will always differ.
You need to choose just one parameter to determine.
You can use group by, or simply make it in two steps using same select. Like this:
select * from Action where id in (
SELECT DISTINCT a.id
FROM [Action] [a]
INNER JOIN [Permission] p
ON ([p].[RelationId] = [a].[RelationId]
AND [p].[RelationType] = [a].[RelationType]
AND [p].[UserId] = [a].[UserId]
AND [p].[Deleted] = 0)
WHERE [a].[ActionType] = 'Clicked'
AND [a].[RelationType] = 'Direct'
AND [a].[UserId] = 5
AND [a].[Deleted] = 0
ORDER BY [a].[TimeStamp] DESC
OFFSET 0 ROWS
FETCH NEXT 3 ROWS ONLY
)

Solved it by doing this. Don't know if its the best solution though.
SELECT [a].*, [p].[Id]
FROM (SELECT *, ROW_NUMBER() OVER(PARTITION BY [RelationId] ORDER BY [TimeStamp] DESC) AS row from [Action]) a
INNER JOIN [Permission] p ON ([ep].[RelationId] = [a].[RelationId] AND [p].[RelationType] = [a].[RelationType] AND [p].[UserId] = [a].[UserId] AND [p].[Deleted] = 0)
WHERE row = 1 AND [a].[ActionType] = 'Clicked' AND [a].[RelationType] = 'Direct' AND [a].[UserId] = 5 AND [a].[Deleted] = 0
ORDER BY [a].[TimeStamp] DESC
OFFSET 0 ROWS
FETCH NEXT 3 ROWS ONLY

Related

How to compare smallint -1 in SQL

I'm trying to find out if status_id field value of SQL smallint is -1 and and get the records that doesn't have -1 for that field. My stored proc content is as follows:
SELECT DISTINCT TOP 7
cs.case_id,
cs.status_id,
cm.company_name,
cs.created_time,
cs.severity_id,
cs.last_updated_time,
COALESCE(NULLIF(cs.priority,''), 'Medium') AS case_priority
FROM
tblcase cs with (nolock)
INNER JOIN tblcompany cm with (nolock) ON (cs.company_id=cm.company_id)
WHERE
CONVERT(INT, cs.status_id) <> -1 AND
(cs.cas_case_owner = #userId AND cs.is_notify_co = 1) OR
(cs.activity_owner = #userId AND cs.is_notify_ao = 1)
ORDER BY cs.severity_id DESC, cs.case_id ASC
I have tried CONVERT(int, cs.status_id) <> -1), cs.status_id <> CONVERT(smallint, -1), cs.status_id != CAST('-1' AS smallint) and more, but I still keep getting records with -1 as the status_id. Please help me understand what I'm doing wrong before downvoting.
I think you forgot to use brackets for OR operation
cs.status_id <> -1 AND
( -- open
(cs.cas_case_owner = #userId AND cs.is_notify_co = 1)
OR
(cs.activity_owner = #userId AND cs.is_notify_ao = 1)
) -- close
See my another answer about it here - SQL Server Left Join Counting
Try this
SELECT DISTINCT TOP 7
cs.case_id,
cs.status_id,
cm.company_name,
cs.created_time,
cs.severity_id,
cs.last_updated_time,
COALESCE(NULLIF(cs.priority,''), 'Medium') AS case_priority
FROM
tblcase cs with (nolock)
INNER JOIN tblcompany cm with (nolock) ON (cs.company_id=cm.company_id)
WHERE ISNULL(CAST(cs.status_id AS INT),0)<> -1
AND cs.is_notify_co = 1
AND
(
cs.cas_case_owner = #userId
OR
cs.activity_owner = #userId
)
ORDER BY cs.severity_id DESC, cs.case_id ASC
Try this:
SELECT DISTINCT TOP 7
cs.case_id,
cs.status_id,
cm.company_name,
cs.created_time,
cs.severity_id,
cs.last_updated_time,
COALESCE(NULLIF(cs.priority,''), 'Medium') AS case_priority
FROM
tblcase cs with (nolock)
INNER JOIN tblcompany cm with (nolock) ON (cs.company_id=cm.company_id)
WHERE
(
( cs.status_id <> -1 AND cs.cas_case_owner = #userId) AND
( (cs.is_notify_co = 1) OR (cs.is_notify_ao = 1))
)
ORDER BY cs.severity_id DESC, cs.case_id ASC

Sql Query : Next Last Rows

I have tables Customer and Calls like this:
Customers table:
CustomerId : uniqueidentifier
BusinessName : nvarchar(MAX)
Calls table:
CallId : uniqueidentifier
CallDate :datetime
CustomerId: uniqueidentifier
Status: int (call has been answered or no)
I want to get all the customers with last called and next call columns.
Last Called is the last call where status = 1. Next Call is the call where status = 0 and the Calldate > now date. Null if they do not have any. This is a non working example just to show the format
Select *
From Customers,
Calls.CallId as LastCalledId
Calls.CallDate as LastCalledDate
Calls.CallId as NextCallId
Calls.CallDate as NextCallDate
LEFT JOIN
Calls ON Customers.CustomerId = Calls.CustomerId
How can I do this?
Is this what you mean?
Update
select c.id
, lastcall.calldate
, lastcall.id
, firstcall.calldate
, firstcall.id
from #customers c
outer apply (select top 1 calls.calldate, calls.id from #calls calls where calls.custid = c.id and calldate < getdate() order by calldate desc) lastcall(calldate, id)
outer apply (select top 1 calls.calldate, calls.id from #calls calls where calls.custid = c.id and calldate >= getdate() order by calldate asc) firstcall(calldate, id)
original answer:
select c.id
, answered.calldate LastCalledDate
, answered.id LastCalledID
, unaswered.calldate NextCalledDate
, unaswered.id NextCalledID
from #customers c
left join #calls answered ON answered.custid = c.id AND status = 1
left join #calls unaswered ON unaswered.custid = c.id AND unaswered.status = 0 and unaswered.calldate > getdate()
You can use WHERE clause in LEFT JOIN for getting required data rows.Here you can use where clause with Calls.status = 0 or 1 (as per your requirement)
Since you didn't provide DDL and DML for sample data I couldn't test my answer, so It's not guaranteed to produce the desired results.
Having said that, here is my attempt that should work on versions 2008 or higher.
Using a cte to calculate the last answered call for each customer (assuming calldate is unique) and a couple of left joins I came up with this:
;WITH LastCall AS
(
SELECT CustomerId,
CallId,
CallDate
FROM Calls t0
WHERE [Status] = 1
AND CallDate =
(
SELECT MAX(CallDate)
FROM Calls t1
WHERE [Status] = 1
AND t1.CustomerId = t0.CustomerId
)
)
SELECT cu.CustomerId,
cu.Name,
lc.CallId as LastCalledId,
lc.CallDate as LastCalledDate,
nc.CallId as NextCallId,
nc.CallDate as NextCallDate
FROM Customers as cu
LEFT JOIN LastCall lc ON cu.CustomerId = lc.CustomerId
LEFT JOIN Calls nc ON cu.CustomerId = nc.CustomerId AND nc[Status] = 0 AND nc.CallDate > GETDATE()
Please note that for versions 2012 and higher you might be able to use FIRST_VALUE or LAST_VALUE built in functions.
You can use AND in join query. Then order calls and get last one with limit.
SELECT *
FROM Customers,
Calls.CallId as LastCalledId
Calls.CallDate as LastCalledDate
Calls.CallId as NextCallId
LEFT JOIN
Calls ON (Customers.CustomerId = Calls.CustomerId AND Calls.Status = 1)
ORDER BY Calls.CallDate DESC
UNION
SELECT *
FROM Customers,
Calls.CallId as LastCalledId
Calls.CallId as NextCallId
Calls.CallDate as NextCallDate
LEFT JOIN
Calls ON (Customers.CustomerId = Calls.CustomerId AND Calls.Status = 0 AND CallDate > NOW())
ORDER BY Calls.CallDate DESC;
You can do this using different ways :
Select cu.CustomerId,LastCall.Callid,LastCall.calldate,NextCall.Callid,NextCall.calldate from Customers cu,
(select max(callid) Callid,max(calldate) calldate,CustomerId from Calls c where status = 1 group by CustomerId) LastCall,
(select max(callid) Callid,max(calldate) calldate,CustomerId from Calls c where status = 0 and calldate > sysdate group by CustomerId) NextCall
where cu.CustomerId = LastCall.CustomerId and cu.CustomerId = NextCall.CustomerId
order by 1
Or by replacing in your query :
Select (select max(callid) from Calls c where c.CustomerId = Cu.CustomerId and status = 1) as LastCalledId,
(select max(CallDate) from Calls c where c.callid = (select max(callid) from Calls c where c.CustomerId = Cu.CustomerId and status = 1)) as LastCalledDate,
(select max(callid) from Calls c where c.CustomerId = Cu.CustomerId and status = 0 and CallDate > sysdate ) as NextCallId,
(select max(CallDate) from Calls c where c.callid = (select max(callid) from Calls c where c.CustomerId = Cu.CustomerId and status = 0 and CallDate > sysdate )) as NextCallDate
From Customers Cu,Calls c ON Cu.CustomerId = c.CustomerId

How can I optimize the SQL query?

I have a query an SQL query as follows, can anybody suggest any optimization for this; I think most of the effort is being done for the Union operation - is there anything else can be done to get the same result ?
Basically I wanna query first portion of the UNION and if for each record there is no result then the second portion need to be run. Please help.
:
SET dateformat dmy;
WITH incidentcategory
AS (
SELECT 1 ord, i.IncidentId, rl.Description Category FROM incident i
JOIN IncidentLikelihood l ON i.IncidentId = l.IncidentId
JOIN IncidentSeverity s ON i.IncidentId = s.IncidentId
JOIN LikelihoodSeverity ls ON l.LikelihoodId = ls.LikelihoodId AND s.SeverityId = ls.SeverityId
JOIN RiskLevel rl ON ls.RiskLevelId = rl.riskLevelId
UNION
SELECT 2 ord, i.incidentid,
rl.description Category
FROM incident i
JOIN incidentreportlikelihood l
ON i.incidentid = l.incidentid
JOIN incidentreportseverity s
ON i.incidentid = s.incidentid
JOIN likelihoodseverity ls
ON l.likelihoodid = ls.likelihoodid
AND s.severityid = ls.severityid
JOIN risklevel rl
ON ls.risklevelid = rl.risklevelid
) ,
ic AS (
SELECT ROW_NUMBER() OVER (PARTITION BY i.IncidentId ORDER BY (CASE WHEN incidentTime IS NULL THEN GETDATE() ELSE incidentTime END) DESC,ord ASC) rn,
i.incidentid,
dbo.Incidentdescription(i.incidentid, '',
'',
'', '')
IncidentDescription,
dbo.Dateconverttimezonecompanyid(closedtime,
i.companyid)
ClosedTime,
incidenttime,
incidentno,
Isnull(c.category, '')
Category,
opencorrectiveactions,
reportcompleted,
Isnull(classificationcompleted, 0)
ClassificationCompleted,
Cast (( CASE
WHEN closedtime IS NULL THEN 0
ELSE 1
END ) AS BIT)
IncidentClosed,
Cast (( CASE
WHEN investigatorfinishedtime IS NULL THEN 0
ELSE 1
END ) AS BIT)
InvestigationFinished,
Cast (( CASE
WHEN investigationcompletetime IS NULL THEN 0
ELSE 1
END ) AS BIT)
InvestigationComplete,
Cast (( CASE
WHEN investigatorassignedtime IS NULL THEN 0
ELSE 1
END ) AS BIT)
InvestigatorAssigned,
Cast (( CASE
WHEN (SELECT Count(*)
FROM incidentinvestigator
WHERE incidentid = i.incidentid
AND personid = 1588
AND tablename = 'AdminLevels') = 0
THEN 0
ELSE 1
END ) AS BIT)
IncidentInvestigator,
(SELECT dbo.Strconcat(osname)
FROM (SELECT TOP 10 osname
FROM incidentlocation l
JOIN organisationstructure o
ON l.locationid = o.osid
WHERE incidentid = i.incidentid
ORDER BY l.locorder) loc)
Location,
Isnull((SELECT TOP 1 teamleader
FROM incidentinvestigator
WHERE personid = 1588
AND tablename = 'AdminLevels'
AND incidentid = i.incidentid), 0)
TeamLeader,
incidentstatus,
incidentstatussearch
FROM incident i
LEFT OUTER JOIN incidentcategory c
ON i.incidentid = c.incidentid
WHERE i.isdeleted = 0
AND i.companyid = 158
AND incidentno <> 0
--AND reportcompleted = 1
--AND investigatorassignedtime IS NOT NULL
--AND investigatorfinishedtime IS NULL
--AND closedtime IS NULL
),
ic2 AS (
SELECT * FROM ic WHERE rn=1
)
SELECT * FROM ic2
--WHERE rownumber >= 0
-- AND rownumber < 0 + 10
--WHERE ic2.incidentid in(53327,53538)
--WHERE ic2.incidentid = 53338
ORDER BY incidentid DESC
Following is the execution plan I got:
https://www.dropbox.com/s/50dcpelr1ag4blp/Execution_Plan.sqlplan?dl=0
There are several issues:
1) use UNION ALL instead of UNION ALL to avoid the additional operation to aggregate the data.
2) try to modify the numerous function calls (e.g. dbo.Incidentdescription() ) to be an in-lie table valued function so you can reference it using CROSS APPLY or OUTER APPLY. Especially, if those functions referencing a table again.
3) move the subqueries from the SELECT part of the query to the FROM part using CROSS APPLY or OUTER APPLY again.
4) after the above is done, check the execution plan again for any missing indexes. Also, run the query with STATISTICS TIME, IO on to verify that the number of times a table
is referenced is correct (sometimes the execution plan put you in the wrong direction, especially if function calls are involved)...
Since the first inner query produces rows with ord=1 and the second produces rows with ord=2, you should use UNION ALL instead of UNION. UNION will filter out equal rows and since you will never get equal rows it is more efficient to use UNION ALL.
Also, rewrite your query to not use the WITH construct. I've had very bad experiences with this. Just use regular derived tables instead. In the case the query is still abnormally slow, try to serialize some derived tables to a temporary table and query the temporary table instead.
Try alternate approach by removing
(SELECT dbo.Strconcat(osname)
FROM (SELECT TOP 10 osname
FROM incidentlocation l
JOIN organisationstructure o
ON l.locationid = o.osid
WHERE incidentid = i.incidentid
ORDER BY l.locorder) loc)
Location,
Isnull((SELECT TOP 1 teamleader
FROM incidentinvestigator
WHERE personid = 1588
AND tablename = 'AdminLevels'
AND incidentid = i.incidentid), 0)
TeamLeader
from the SELECT. Avoid using complex functions/sub-queries in select.

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.