【问题标题】:Improve Query Performance of This SQL提高此 SQL 的查询性能
【发布时间】:2016-01-02 13:34:15
【问题描述】:

我想提高这个查询的性能,现在需要 13 秒才能返回结果。

主表有超过 2,000,000 条记录。结果仅包含 34 条记录。这些表在r.date_gps 上有索引。

SELECT
    r.id,
    c.name,
    CONCAT(v.title, '', v.plaque) AS title,v.type_vehicle,
    r.sn,
    r.lat,
    r.lng,
    r.direction,
    r.date_db,
    r.date_gps,
    r.volt_main,
    r.speed,
    r.ing,
    t.sat_fix,
    r.sat
FROM registers r
JOIN trackers t ON t.cod = r.sn
JOIN installations i ON t.id = i.trackers_id
JOIN vehicles v ON v.id = i.vehicles_id
JOIN clients c ON c.id = v.clients_id
WHERE r.date_gps = (
    SELECT MAX(rr.date_gps) FROM registers rr WHERE r.sn = rr.sn
)
AND c.management_id = p_management
GROUP BY r.sn
ORDER BY r.date_gps DESC;

【问题讨论】:

  • 你检查解释计划了吗?
  • 请在问题中添加查询执行计划。

标签: mysql sql mariadb


【解决方案1】:

很遗憾Mysql 不支持Row_Number 窗口函数

尝试将sub-query 重写为INNER JOIN

SELECT r.id, 
       c.NAME, 
       Concat(v.title, '', v.plaque) AS title, 
       v.type_vehicle, 
       r, 
       sn, 
       .....
FROM   registers r 
       JOIN trackers t 
         ON t.cod = r.sn 
       JOIN installations i 
         ON t.id = i.trackers_id 
       JOIN vehicles v 
         ON v.id = i.vehicles_id 
       JOIN clients c 
         ON c.id = v.clients_id 
            AND c.management_id = p_management 
       INNER JOIN (SELECT Max(rr.date_gps) AS date_gps, 
                          sn 
                   FROM   registers rr
                   GROUP BY sn) a 
               ON r.sn = a.sn 
                  AND r.date_gps = a.date_gps 
GROUP  BY r.sn 
ORDER  BY r.date_gps DESC; 

【讨论】:

  • 我不清楚聚合整个registers 表而不是只查找匹配的行将如何提高性能。收到 OP 的回复会很有趣。
【解决方案2】:

在考虑提高查询性能时,可以从 FROMWHERE 子句着手:

FROM registers r JOIN 
     trackers t
     ON t.cod = r.sn JOIN
     installations i
     ON t.id = i.trackers_id JOIN
     vehicles v
     ON v.id = i.vehicles_id JOIN
     clients c
     ON c.id = v.clients_id
WHERE r.date_gps = (SELECT MAX(rr.date_gps) FROM registers rr WHERE r.sn = rr.sn
                   ) AND
      c.management_id = p_management
GROUP BY r.sn
ORDER BY r.date_gps DESC;

这些建议索引clients(management_id, id)vehicles(clients_id, id)installations(vehicles_id, trackers_id)trackers(trackers_id, cod)registers(sn, date_gps)

这将(本质上)鼓励优化器实现从clients 表开始的连接,过滤management_id。然后连接将只使用索引,这部分查询应该很快。当然,如果您有数千或数百万个匹配项,那么 GROUP BY 仍然会减慢查询速度(并且 MySQL 几乎没有选项可以提高 GROUP BY 的性能)。

【讨论】:

    【解决方案3】:

    要使用此类数据加快查询速度,首先必须缩小主表。有时 2 个查询比 1 个更好。

    尝试先获取您的 WHERE 数据

    SELECT MAX(rr.date_gps) FROM registers rr WHERE r.sn = rr.sn
    

    然后使用结果,将其添加到主查询中。

    这里也可以先使用子查询,然后再加入其他表。

    例如

    SELECT registers.*
    FROM (
        SELECT registers.id
        FROM registers
        WHERE <My WHERE Conditions>
    ) AS registers_sub
    
    INNER JOIN registers 
    ON registers_sub.id = registers.id
    

    然后执行其余的查询。因此,当您进行大量联接和分组时,您会得到少量数据。

    最后一个提示,如果你不需要所有东西,不要在 SELECT 段中使用 * ;D

    问候

    【讨论】:

      【解决方案4】:

      感谢大家在尝试提高查询性能方面的帮助。

      VR46,已将查询重写为这种格式,但主银行继续缓慢。

      ZFNerd,原来也分为两个查询,出现同样的慢。 Gordon Linoff,这些字段已编入索引。

      我有一个使用另一个字段的查询来获取最新的,但这不是我想要的,因为某些设备首先在最旧的数据之后发送当前数据,因此最终无法找到最新的记录。 但有些可以配置为最早先发送。 我想现在我会使用这种格式,因为更新速度很快,问题也很少。

      SELECT c.name,CONCAT(v.title,' ',v.plaque) AS title,v.type_vehicle,r.id,
      r.sn,r.lat,r.lng, r.direction,r.date_db,r.date_gps,r.volt_main,r.speed,
      r.ign,r.sat_fix,r.sat
      FROM registers r
      JOIN trackers t ON t.cod = r.sn
      JOIN installations i ON t.id = i.trackers_id
      JOIN vehicles v ON v.id = i.vehicles_id
      JOIN clients c ON c.id = v.clients_id
      JOIN (
          SELECT rr.sn, MAX(rr.id) AS id FROM registers rr GROUP BY sn
      ) AS L2 ON r.sn = L2.sn AND r.id = L2.id
      AND c.management_id = p_management
      GROUP BY r.sn;
      

      【讨论】:

        【解决方案5】:

        虽然其他查询可能通过 select max() group by 有所帮助,但您最好选择不同的路线。您似乎正在跟踪运输某种类型货物的车辆,并希望获得任何给定点的最新位置。

        更新“跟踪器”表并为 LastGPSDate 添加一列可能会更好。然后,在插入 Date_GPS 表时通过触发器,向相应的 Trackers.LastGPSDate 发出更新,其中 COD = 您插入的 gps.sn(基于您的 join 子句)。然后您的查询将被简化为

        SELECT
              r.id,
              c.name,
              CONCAT(v.title, '', v.plaque) AS title,v.type_vehicle,
              r.sn,
              r.lat,
              r.lng,
              r.direction,
              r.date_db,
              r.date_gps,
              r.volt_main,
              r.speed,
              r.ing,
              t.sat_fix,
              r.sat
           FROM 
              clients c 
                 JOIN vehicles v 
                    ON c.id = v.clients_id
                    JOIN installations i 
                      ON v.id = i.vehicles_id
                      join trackers t 
                         ON i.trackers_id = t.id
                         JOIN registers r 
                            ON t.cod = r.sn
                           AND t.LastGPSDate = r.date_gps
           WHERE
              c.management_id = p_management
           ORDER BY 
              t.LastGPSDate DESC;
        

        我颠倒了表格的顺序以更好地匹配您要查找的内容。通过管理 ID 从一个具有特定位置的客户端开始。然后,只有他们的车辆才能安装、跟踪并最终注册。

        然后我会建议以下索引 表索引 客户 ( management_id, id ) 车辆(id,clients_id) 安装(trackers_id、vehicles_id) 跟踪器(LastGPSDate、cod、id、sat_fix) 寄存器 (sn, date_gps)

        由于 join 子句专门限制了一个管理 ID,我将其放在索引的第一个位置。

        因此,在 2+ 百万条记录中,您的数据的 BULK 可能是 20、30、100 个不同的管理 ID,为什么每次运行时都会生成应用于所有寄存器的 MAX。通过触发器每次标记日期/时间一次,您可以更快地获取数据,并且只针对您当时想要的那些客户。

        【讨论】:

          猜你喜欢
          • 2019-03-20
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-05-26
          相关资源
          最近更新 更多