Good morning,
I have been scratching my head over this query for quite some time, and it has reached the point where I am just going in circles and have to finally admit defeat and ask for help.
The query that I am working with contains ID card transactions from one table, with all entry and exit card taps pertaining to each individual cardholder residing in this table.
What I would like is to display card taps and the timestamp from the entry card reader and its corresponding exit card tap plus timestamp, with one pair of related entry and exit times on the same row.
There should be a null where there is no corresponding entry or exit card tap, and there exists just one point of entry and one point of exit.
There is no restriction on the number of times that someone can enter or leave the building, and there is no requirement to enforce shift patterns.
Here is the code that creates a temporary table containing a representation of the data I am working with:
USE tempdb;
GO
IF OBJECT_ID(N'tempdb.dbo.#temptable') IS NOT NULL
DROP TABLE #temptable
CREATE TABLE #temptable (
[UniqueID] int,
[DateTimeOfCardTap] datetime,
[CardReaderLocation] nvarchar(255),
[ReaderNumber] int,
[AccessAttemptResult] nvarchar(255),
[CardNumber] int,
[FirstName] nvarchar(40),
[LastName] nvarchar(40)
)
INSERT INTO #temptable (
[UniqueID],
[DateTimeOfCardTap],
[CardReaderLocation],
[ReaderNumber],
[AccessAttemptResult],
[CardNumber],
[FirstName],
[LastName]
)
VALUES
( 32042292, N'2023-05-20T11:22:24', N'Ingress', 116, N'Success for [1234567]', 1234567, N'John', N'Another' ),
( 32042474, N'2023-05-20T12:35:30', N'Egress', 117, N'Success for [1234567]', 1234567, N'John', N'Another' ),
( 32042532, N'2023-05-20T12:43:57', N'Egress', 117, N'Success for [1234567]', 1234567, N'John', N'Another' ),
( 32042607, N'2023-05-20T13:13:34', N'Ingress', 116, N'Success for [1234567]', 1234567, N'John', N'Another' ),
( 32043558, N'2023-05-20T16:24:58', N'Egress', 117, N'Success for [1234567]', 1234567, N'John', N'Another'),
( 32043564, N'2023-05-20T16:34:36', N'Ingress', 116, N'Success for [1234567]', 1234567, N'John', N'Another' ),
( 32043835, N'2023-05-20T20:03:34', N'Ingress', 116, N'Success for [1234567]', 1234567, N'John', N'Another' ),
( 32043945, N'2023-05-20T21:25:56', N'Egress', 117, N'Success for [1234567]', 1234567, N'John', N'Another')
I would like the data to be returned looking like this:
CardNumber CardReaderLocation DateTimeOfCardTap CardReaderLocation DateTimeOfCardTap
1234567 In 2023-05-20 11:22:24 Out 2023-05-20 12:35:30
1234567 In null Out 2023-05-20 12:43:58
1234567 In 2023-05-20 13:13:34 Out 2023-05-20 16:24:58
1234567 In 2023-05-20 16:34:36 Out null
1234567 In 2023-05-20 20:03:34 Out 2023-05-20 21:25:56
There is no entry card tap on the second row with an exit time of 12:43:58, and similarly, there is no corresponding exit time on row four with the entry time of 16:34:36, which is logically correct, as this person tailgated another person who was in front of them where the are nulls are recorded.
I have had a go at trying to create the query, and the closest that I was able to get to was by using the code as posted by a user on YouTube called SQL Server 101:
;WITH myTable AS (
SELECT *,
CASE WHEN DATEPART(day, DateTimeOfCardTap) <> LAG(DATEPART(day, DateTimeOfCardTap), 1) OVER (ORDER BY CardNumber)
OR [CardReaderLocation] = 'Ingress' AND LAG([CardReaderLocation], 1) OVER (ORDER BY CardNumber) <> 'Ingress'
THEN 1 ELSE 0 END AS [PartitionStart]
FROM #temptable),
myTable2 AS (
SELECT *, SUM(PartitionStart) OVER (ORDER BY CardNumber) AS [PartitionNumber]
, CASE WHEN [CardReaderLocation] = 'Ingress' THEN DateTimeOfCardTap END AS [InTime]
, CASE WHEN [CardReaderLocation] = 'Egress' THEN DateTimeOfCardTap END AS [OutTime]
FROM myTable)
SELECT CardNumber,
PartitionNumber,
--cardreaderlocation,
case when [CardReaderLocation] = 'Ingress' and PartitionStart = 0 then 'Ingress' end as [CardReaderLocation],
InTime AS [InTimeStart],
case when [CardReaderLocation] = 'Egress' and PartitionStart = 0 then 'Egress' end as [CardReaderLocation],
OutTime AS [OutTimeLast]
FROM myTable2
group by CardNumber, DateTimeOfCardTap, PartitionNumber, PartitionStart, CardReaderLocation, InTime, OutTime
Which returns this result set (apologies for the non-aligned formatting):
CardNumber PartitionNumber CardReaderLocation InTimeStart CardReaderLocation OutTimeLast
1234567 2 Ingress 2023-05-20 11:22:24.000 NULL NULL
1234567 2 NULL NULL Egress 2023-05-20 12:35:30.000
1234567 2 NULL NULL Egress 2023-05-20 12:43:57.000
1234567 2 NULL 2023-05-20 13:13:34.000 NULL NULL
1234567 2 NULL NULL Egress 2023-05-20 16:24:58.000
1234567 2 NULL 2023-05-20 16:34:36.000 NULL NULL
1234567 2 Ingress 2023-05-20 20:03:34.000 NULL NULL
1234567 2 NULL NULL Egress 2023-05-20 21:25:56.000
It's very close to what I would like to achieve in that all card taps appear just the once, but the entry and corresponding exit card taps do not line up on one row, and there are too many nulls where the correct number should be two (a null for the entry card tap on row two, and a null for the exit card tap on row four).
What sort of query would achieve the data returned in the first result set?
Thank you very much in advance for your help,
Andy