【问题标题】:Optimization of query using covering indices使用覆盖索引优化查询
【发布时间】:2016-05-09 20:54:39
【问题描述】:

我有以下带有子查询和自联接的查询:

SELECT bucket.patient_sid AS sid
FROM 
(SELECT clinical_data.patient_sid, 
        clinical_data.lft, 
        clinical_data.rgt
FROM clinical_data INNER JOIN 
(SELECT clinical_data.patient_sid, 
        clinical_data.lft, 
        clinical_data.rgt, 
        clinical_data.attribute_id 
FROM clinical_data 
WHERE clinical_data.attribute_id = '33' AND clinical_data.string_value = '2160-0') AS attribute 
ON clinical_data.patient_sid = attribute.patient_sid 
    AND clinical_data.lft >= attribute.lft 
    AND clinical_data.rgt <= attribute.rgt 
WHERE clinical_data.attribute_id = '36') AS bucket;

我对此定义了以下索引:

KEY `idx_bucket` (`attribute_id`,`string_value`)
KEY `idx_self_join` (`patient_sid`,`attribute_id`,`lft`,`rgt`)

当我查看使用 EXPLAIN 的查询时,使用覆盖索引 idx_bucket 的子查询肯定是优化的,但自连接和 where 子句没有。此外,为什么它报告只有patient_sidattribute_id 用于used_key_parts,而attachment_condition 显示为lftrgt(这是什么意思?)。 lft 和 'rgt` 都只是定义为没有特殊属性的整数,那么为什么不在我的覆盖索引中使用它们呢?

更奇怪的是当我定义

KEY `idx_self_join` (`patient_sid`,`lft`,`rgt`,`attribute_id`) 

只有patient_sidused_key_parts. 中注册此外filtered11.00% 下降到1.60%

{
  "query_block": {
    "select_id": 1,
    "cost_info": {
      "query_cost": "645186.71"
    },
    "nested_loop": [
      {
        "table": {
          "table_name": "clinical_data",
          "access_type": "ref",
          "possible_keys": [
            "fk_attribute_idx",
            "idx_value_string",
            "idx_value_double",
            "idx_bucket",
            "idx_self_join_idx"
          ],
          "key": "idx_bucket",
          "used_key_parts": [
            "attribute_id",
            "string_value"
          ],
          "key_length": "308",
          "ref": [
            "const",
            "const"
          ],
          "rows_examined_per_scan": 126402,
          "rows_produced_per_join": 126402,
          "filtered": "100.00",
          "cost_info": {
            "read_cost": "126402.00",
            "eval_cost": "25280.40",
            "prefix_cost": "151682.40",
            "data_read_per_join": "46M"
          },
          "used_columns": [
            "patient_sid",
            "string_value",
            "attribute_id",
            "lft",
            "rgt"
          ],
          "attached_condition": "(`ns_large2`.`clinical_data`.`patient_sid` is not null)"
        }
      },
      {
        "table": {
          "table_name": "clinical_data",
          "access_type": "ref",
          "possible_keys": [
            "fk_attribute_idx",
            "idx_value_string",
            "idx_value_double",
            "idx_bucket",
            "idx_self_join_idx"
          ],
          "key": "idx_self_join_idx",
          "used_key_parts": [
            "attribute_id",
            "patient_sid"
          ],
          "key_length": "10",
          "ref": [
            "const",
            "ns_large2.clinical_data.patient_sid"
          ],
          "rows_examined_per_scan": 14,
          "rows_produced_per_join": 201169,
          "filtered": "11.11",
          "using_index": true,
          "cost_info": {
            "read_cost": "131327.39",
            "eval_cost": "40233.83",
            "prefix_cost": "645186.71",
            "data_read_per_join": "73M"
          },
          "used_columns": [
            "patient_sid",
            "attribute_id",
            "lft",
            "rgt"
          ],
          "attached_condition": "((`ns_large2`.`clinical_data`.`lft` >= `ns_large2`.`clinical_data`.`lft`) and (`ns_large2`.`clinical_data`.`rgt` <= `ns_large2`.`clinical_data`.`rgt`))"
        }
      }
    ]
  }
}

【问题讨论】:

    标签: mysql optimization self-join database-indexes compound-index


    【解决方案1】:

    这是您的基本 JOIN:

    SELECT
    
    FROM clinical_data cd1
    
    JOIN clinical_data cd2
        ON cd1.patient_sid = cd2.patient_sid
        AND cd2.attribute_id = '33'
    
    WHERE cd1.attribute_id = '36'
    

    【讨论】:

    • 对,但是lftrgt 定义了嵌套集的边缘,这给了我一组记录。您的查询不会产生正确的结果。我可以将 lftrgt 的范围计算放在 where 子句中,但这也可能不是最优的。
    • 为了清楚起见,我没有包括所有这些。您仍然可以添加它。或者只是片刻,我会为你服务的。
    • 完成:实际上,将这些移动到 where 子句,从执行时间缩短了大约 20 秒。
    • 你的减少比执行时间缩短了 10 秒。好的! (现在,只需弄清楚如何在 SQLAlechemy 中实现它)
    • 看看你的索引。您有一个关于属性 + sid 的索引和另一个关于属性的索引。第一个使第二个多余。但是您可能希望将第一个切换为 sid + 属性,因为 sid 具有更大的基数(即先选择 sid 比先选择属性代码更快地缩小范围。)
    【解决方案2】:

    这是我最终想到的:

    SELECT
        cd1.patient_sid as sid
    
    FROM clinical_data cd1
    
    JOIN clinical_data cd2
        ON cd1.patient_sid = cd2.patient_sid
        AND cd1.lft >= cd2.lft 
        AND cd1.rgt <= cd2.rgt 
    
    WHERE cd1.attribute_id = '36'
        AND cd2.attribute_id = '33'
        AND cd2.string_value = '2160-0'
    

    【讨论】:

    • 将 lft 和 rgt 移到 where 子句对我来说加快了很多速度。
    【解决方案3】:

    “Used_columns”表示它“覆盖”。最后的“使用过的关键部分”并不全部用作“关键”,因为它们需要在“范围”内,而不是'='。

    摆脱外部查询:

            SELECT  clinical_data.patient_sid, clinical_data.lft, clinical_data.rgt
                FROM  clinical_data
                INNER JOIN  
                  ( SELECT  clinical_data.patient_sid, clinical_data.lft, clinical_data.rgt,
                            clinical_data.attribute_id
                        FROM  clinical_data
                        WHERE  clinical_data.attribute_id = '33'
                          AND  clinical_data.string_value = '2160-0'
                  ) AS attribute  ON clinical_data.patient_sid = attribute.patient_sid
                  AND  clinical_data.lft >= attribute.lft
                  AND  clinical_data.rgt <= attribute.rgt
                WHERE  clinical_data.attribute_id = '36'
    

    抱歉,lft-rgt 架构效率不高。

    【讨论】:

    • 还有内部查询。只需 SELECT ... FROM Clinical_data cd1 INNER JOIN Clinical_data cd2。
    • 知道了。感谢您的澄清。不幸的是,我需要使用外部查询,因为这是一个嵌套的集合结构。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-01-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-15
    • 2015-05-18
    相关资源
    最近更新 更多