Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Spm fix hint issue #19740

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 25 additions & 31 deletions sql-plan-management.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,50 +43,44 @@ CREATE [GLOBAL | SESSION] BINDING [FOR BindableStmt] USING BindableStmt;
This statement binds SQL execution plans at the GLOBAL or SESSION level. Currently, supported bindable SQL statements (BindableStmt) in TiDB include `SELECT`, `DELETE`, `UPDATE`, and `INSERT` / `REPLACE` with `SELECT` subqueries. The following is an example:

```sql
CREATE GLOBAL BINDING USING SELECT /*+ use_index(t1, idx_a) */ * FROM t1;
CREATE GLOBAL BINDING FOR SELECT * FROM t1 USING SELECT /*+ use_index(t1, idx_a) */ * FROM t1;
CREATE GLOBAL BINDING USING SELECT /*+ use_index(orders, orders_book_id_idx) */ * FROM orders;
CREATE GLOBAL BINDING FOR SELECT * FROM orders USING SELECT /*+ use_index(orders, orders_book_id_idx) */ * FROM orders;
```

> **Note:**
>
> Bindings have higher priority over manually added hints. Therefore, when you execute a statement containing a hint while a corresponding binding is present, the hint controlling the behavior of the optimizer does not take effect. However, other types of hints are still effective.

Specifically, two types of these statements cannot be bound to execution plans due to syntax conflicts. See the following examples:
Specifically, two types of these statements cannot be bound to execution plans due to syntax conflicts. A syntax error will be reported during creation. See the following examples:

```sql
-- Type one: Statements that get the Cartesian product by using the `JOIN` keyword and not specifying the associated columns with the `USING` keyword.
CREATE GLOBAL BINDING for
SELECT * FROM t t1 JOIN t t2
SELECT * FROM orders o1 JOIN orders o2
USING
SELECT * FROM t t1 JOIN t t2;
SELECT * FROM orders o1 JOIN orders o2;

-- Type two: `DELETE` statements that contain the `USING` keyword.
CREATE GLOBAL BINDING for
DELETE FROM t1 USING t1 JOIN t2 ON t1.a = t2.a
DELETE FROM users USING users JOIN orders ON users.id = orders.user_id
USING
DELETE FROM t1 USING t1 JOIN t2 ON t1.a = t2.a;
DELETE FROM users USING users JOIN orders ON users.id = orders.user_id;
```

You can bypass syntax conflicts by using equivalent statements. For example, you can rewrite the above statements in the following ways:

```sql
-- First rewrite of type one statements: Add a `USING` clause for the `JOIN` keyword.
-- First rewrite of type one statements: Delete the `JOIN` keyword. Replace it with a comma.
hfxsd marked this conversation as resolved.
Show resolved Hide resolved
CREATE GLOBAL BINDING for
SELECT * FROM t t1 JOIN t t2 USING (a)
SELECT * FROM orders o1, orders o2
USING
SELECT * FROM t t1 JOIN t t2 USING (a);
SELECT * FROM orders o1, orders o2;

-- Second rewrite of type one statements: Delete the `JOIN` keyword.
-- Rewrite of type two statements: Remove the `USING` keyword from the `DELETE` statement.
CREATE GLOBAL BINDING for
SELECT * FROM t t1, t t2
USING
SELECT * FROM t t1, t t2;

-- Rewrite of type two statements: Remove the `USING` keyword from the `delete` statement.
CREATE GLOBAL BINDING for
DELETE t1 FROM t1 JOIN t2 ON t1.a = t2.a
DELETE users FROM users JOIN orders ON users.id = orders.user_id
using
hfxsd marked this conversation as resolved.
Show resolved Hide resolved
DELETE t1 FROM t1 JOIN t2 ON t1.a = t2.a;
DELETE users FROM users JOIN orders ON users.id = orders.user_id;
```

> **Note:**
Expand All @@ -98,25 +92,25 @@ Here are two examples:
```sql
-- The hint takes effect in the following statement.
CREATE GLOBAL BINDING for
INSERT INTO t1 SELECT * FROM t2 WHERE a > 1 AND b = 1
using
INSERT INTO t1 SELECT /*+ use_index(@sel_1 t2, idx_a) */ * FROM t2 WHERE a > 1 AND b = 1;
INSERT INTO orders SELECT * FROM pre_orders WHERE status = 'VALID' AND created <= (NOW() - INTERVAL 1 HOUR)
USING
INSERT INTO orders SELECT /*+ use_index(@sel_1 pre_orders, idx_created) */ * FROM pre_orders WHERE status = 'VALID' AND created <= (NOW() - INTERVAL 1 HOUR);

-- The hint cannot take effect in the following statement.
CREATE GLOBAL BINDING for
INSERT INTO t1 SELECT * FROM t2 WHERE a > 1 AND b = 1
using
INSERT /*+ use_index(@sel_1 t2, idx_a) */ INTO t1 SELECT * FROM t2 WHERE a > 1 AND b = 1;
INSERT INTO orders SELECT * FROM pre_orders WHERE status = 'VALID' AND created <= (NOW() - INTERVAL 1 HOUR)
USING
INSERT /*+ use_index(@sel_1 pre_orders, idx_created) */ INTO orders SELECT * FROM pre_orders WHERE status = 'VALID' AND created <= (NOW() - INTERVAL 1 HOUR);
```

If you do not specify the scope when creating an execution plan binding, the default scope is SESSION. The TiDB optimizer normalizes bound SQL statements and stores them in the system table. When processing SQL queries, if a normalized statement matches one of the bound SQL statements in the system table and the system variable `tidb_use_plan_baselines` is set to `on` (the default value is `on`), TiDB then uses the corresponding optimizer hint for this statement. If there are multiple matchable execution plans, the optimizer chooses the least costly one to bind.

`Normalization` is a process that converts a constant in an SQL statement to a variable parameter and explicitly specifies the database for tables referenced in the query, with standardized processing on the spaces and line breaks in the SQL statement. See the following example:

```sql
SELECT * FROM t WHERE a > 1
SELECT * FROM users WHERE balance > 100
-- After normalization, the above statement is as follows:
SELECT * FROM test . t WHERE a > ?
SELECT * FROM bookshop . users WHERE balance > ?
```

> **Note:**
Expand All @@ -126,11 +120,11 @@ SELECT * FROM test . t WHERE a > ?
> For example:
>
> ```sql
> SELECT * FROM t WHERE a IN (1)
> SELECT * FROM t WHERE a IN (1,2,3)
> SELECT * FROM books WHERE type IN ('Novel')
> SELECT * FROM books WHERE type IN ('Novel','Life','Education')
> -- After normalization, the above statements are as follows:
> SELECT * FROM test . t WHERE a IN ( ... )
> SELECT * FROM test . t WHERE a IN ( ... )
> SELECT * FROM bookshop . books WHERE type IN ( ... )
> SELECT * FROM bookshop . books WHERE type IN ( ... )
> ```
>
> After normalization, `IN` predicates of different lengths are recognized as the same statement, so you only need to create one binding that applies to all these predicates.
Expand Down
Loading