diff --git a/sql-plan-management.md b/sql-plan-management.md index 46ddffded880..7f589a0d3e57 100644 --- a/sql-plan-management.md +++ b/sql-plan-management.md @@ -103,35 +103,26 @@ SELECT * FROM test . t WHERE a > ? > **注意:** > -> 在进行标准化的时候,被逗号 `,` 连接起来的多个常量会被标准化为 `...` 而不是 `?`。 +> 在进行标准化的时候,`IN` 表达式中的 `?` 会被标准化为 `...`。 > > 例如: > > ```sql -> SELECT * FROM t limit 10 -> SELECT * FROM t limit 10, 20 > SELECT * FROM t WHERE a IN (1) > SELECT * FROM t WHERE a IN (1,2,3) > -- 以上语句标准化后如下: -> SELECT * FROM test . t limit ? -> SELECT * FROM test . t limit ... -> SELECT * FROM test . t WHERE a IN ( ? ) +> SELECT * FROM test . t WHERE a IN ( ... ) > SELECT * FROM test . t WHERE a IN ( ... ) > ``` > -> 因此包含单个常量的 SQL 语句和包含被逗号连接起来多个常量的 SQL 语句,在被绑定时会被 TiDB 视作不同的 SQL 语句,需要分别创建绑定。 +> 不同长度的 `IN` 表达式被标准化后,会被识别为同一条语句,因此只需要创建一条绑定,对这些表达式同时生效。 > > 例如: > > ```sql -> CREATE TABLE t(a INT, b INT, KEY idx(a)); -> CREATE SESSION BINDING for SELECT * FROM t WHERE a IN (?) USING SELECT /*+ use_index(t, idx) */ * FROM t WHERE a in (?); -> SHOW BINDINGS; -> +-----------------------------------------------+----------------------------------------------------------------------+------------+---------+-------------------------+-------------------------+---------+--------------------+--------+------------------------------------------------------------------+-------------+ -> | Original_sql | Bind_sql | Default_db | Status | Create_time | Update_time | Charset | Collation | Source | Sql_digest | Plan_digest | -> +-----------------------------------------------+----------------------------------------------------------------------+------------+---------+-------------------------+-------------------------+---------+--------------------+--------+------------------------------------------------------------------+-------------+ -> | SELECT * FROM `test` . `t` WHERE `a` IN ( ? ) | SELECT /*+ use_index(`t` `idx`)*/ * FROM `test`.`t` WHERE `a` IN (?) | test | enabled | 2023-08-23 14:15:31.472 | 2023-08-23 14:15:31.472 | utf8mb4 | utf8mb4_general_ci | manual | 8b9c4e6ab8fad5ba29b034311dcbfc8a8ce57dde2e2d5d5b65313b90ebcdebf7 | | -> +-----------------------------------------------+----------------------------------------------------------------------+------------+---------+-------------------------+-------------------------+---------+--------------------+--------+------------------------------------------------------------------+-------------+ +> CREATE TABLE t (a INT, KEY(a)); +> CREATE BINDING FOR SELECT * FROM t WHERE a IN (?) USING SELECT /*+ use_index(t, a) */ * FROM t WHERE a in (?); +> > SELECT * FROM t WHERE a IN (1); > SELECT @@LAST_PLAN_FROM_BINDING; > +--------------------------+ @@ -139,51 +130,38 @@ SELECT * FROM test . t WHERE a > ? > +--------------------------+ > | 1 | > +--------------------------+ -> SELECT * FROM t WHERE a IN (1,2); -> SELECT @@LAST_PLAN_FROM_BINDING; -> +--------------------------+ -> | @@LAST_PLAN_FROM_BINDING | -> +--------------------------+ -> | 0 | -> +--------------------------+ -> CREATE SESSION BINDING for SELECT * FROM t WHERE a IN (?,?) USING SELECT /*+ use_index(t, idx) */ * FROM t WHERE a IN (?,?); -> show bindings; -> +-------------------------------------------------+------------------------------------------------------------------------+------------+---------+-------------------------+-------------------------+---------+--------------------+--------+------------------------------------------------------------------+-------------+ -> | Original_sql | Bind_sql | Default_db | Status | Create_time | Update_time | Charset | Collation | Source | Sql_digest | Plan_digest | -> +-------------------------------------------------+------------------------------------------------------------------------+------------+---------+-------------------------+-------------------------+---------+--------------------+--------+------------------------------------------------------------------+-------------+ -> | SELECT * FROM `test` . `t` WHERE `a` IN ( ... ) | SELECT /*+ use_index(`t` `idx`)*/ * FROM `test`.`t` WHERE `a` IN (?,?) | test | enabled | 2023-08-23 14:16:30.762 | 2023-08-23 14:16:30.762 | utf8mb4 | utf8mb4_general_ci | manual | da38bf216db4a53e1a1e01c79ffa42306419442ad7238480bb7ac510723c8bdf | | -> | SELECT * FROM `test` . `t` WHERE `a` IN ( ? ) | SELECT /*+ use_index(`t` `idx`)*/ * FROM `test`.`t` WHERE `a` IN (?) | test | enabled | 2023-08-23 14:15:31.472 | 2023-08-23 14:15:31.472 | utf8mb4 | utf8mb4_general_ci | manual | 8b9c4e6ab8fad5ba29b034311dcbfc8a8ce57dde2e2d5d5b65313b90ebcdebf7 | | -> +-------------------------------------------------+------------------------------------------------------------------------+------------+---------+-------------------------+-------------------------+---------+--------------------+--------+------------------------------------------------------------------+-------------+ -> SELECT * FROM t WHERE a IN (1,2); -> SELECT @@LAST_PLAN_FROM_BINDING; -> +--------------------------+ -> | @@LAST_PLAN_FROM_BINDING | -> +--------------------------+ -> | 1 | -> +--------------------------+ -> SELECT * FROM t WHERE a IN (1,2,3); +> +> SELECT * FROM t WHERE a IN (1, 2, 3); > SELECT @@LAST_PLAN_FROM_BINDING; > +--------------------------+ > | @@LAST_PLAN_FROM_BINDING | > +--------------------------+ > | 1 | > +--------------------------+ -> DROP SESSION BINDING for SELECT * FROM t WHERE a IN (?); -> SHOW BINDINGS; -> +-------------------------------------------------+------------------------------------------------------------------------+------------+---------+-------------------------+-------------------------+---------+--------------------+--------+------------------------------------------------------------------+-------------+ -> | Original_sql | Bind_sql | Default_db | Status | Create_time | Update_time | Charset | Collation | Source | Sql_digest | Plan_digest | -> +-------------------------------------------------+------------------------------------------------------------------------+------------+---------+-------------------------+-------------------------+---------+--------------------+--------+------------------------------------------------------------------+-------------+ -> | SELECT * FROM `test` . `t` WHERE `a` IN ( ... ) | SELECT /*+ use_index(`t` `idx`)*/ * FROM `test`.`t` WHERE `a` IN (?,?) | test | enabled | 2023-08-23 14:16:30.762 | 2023-08-23 14:16:30.762 | utf8mb4 | utf8mb4_general_ci | manual | da38bf216db4a53e1a1e01c79ffa42306419442ad7238480bb7ac510723c8bdf | | -> +-------------------------------------------------+------------------------------------------------------------------------+------------+---------+-------------------------+-------------------------+---------+--------------------+--------+------------------------------------------------------------------+-------------+ -> SELECT * FROM t WHERE a IN (1); -> SELECT @@LAST_PLAN_FROM_BINDING; -> +--------------------------+ -> | @@LAST_PLAN_FROM_BINDING | -> +--------------------------+ -> | 0 | -> +--------------------------+ > ``` +> +> 在 v7.4.0 之前版本的 TiDB 集群中创建的绑定可能会包含 `IN (?)`,在升级到 v7.4.0 或更高版本后,这些绑定会被统一修改为 `IN (...)`。 +> +> 例如: > +> ```sql +> -- 在 v7.3.0 集群上创建绑定 +> mysql> CREATE GLOBAL BINDING FOR SELECT * FROM t WHERE a IN (1) USING SELECT /*+ use_index(t, a) */ * FROM t WHERE a IN (1); +> mysql> SHOW GLOBAL BINDINGS; +> +-----------------------------------------------+--------------------------------------------------------------------+------------+---------+-------------------------+-------------------------+---------+-----------------+--------+------------------------------------------------------------------+-------------+ +> | Original_sql | Bind_sql | Default_db | Status | Create_time | Update_time | Charset | Collation | Source | Sql_digest | Plan_digest | +> +-----------------------------------------------+--------------------------------------------------------------------+------------+---------+-------------------------+-------------------------+---------+-----------------+--------+------------------------------------------------------------------+-------------+ +> | select * from `test` . `t` where `a` in ( ? ) | SELECT /*+ use_index(`t` `a`)*/ * FROM `test`.`t` WHERE `a` IN (1) | test | enabled | 2023-10-20 14:28:10.093 | 2023-10-20 14:28:10.093 | utf8 | utf8_general_ci | manual | 8b9c4e6ab8fad5ba29b034311dcbfc8a8ce57dde2e2d5d5b65313b90ebcdebf7 | | +> +-----------------------------------------------+--------------------------------------------------------------------+------------+---------+-------------------------+-------------------------+---------+-----------------+--------+------------------------------------------------------------------+-------------+ +> +> -- 升级到 v7.4.0 或更高的版本后 +> mysql> SHOW GLOBAL BINDINGS; +> +-------------------------------------------------+--------------------------------------------------------------------+------------+---------+-------------------------+-------------------------+---------+-----------------+--------+------------------------------------------------------------------+-------------+ +> | Original_sql | Bind_sql | Default_db | Status | Create_time | Update_time | Charset | Collation | Source | Sql_digest | Plan_digest | +> +-------------------------------------------------+--------------------------------------------------------------------+------------+---------+-------------------------+-------------------------+---------+-----------------+--------+------------------------------------------------------------------+-------------+ +> | select * from `test` . `t` where `a` in ( ... ) | SELECT /*+ use_index(`t` `a`)*/ * FROM `test`.`t` WHERE `a` IN (1) | test | enabled | 2023-10-20 14:28:10.093 | 2023-10-20 14:28:10.093 | utf8 | utf8_general_ci | manual | 8b9c4e6ab8fad5ba29b034311dcbfc8a8ce57dde2e2d5d5b65313b90ebcdebf7 | | +> +-------------------------------------------------+--------------------------------------------------------------------+------------+---------+-------------------------+-------------------------+---------+-----------------+--------+------------------------------------------------------------------+-------------+ +> ``` 值得注意的是,如果一条 SQL 语句在 GLOBAL 和 SESSION 作用域内都有与之绑定的执行计划,因为优化器在遇到 SESSION 绑定时会忽略 GLOBAL 绑定的执行计划,该语句在 SESSION 作用域内绑定的执行计划会屏蔽掉语句在 GLOBAL 作用域内绑定的执行计划。