Reputation: 162
I have the following query:
SELECT
fs.FILOID currentfilo,
ISNULL(LEAD(af.FILOID) OVER (ORDER BY StartTransactionTimeUTC), fs.Next_FILOID) Nextfilo,
ISNULL(LAG(af.FILOID) OVER (ORDER BY StartTransactionTimeUTC), fs.Previous_FILOID) lf
FROM
[DataWarehouseCore].[DWC_FILOSummary] fs
JOIN
[AttendanceCore].[AC_FILO] af ON af.FILOID = fs.FILOID
WHERE
fs.Employee_ID = 15049
AND Client_ID = 306
Which returns the following results:
currentfilo Nextfilo lf
----------------------------
5 2 NULL
2 3 5
3 6 2
6 7 3
7 1 6
1 NULL 7
I only want to have to see currentfilo 3, 6, 7 , 1 if ID 6 was passed in. So instead of returning what I have in my result set just the previous and next records which are actually based on a date field.
Here is some examples of data and what I expect to see this is the parent table: AC_FILO
FILOID ClientID EmployeeID StartTransactionTimeUTC
-----------------------------------------------------------
5 306 15049 2021-08-29 02:53:00.0000000
2 306 15049 2021-09-01 06:46:00.0000000
3 306 15049 2021-09-02 07:50:00.0000000
6 306 15049 2021-09-06 08:56:00.0000000
7 306 15049 2021-09-10 07:58:00.0000000
1 306 15049 2021-09-15 07:45:00.0000000
What I want in my new table is an ordered list of the current FILOID, the next FILOID and previous FILOID is based on the StartTransactionTimeUTC
For example when FILOID 6 was inserted I would want to see the previous FILOID to FILOID 6 followed by all proceeding FILOID's based on the date!
For example I would want to see
CurrentFilo NextFilo LastFilo
-----------------------------
3 6 2
6 7 3
7 1 6
1 NULL 7
Upvotes: 0
Views: 74
Reputation: 415725
It's not totally clear what you're after, but given the final desired results and input value of 6
, I take it to mean you want the (CurrentFilo, NextFilo,LastFilo)
sets ordered by StartTransactionTimeUTC
starting with the point where a given NextFilo
occurs, since the 6
input matches the 6
in that place in the expected result.
As a first step, start with not caring about anything passed in and only create the next/last result for the entire table. We can use the LAG()
and LEAD()
window functions to do this.
You've done most of this work already in the question's first code listing, but let's also include the StartTransactionTimeUTC
field for now, so we will be able to reproduce the correct order in later steps. I'm also using a simplified schema right now so I can reproduce this on SQL Fiddle:
SELECT FILOID as CurrentFILO,
LEAD(FILOID) OVER (ORDER BY StartTransactionTimeUTC) as NextFILO,
LAG(FILOID) OVER (ORDER BY StartTransactionTimeUTC) as LastFILO,
StartTransactionTimeUTC
FROM AC_FILO
Now for a given NextFILO
value like 6
we can find the desired StartTransactionTimeUTC
time of that record by using a Common Table Expression (CTE), like this:
WITH FILOTree AS (
SELECT FILOID as CurrentFILO,
LEAD(FILOID) OVER (ORDER BY StartTransactionTimeUTC) as NextFILO,
LAG(FILOID) OVER (ORDER BY StartTransactionTimeUTC) as LastFILO,
StartTransactionTimeUTC
FROM AC_FILO
)
SELECT StartTransactionTimeUTC
FROM FILOTree
WHERE NextFILO = 6
We could also use a nested inner SELECT instead of the CTE. The important thing is we need a layer of indirection around the initial query in order to use a WHERE
clause with a window function like LAG()
or LEAD()
. But I prefer the CTE in this case for reasons that will become clear later.
Once we know the correct StartTransactionTimeUTC
we can get the final results using the same technique:
WITH FILOTree AS (
SELECT FILOID as CurrentFILO,
LEAD(FILOID) OVER (ORDER BY StartTransactionTimeUTC) as NextFILO,
LAG(FILOID) OVER (ORDER BY StartTransactionTimeUTC) as LastFILO,
StartTransactionTimeUTC
FROM AC_FILO
)
SELECT CurrentFILO, NextFILO, LastFILO
FROM FILOTree
WHERE StartTransactionTimeUTC >= '2021-09-02 07:50:00'
ORDER BY StartTransactionTimeUTC
This produces the correct results, but from the wrong input. However, now we can use the same CTE (aha!) to put the two pieces together into the same query without repeating code for the initial current/next/last projection, like this:
WITH FILOTree As (
SELECT FILOID as CurrentFILO,
LEAD(FILOID) OVER (ORDER BY StartTransactionTimeUTC) as NextFILO,
LAG(FILOID) OVER (ORDER BY StartTransactionTimeUTC) as LastFILO,
StartTransactionTimeUTC
FROM AC_FILO
)
SELECT CurrentFILO, NextFILO, LastFILO
FROM FILOTree
WHERE StartTransactionTimeUTC >= (
SELECT StartTransactionTimeUTC
FROM FILOTree
WHERE NextFILO = 6
)
ORDER BY StartTransactionTimeUTC
See it work here:
Finally, as I said before, these examples use a simplified schema from the actual production system. So let's plug the original query into the CTE:
WITH FILOTree As (
SELECT fs.FILOID currentfilo
, ISNULL(LEAD(af.FILOID) over (order by StartTransactionTimeUTC), fs.Next_FILOID) Nextfilo
, ISNULL(LAG(af.FILOID) over (order by StartTransactionTimeUTC), fs.Previous_FILOID) LastFILO
, StartTransactionTimeUTC
FROM [DataWarehouseCore].[DWC_FILOSummary] fs
JOIN [AttendanceCore].[AC_FILO] af ON af.FILOID = fs.FILOID
WHERE fs.Employee_ID = 15049
AND Client_ID = 306
)
SELECT CurrentFILO, NextFILO, LastFILO
FROM FILOTree
WHERE StartTransactionTimeUTC >= (
SELECT StartTransactionTimeUTC
FROM FILOTree
WHERE NextFILO = 6
)
ORDER BY StartTransactionTimeUTC
The only things I had to change from that initial SQL command were adding StartTransactionTimeUTC
to the SELECT
list and the alias name for the LastFILO
column.
Note this will run the CTE twice and it will not cache any results between the two runs, so be aware of that as a performance concern.
Upvotes: 2