使用 FROM 子句中的派生表进行慢速查询

问题描述

我已经尝试了几个小时来了解这个 SQL 查询发生了什么。

查询目标:列出在给定城市周围 6 公里范围内发生的所有事件。

在所有评论(谢谢)之后,我使用 CTE 移到了下面的查询,删除了一个无用的 DISTINCT 并使用了 JOIN

之前

    SELECT DISTINCT c.id AS cours_id,GROUP_CONCAT(DISTINCT cl.type) AS type_loc,GROUP_CONCAT(DISTINCT CONCAT(cl.home,'-',cl.e)) AS home_loc,MIN(ROUND(pld.distance)) AS distance,pld.loc2_id AS distance_loc,cl.e,ROUND(pld.distance),pld.loc2_id)) AS all_loc
 FROM cours2 c,cours_locations cl,locations_locations ll,(SELECT dest.id AS loc2_id,1000 * 6371.03 * 2 * 
       ASIN(SQRT( POWER(SIN((orig.latitude - ABS(dest.latitude)) * 
       PI()/180 / 2),2) + COS(orig.latitude * 
       PI()/180 ) * COS(ABS(dest.latitude) * 
       PI()/180) * POWER(SIN((orig.longitude - dest.longitude) * 
       PI()/180 / 2),2) )) AS distance 
       FROM locations orig,locations dest 
       WHERE orig.id = 14861 
       AND (dest.type='V' OR dest.type='A') 
       AND dest.latitude BETWEEN orig.latitude - (6000 / 1000 / 111.045) AND orig.latitude + (6000 / 1000 / 111.045) 
       AND dest.longitude BETWEEN orig.longitude - (6000 / 1000 / (111.045 * COS(RADIANS(orig.latitude)))) 
       AND orig.longitude + (6000 / 1000 / (111.045 * COS(RADIANS(orig.latitude)))) 
       HAVING distance <= (6000 + 1000)) pld
 WHERE
     c.active_today = '1' AND
     c.id = cl.cours_id AND
     cl.domain = '4' AND
     c.subject_id = 404 AND
     cl.location_id = ll.rel_loc_id AND
     ll.location_id = pld.loc2_id
 GROUP BY c.id;

之后

WITH location_distances (loc_id,distance) AS (
    SELECT dest.id AS loc_id,1000 * 6371.03 * 2 * ASIN(SQRT( POWER(SIN((orig.latitude - ABS(dest.latitude)) * PI()/180 / 2),2) + COS(orig.latitude * PI()/180 ) * COS(ABS(dest.latitude) * PI()/180) * POWER(SIN((orig.longitude - dest.longitude) * PI()/180 / 2),2) )) AS distance
        FROM locations orig,locations dest
        WHERE orig.id = 14861
          AND (dest.type='V' OR dest.type='A')
          AND dest.latitude BETWEEN orig.latitude - (6000 / 1000 / 111.045) AND orig.latitude + (6000 / 1000 / 111.045)
          AND dest.longitude BETWEEN orig.longitude - (6000 / 1000 / (111.045 * COS(RADIANS(orig.latitude)))) AND orig.longitude + (6000 / 1000 / (111.045 * COS(RADIANS(orig.latitude))))
        HAVING distance < 7000
)
SELECT c.id AS cours_id,pld.loc_id)) AS all_loc
    FROM cours2 c
             JOIN cours_locations cl ON cl.domain = '4' AND c.id = cl.cours_id
             JOIN locations_locations ll ON cl.location_id = ll.location_id AND ll.up_propagation = 0
             JOIN location_distances pld ON ll.rel_loc_id = pld.loc_id
    WHERE c.active_today = '1'
      AND c.subject_id = 404
    GROUP BY c.id;

执行时间没有改变(甚至略有增加):大约 15 秒。

CTE 部分本身在 290 毫秒内运行并返回 4 行。这部分是关于寻找距离城市id 14681 6km 的城市。

然而,奇怪的是:

  • 如果我从 CTE 中删除 HAVING 子句,它仍会在 290 毫秒内运行 4 行,但这次全局查询需要 60 秒而不是 15 秒。
  • 如果我将 LIMIT 5 添加到 CTE,全局查询会下降到 300 毫秒而不是 15 秒。
  • 如果我添加一个 LIMIT 15(而行数是 4),它会下降到 60 秒。 这些观察结果可以在有或没有 CTE、有或没有 JOIN 的情况下复制。

数据库服务器版本为 10.3.27-MariaDB

桌子尺寸: cours2 c(事件列表):15K cours_locations cl(事件发生的区域):45K location_locations ll(区域层次结构 - 从一个地区移动到一个城市):310K location_distances pld(输入城市周围 6km 的计算城市):5

这是解释结果:

|id|select_type|table|type|possible_keys|key|key_len|ref|rows|Extra|
|--|--------------|--------------|--------------|-------------|----------------|--------|------------|---------|---------|
|1|PRIMARY|c|ref|ID,cours2_active_today_id,cours2_active_today_domain_display_home_update_id,cours2_active_today_subject_id_id,cours2_active_today_subject_id_domain_lang_id,cours2_active_today_subject_id_lang_publish_end_id,cours2_active_today_lang_priv_loc_MOVE_subject_id_id,cours2_active_today_lang_subject_id_id,cours2_active_today_lang_priv_loc_ADR_subject_id_id,cours2_active_today_lang_priv_loc_WEBCAM_subject_id_id,cours2_subject_id_lang_id_index|cours2_active_today_subject_id_domain_lang_id|5|const,const|179|Using where; Using index; Using filesort|
|1|PRIMARY|cl|ref|PRIMARY,domain,location_id,cours_locations_domain_home_cours_id_index|PRIMARY|27|db.c.id,const|2|Using where; Using index|
|1|PRIMARY|ll|ref|PRIMARY,rel_loc_id|rel_loc_id|4|db.cl.location_id|7|""|
|1|PRIMARY|<derived2>|ref|key0|key0|4|db.ll.location_id|10|""|
|2|DERIVED|orig|const|PRIMARY|PRIMARY|4|const|1|""|
|2|DERIVED|dest|range|locations_type_D_id,locations_type_longitude_latitude|locations_type_longitude_latitude|9||1174|Using index condition; Using where|

编辑: 这是一个总运行时间为 300 毫秒的 2 CTE 版本。这真的有助于 MariaDB 找到合适的 JOIn 优化...

WITH
    location_distances (loc_id,distance) AS (
        SELECT dest.id AS loc_id,ROUND(1000 * 6371.03 * 2 * ASIN(SQRT( POWER(SIN((orig.latitude - ABS(dest.latitude)) * PI()/180 / 2),2) ))) AS distance
            FROM locations orig,locations dest
            WHERE orig.id = 14861
              AND (dest.type='V' OR dest.type='A')
              AND dest.latitude BETWEEN orig.latitude - (6000 / 1000 / 111.045) AND orig.latitude + (6000 / 1000 / 111.045)
              AND dest.longitude BETWEEN orig.longitude - (6000 / 1000 / (111.045 * COS(RADIANS(orig.latitude)))) AND orig.longitude + (6000 / 1000 / (111.045 * COS(RADIANS(orig.latitude))))
            HAVING distance < 7000
    ),location_distances_hierarchy (parent_loc_id,loc_id,distance) AS (
        SELECT DISTINCT ll.location_id,ld.loc_id,ld.distance
            FROM locations_locations ll,location_distances ld
            WHERE ll.rel_loc_id = ld.loc_id
    )
SELECT c.id AS cours_id,GROUP_CONCAT(DISTINCT CONCAT(cl.type,cl.home,ldh.distance,ldh.loc_id)) AS all_loc
    FROM cours2 c
        JOIN cours_locations cl ON cl.domain = '4' AND c.id = cl.cours_id
        JOIN location_distances_hierarchy ldh ON cl.location_id = ldh.parent_loc_id
    WHERE c.active_today = '1'
      AND c.subject_id = 404
    GROUP BY c.id;

新的解释结果:

|id|select_type|table|type|possible_keys|key|key_len|ref|rows|Extra|
|1|PRIMARY|ss|ref|PRIMARY,subject_id|PRIMARY|4|const|1|Using index; Using temporary; Using filesort|
|1|PRIMARY|c|ref|ID,trouver-un-cours.ss.rel_subject_id|30|Using where; Using index|
|1|PRIMARY|caf|eq_ref|PRIMARY|PRIMARY|26|trouver-un-cours.c.id|1|""|
|1|PRIMARY|cl|ref|PRIMARY,cours_locations_domain_home_cours_id_index|PRIMARY|27|trouver-un-cours.c.id,const|2|Using where; Using index|
|1|PRIMARY|<derived3>|ref|key0|key0|4|trouver-un-cours.cl.location_id|10|Using where|
|3|DERIVED|<derived2>|ALL|||||1174|Using temporary|
|3|DERIVED|ll|ref|rel_loc_id|rel_loc_id|4|ld.loc_id|7|""|
|2|DERIVED|orig|const|PRIMARY|PRIMARY|4|const|1|""|
|2|DERIVED|dest|range|locations_type_D_id,locations_type_longitude_latitude|locations_type_longitude_latitude|9||1174|Using index condition; Using where|

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)