简体   繁体   English

避免在内部选择中进行全表扫描

[英]Avoid full table scan in inner select

I have the following select in MySQL, which produces the right results but it takes unnecessarily long to execute: 我在MySQL中选择了以下内容,它会产生正确的结果,但是执行起来会花费不必要的时间:

SELECT tblGPSDevices.Email, tblLoc.Lat, tblLoc.Lon, tblLoc.Radius, tblLoc.CreationTimeStamp, tblTrackedUsers.ID, tblTrackedUsers.TrackerDeviceID, tblTrackedUsers.TrackedDeviceID
    FROM tblTrackedUsers
    INNER JOIN tblGPSDevices ON tblTrackedUsers.TrackedDeviceID = tblGPSDevices.ID
    LEFT OUTER JOIN (
            SELECT A.DeviceID, A.Lat, A.Lon, A.Radius, A.CreationTimeStamp, A.ID 
            FROM tblLocations A 
            INNER JOIN (
                    SELECT DeviceID, MAX(CreationTimeStamp) AS CreationTimeStamp, MAX(ID) AS ID
                    FROM tblLocations
                    GROUP BY DeviceID
            ) AS B ON A.DeviceID = B.DeviceID
                AND A.CreationTimeStamp = B.CreationTimeStamp
                AND A.ID = B.ID                         
    ) AS tblLoc ON tblLoc.DeviceID = tblGPSDevices.ID
    WHERE tblGPSDevices.Validated = 0x01
    AND tblGPSDevices.Enabled = 0x01
    AND tblTrackedUsers.Validated = 0x01
    AND tblTrackedUsers.TrackerDeviceID = 1
    ORDER BY tblTrackedUsers.ID;

This query runs much slower than it should because it does a full table scan on tblLocations. 该查询的运行速度比正常情况要慢得多,因为它会对tblLocations进行全表扫描。

This is the part that really slows down the query: 这是真正减慢查询速度的部分:

SELECT A.DeviceID, A.Lat, A.Lon, A.Radius, A.CreationTimeStamp, A.ID 
        FROM tblLocations A 
        INNER JOIN (
            SELECT DeviceID, MAX(CreationTimeStamp) AS CreationTimeStamp, MAX(ID) AS ID
            FROM tblLocations
            GROUP BY DeviceID
        ) AS B ON A.DeviceID = B.DeviceID
            AND A.CreationTimeStamp = B.CreationTimeStamp
            AND A.ID = B.ID 

Here is the explain plan: 这是解释计划:

id  select_type     table   type    possible_keys   key     key_len     ref     rows    Extra
1   PRIMARY     tblTrackedUsers     ref     TrackerDeviceID,TrackedDeviceID     TrackerDeviceID     9   const   14  Using where; Using temporary; Using filesort
1   PRIMARY     tblGPSDevices   eq_ref  PRIMARY     PRIMARY     8   tblTrackedUsers.TrackedDeviceID     1   Using where
1   PRIMARY     <derived2>  ALL     NULL    NULL    NULL    NULL    2073    
2   DERIVED     <derived3>  ALL     NULL    NULL    NULL    NULL    2073    
2   DERIVED     A   eq_ref  PRIMARY,DeviceID,CreationTimeStampIndex     PRIMARY     8   B.ID    1   Using where
3   DERIVED     tblLocations    index   NULL    DeviceID    8   NULL    174058  

It does a full table scan on tblLocations, even though I only need s small subset of DeviceID's in that table. 即使我只需要该表中DeviceID的一小部分,它也会对tblLocations进行全表扫描。

I just need to look at the DeviceID's that are returned from this part: 我只需要查看从此部分返回的DeviceID:

INNER JOIN tblGPSDevices ON tblTrackedUsers.TrackedDeviceID = tblGPSDevices.ID
WHERE tblTrackedUsers.TrackerDeviceID = 1

But unfortunately tblTrackedUsers.TrackedDeviceID is not visible in the inner select. 但是很遗憾,tblTrackedUsers.TrackedDeviceID在内部选择中不可见。 So if I add 所以如果我加

WHERE DeviceID = tblTrackedUsers.TrackedDeviceID

right above 正上方

GROUP BY DeviceID

It does not work. 这是行不通的。

How can I go about optimizing this query? 我该如何优化此查询?

Structure of the tables involved with the relevant fields only: 仅涉及相关字段的表的结构:

tblGPSDevices: tblGPSDevices:


ID | ID | Email | 电邮| Validated | 已验证| Enabled 已启用


tblLocations: tbl位置:


ID | ID | DeviceID | 设备ID | Lat | 纬度| Lon | 隆| Radius | 半径| CreationTimeStamp CreationTimeStamp


tblTrackedUsers: tblTrackedUsers:


ID | ID | TrackerDeviceID | TrackerDeviceID | TrackedDeviceID | TrackedDeviceID | Validated 已验证


tblLocations.DeviceID, tblTrackedUsers.TrackerDeviceID and tblTrackedUsers.TrackedDeviceID are foreign keys pointing to tblGPSDevices.ID tblLocations.DeviceID,tblTrackedUsers.TrackerDeviceID和tblTrackedUsers.TrackedDeviceID是指向tblGPSDevices.ID的外键

What this query does: 该查询的作用:

The query should return all devices from tblGPSDevices that are being tracked by the user and their last location from tblLocations. 该查询应返回用户正在跟踪的tblGPSDevices中的所有设备,以及tblLocations中它们的最后位置。 The way to determine which devices are being tracked by a user is determined by tblTrackedUsers: select TrackedDeviceID from tblTrackedUsers where TrackerDeviceID = some_value 确定用户正在跟踪哪些设备的方法由tblTrackedUsers确定:从tblTrackedUsers中选择TrackedDeviceID,其中TrackerDeviceID = some_value

I did find the answer myself and I will post for future reference. 我确实找到了答案,并将发布以供将来参考。 This sped up the query from 2 sec. 从2秒开始,查询速度加快了。 to 0.0179 sec. 至0.0179秒 That is an enormous gain. 那是巨大的收获。

The key was to add one more inner select within: 关键是在其中添加一个内部选择:

SELECT DeviceID, MAX(CreationTimeStamp) AS CreationTimeStamp, MAX(ID) AS ID
FROM tblLocations
GROUP BY DeviceID

in order to avoid a full table scan, since we are only interested in DeviceID's that are = to tblTrackedUsers.TrackedDeviceID. 为了避免全表扫描,因为我们只对tblTrackedUsers.TrackedDeviceID =的DeviceID感兴趣。 Now this select looks like this: 现在,此选择如下所示:

 SELECT C.DeviceID, MAX(C.CreationTimeStamp) AS CreationTimeStamp, MAX(C.ID) AS ID
 FROM tblLocations C
         INNER JOIN (
              SELECT ID, TrackerDeviceID, TrackedDeviceID,  TrackedName, AccessCode, Validated FROM tblAllowedUsers WHERE TrackerDeviceID = 1 AND Validated=0x01
          ) AS D ON D.TrackedDeviceID = C.DeviceID
  GROUP BY DeviceID

Here is the full select now: 现在是全部选择:

SELECT tblGPSDevices.Email, tblLoc.Lat, tblLoc.Lon, tblLoc.Radius, tblLoc.CreationTimeStamp, tblTrackedUsers.ID, tblTrackedUsers.TrackerDeviceID, tblTrackedUsers.TrackedDeviceID
    FROM tblTrackedUsers
    INNER JOIN tblGPSDevices ON tblTrackedUsers.TrackedDeviceID = tblGPSDevices.ID
    LEFT OUTER JOIN (
            SELECT A.DeviceID, A.Lat, A.Lon, A.Radius, A.CreationTimeStamp, A.ID 
            FROM tblLocations A 
            INNER JOIN (
                SELECT C.DeviceID, MAX(C.CreationTimeStamp) AS CreationTimeStamp, MAX(C.ID) AS ID
            FROM tblLocations C
                INNER JOIN (
                    SELECT ID, TrackerDeviceID, TrackedDeviceID,  TrackedName, AccessCode, Validated FROM tblAllowedUsers WHERE TrackerDeviceID = 1 AND Validated=0x01
                ) AS D ON D.TrackedDeviceID = C.DeviceID
            GROUP BY DeviceID
            ) AS B ON A.DeviceID = B.DeviceID
                AND A.CreationTimeStamp = B.CreationTimeStamp
                AND A.ID = B.ID                         
    ) AS tblLoc ON tblLoc.DeviceID = tblGPSDevices.ID
    WHERE tblGPSDevices.Validated = 0x01
    AND tblGPSDevices.Enabled = 0x01
    AND tblTrackedUsers.Validated = 0x01
    AND tblTrackedUsers.TrackerDeviceID = 1
    ORDER BY tblTrackedUsers.ID;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM