SQL Server query optimization?

2019-05-11 14:59发布

问题:

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? :)

回答1:

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


回答2:

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.



回答3:

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.



回答4:

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.

  1. Do you have primary keys and foreign key constraints
  2. 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


回答5:

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).