行构造器表达式优化

来自泡泡学习笔记
跳到导航 跳到搜索

行构造器允许同时比较多个值。例如,下面这两个语句在语义上是等价的:

SELECT * FROM t1 WHERE (column1,column2) = (1,1);
SELECT * FROM t1 WHERE column1 = 1 AND column2 = 1;

此外,优化器会以相同的方式处理这两个表达式。

如果行构造器的列没有覆盖索引的前缀,优化器可能不太可能使用可用的索引。考虑下面这个表,它在(c1, c2, c3)上有一个主键:

CREATE TABLE t1 (
c1 INT, c2 INT, c3 INT, c4 CHAR(100),
PRIMARY KEY(c1,c2,c3)
);

在这个查询中,WHERE子句使用了索引中的所有列。然而,行构造器本身没有覆盖索引前缀,因此优化器只使用了c1(key_len=4,c1的长度):

mysql> EXPLAIN SELECT * FROM t1
    WHERE c1=1 AND (c2,c3) > (1,1)\G
*************************** 1. row ***************************
        id: 1
select_type: SIMPLE
        table: t1
partitions: NULL
        type: ref
possible_keys: PRIMARY
        key: PRIMARY
    key_len: 4
        ref: const
        rows: 3
    filtered: 100.00
        Extra: Using where

在这种情况下,使用等效的非构造器表达式重写行构造器表达式可能会导致更完整的索引使用。对于给定的查询,行构造器和等效的非构造器表达式如下:

(c2,c3) > (1,1)
c2 > 1 OR ((c2 = 1) AND (c3 > 1))

将查询重写为使用非构造器表达式会导致优化器使用索引中的所有三列(key_len=12):

mysql> EXPLAIN SELECT * FROM t1
    WHERE c1 = 1 AND (c2 > 1 OR ((c2 = 1) AND (c3 > 1)))\G
*************************** 1. row ***************************
        id: 1
select_type: SIMPLE
        table: t1
partitions: NULL
        type: range
possible_keys: PRIMARY
        key: PRIMARY
    key_len: 12
        ref: NULL
        rows: 3
    filtered: 100.00
        Extra: Using where

因此,为了获得更好的结果,避免在行构造器和AND/OR表达式中混合使用。使用其中之一即可。

在某些情况下,优化器可以将区间访问方法应用于具有行构造器参数的IN()表达式。