如何使用常量优化MySQL的查询?

时间:2008-11-22 22:55:59

标签: mysql optimization sql-execution-plan

注意:原始问题没有实际意义,但扫描到底部是否有相关内容。

我想要优化的查询看起来像这样:

select cols from tbl where col = "some run time value" limit 1;

我想知道正在使用哪些键,但无论我传递什么来解释,它都能够将where子句优化为什么(“Impossible WHERE注意到......”)因为我给它一个常量。

  • 有没有办法告诉mysql不要在explain中进行常量优化?
  • 我错过了什么吗?
  • 有没有更好的方法来获取我需要的信息?

编辑:EXPLAIN似乎在给我一个由常量值产生的查询计划。由于查询是存储过程的一部分(并且sproc中的IIRC查询计划在调用之前生成),这对我没有好处,因为该值不是常量。我想要的是找出优化器在不知道实际值是什么时将生成什么查询计划。

我错过了什么吗?

Edit2:在其他地方询问,似乎MySQL总是重新生成查询计划,除非你不遗余力地让它重复使用它们。即使在存储过程中。从这看起来我的问题似乎没有实际意义。

然而,这并不是我真正想知道的事情: 如何优化包含任何特定查询中常量的值的查询,但我,程序员,事先不知道将使用什么值? - 例如说我的客户端代码生成一个查询,其中包含where子句中的数字。有时这个数字会导致不可能where where 其他时候它不会。如何使用explain来检查查询的优化程度?

我正在看到的最好的方法是在其上运行EXPLAIN以获得存在/不存在情况的完整矩阵。真的,这不是一个非常好的解决方案,因为手工操作既容易又容易出错。

3 个答案:

答案 0 :(得分:5)

你得到“不可能注意到”,因为你指定的值不在列中,而不仅仅是因为它是一个常量。您可以1)使用列中存在的值或2)只说col = col

explain select cols from tbl where col = col;

答案 1 :(得分:4)

  

例如,假设我的客户端代码正在生成一个带有数字的查询,其中包含where子句。

     

有时候,这个数字会导致一个不可能的where子句,有时则不会。

     

如何使用explain来检查查询的优化程度?

MySQL为绑定参数的不同值构建不同的查询计划。

在此article中,您可以阅读MySQL优化程序何时执行以下操作的列表:

    Action                                      When

    Query parse                                 PREPARE
    Negation elimination                        PREPARE
    Subquery re-writes                          PREPARE

    Nested JOIN simplification                  First EXECUTE
    OUTER->INNER JOIN conversions               First EXECUTE

    Partition pruning                           Every EXECUTE
    COUNT/MIN/MAX elimination                   Every EXECUTE
    Constant subexpression removal              Every EXECUTE
    Equality propagation                        Every EXECUTE
    Constant table detection                    Every EXECUTE
    ref access analysis                         Every EXECUTE
    range/index_merge analysis and optimization Every EXECUTE
    Join optimization                           Every EXECUTE

此列表中还缺少一件事。

MySQL可以在JOIN次迭代上重建一个查询计划:这样称为range checking for each record

如果表上有复合索引:

CREATE INDEX ix_table2_col1_col2 ON table2 (col1, col2)

和这样的查询:

SELECT  *
FROM    table1 t1
JOIN    table2 t2
ON      t2.col1 = t1.value1
        AND t2.col2 BETWEEN t1.value2_lowerbound AND t2.value2_upperbound

MySQL不会使用RANGE(t1.value1, t1.value2_lowerbound)的索引(t1.value1, t1.value2_upperbound)访问权限。相反,它将使用REF上的索引(t1.value)访问权限,只过滤掉错误的值。

但是如果你重写这样的查询:

SELECT  *
FROM    table1 t1
JOIN    table2 t2
ON      t2.col1 <= t1.value1
        AND t2.col1 >= t2.value1
        AND t2.col2 BETWEEN t1.value2_lowerbound AND t2.value2_upperbound

,然后MySQL将重新检查RANGE 中每条记录的索引table1访问,并决定是否即时使用RANGE访问权限

您可以在我的博客中阅读这些文章:

所有这些都使用RANGE CHECKING FOR EACH RECORD

回到你的问题:无法确定MySQL将为每个给定常量使用哪个计划,因为在给出常量之前没有计划。

不幸的是,没有办法强制MySQL对绑定参数的每个值使用一个查询计划。

您可以使用JOININDEX条款来控制所选的STRAIGHT_JOIN订单和FORCE INDEX,但它们不会强制索引上的某个访问路径或禁止IMPOSSIBLE WHERE

另一方面,对于所有JOINMySQL仅使用NESTED LOOPS。这意味着,如果您构建正确的JOIN订单或选择正确的索引,MySQL可能会受益于所有IMPOSSIBLE WHERE

答案 2 :(得分:0)

  

如何使用仅对查询保持不变的值来优化查询,但是我,程序员,事先不知道将使用哪个值?

通过在特定列上使用索引(如果始终一起查询给定列,甚至可以使用列组合)。如果您有索引,查询计划程序将可能使用它们。

关于“不可能”的值:查询计划程序可以从几个来源推断出给定值不在表中:

  • 如果特定列上有索引,则可以观察到特定值大于或小于索引中的任何值(最小/最大值需要从索引中提取的恒定时间)
  • 如果传入错误的类型(如果要求数字列与文本相同)

PS。通常,创建查询计划并不昂贵,重新创建比重新使用它们更好,因为自生成查询计划以来可能已更改条件并且可能存在更好的查询计划。