your query doesn't mention Shift Change, instead it has 'Not In ("Log Out","Log In","New Job")'. So I am confused about what you really require. Also you are applying a date criteria to shortdate, which is derived from log_event_time - would be more efficient to apply the criteria to that field instead. Further your criteria is in UK format, for sql you need to use the US format of mm/dd/yyyy. Sqll is clever enough to know there are not 29 months, so corrects it. But if the day is less than 12 it will be interpreted as US (i.e. a day of 1/6/2019 will be interpreted as 6th Jan, not 1st June). Suggest you need to research and understand the date datatype, and then understand the difference between the format function and the format property.
Based on your original requirement I found this was the fastest - took 40 seconds on my machine for the whole dataset.
Code:
SELECT *
FROM (SELECT A.idx, A.description, A.log_event_time, A.EventName
FROM tblTest1 AS A
WHERE (SELECT TOP 1 eventname FROM tblTest1 WHERE [description]=a.[description] AND log_event_time>a.log_event_time ORDER BY log_event_time, eventname desc)='Shift Change'
UNION ALL SELECT A.idx, A.description, A.log_event_time, A.EventName
FROM tblTest1 AS A
WHERE (SELECT TOP 1 eventname FROM tblTest1 WHERE [description]=a.[description] AND log_event_time<a.log_event_time ORDER BY log_event_time desc, eventname desc)='Shift Change'
UNION ALL SELECT A.idx, A.description, A.log_event_time, A.EventName
FROM tblTest1 AS A WHERE eventname='Shift Change') AS B
ORDER BY B.description, B.log_event_time;
You have other fields which may be a better choice - numerical equivalents log-idx seems consistent with eventdate, although you have some data errors around 2/11/2018 and 29/01/2019 (perhaps ignore those without a log event name?, you know your data) and using log_event_type rather than eventname. Numbers take up less space than text so indexes are smaller and therefore faster. If you could work off log_idx by tidying up the data you would have a much faster query - this for example to 6 seconds
Code:
SELECT A.idx, B.log_idx, B.description, B.log_event_time, B.EventName
FROM tblTest1 AS A INNER JOIN tblTest1 AS B ON A.description = B.description
WHERE (((B.log_idx) Between [a].[log_idx]-1 And [a].[log_idx]+1) AND ((A.log_event_type)=5))
ORDER BY B.description, B.log_event_time
and this with a modified join took 4 seconds
Code:
SELECT A.idx, B.log_idx, B.description, B.log_event_time, B.EventName
FROM tblTest1 AS A INNER JOIN tblTest1 AS B ON A.description = B.description AND (B.log_idx Between [a].[log_idx]-1 And [a].[log_idx]+1)
WHERE A.log_event_type=5
ORDER BY B.description, B.log_event_time
for both of these I added the necessary indexes
So it may be worth your while spending a bit of time correcting your duplicate log_idx fields and having an 'early warning' audit query of some sort to check for duplicates as new data is added.
depends on whether this a full report requirement or a step toward it. If the latter, remove sorting from the main query, it will be quicker because it is one less thing to do. You sort at the final stage when there is least amount of rows to sort. - typically in the report
And as mentioned before, anything you can do to reduce the dataset will help performance. For example limiting to a single machine reduced the first query to 6 seconds and the other two to less than a second.