I have the following query:
SELECT
tl.*, d.*
FROM
TrackerLocations AS tl
inner join Trackers t on tl.TrackerId = t.TrackerId
inner join Devices d on t.UserId = d.UserId
WHERE
tl.ReceivedTime = (SELECT MAX(tl2.ReceivedTime) FROM TrackerLocations tl2 WHERE tl2.TrackerId = tl.TrackerId)
and tl.ReceivedTime >= DATEADD (MINUTE,-2,GETUTCDATE())
and d.OSType <> 3
and d.Notify = 1
...and for my surprise, it does not return the results in an acceptable time. First times I runned it in production environment, was like 3 seconds of execution. Now, it runs until timeout (inside C# application, 30 seconds).
The main purpose is: "give me the most recent locations from any tracker and info about it's users's devices, looking 2 minutes backwards".
Any hints about optimizing this query?
Thanks!
Indexes:
Anywhere but where clause columns (ReceivedTime, OSType, Notify).
About the execution plan, it's a huge thing and it's not too familiar to me. Should I paste it here? :)
SELECT tl.*,d.*
FROM
TrackerLocations AS tl
inner join Trackers t on tl.TrackerId = t.TrackerId
inner join Devices d on t.UserId = d.UserId
WHERE
tl.ReceivedTime = (SELECT MAX(tl2.ReceivedTime)
FROM TrackerLocations tl2
WHERE tl2.TrackerId = tl.TrackerId
and tl2.ReceivedTime >= @searchTerm)
and d.RegistrationId <> ''
and d.OSType <> 3
and d.Notify = 1
I would suggest trying an index on (TrackerId
, ReceivedTime
) and maybe just ReceivedTime
as well. But, without profiling and an actual execution plan it's just a shot in the dark.
Create indexes on TrackerLocations.TrackerId
and Trackers.TrackerId
. Also Devices.UserId
and Trackers.UserId
for the 2nd JOIN
. Finally, look at creating an index on TrackerLocations.ReceivedTime
as well for the WHERE
clause.
In general, the first place to look to optimize queries like this is checking to see if you have indexes on your join criteria. Also, if you have some sort of filter (WHERE
clause), that could be another place to add an index.
It's hard to say exactly where you need to optimize without looking at the query execution plan. A lot of factors can feed into it, including how many records are in each table. Don't go too crazy creating indexes, though -- they have a performance impact, for example when inserting new data. Best advice is to just test with different indexes, and see if the execution plan gives you some hints.
because I don't know what the execution plan looks like for this query I can only go on what I feel would help speed up the results.
- Do you have primary keys and foreign key constraints
- Are those keys then indexed
beyond that I would try and use a CTE to limit the results being joined together something to the effect of (Not Tested):
;WITH cte_loc AS
(
SELECT
tl.*
FROM
TrackerLocations tl
WHERE
tl.ReceivedTime >= DATEADD (MINUTE,-2,GETUTCDATE())
),cte_loc2 AS
(
SELECT
TrackerId
,MAX(ReceivedTime) AS MaxReceivedTime
FROM
cte_loc
GROUP BY
TrackerId
),cte_dev AS
(
SELECT
tl.TrackerId,
d.*
FROM
cte_loc2 tl
INNER JOIN Trackers t ON tl.TrackerId = t.TrackerId
INNER JOIN Devices d ON t.UserId = d.UserId
WHERE
d.OSType <> 3 AND d.Notify = 1
)
SELECT
tl.*, d.*
FROM
cte_loc AS tl
INNER JOIN cte_loc2 tl2 ON tl.TrackerId = tl2.TrackerId
AND tl.ReceivedTime = tl2.MaxReceivedTime
LEFT JOIN cte_dev d ON tl.TrackerId = d.TrackerId
Since you are looking for the latest ReceivedTime within the last 2 minutes, you're going to want to create an index on ReceivedTime
in DESC order. The DESC order part is important. It will also help with the MAX function. You can also try an index on (ReceivedTime DESC, TrackerID)
.