pingcap / pingcap/tidb

Redundant Selection and Projection in the query plan of normal SELECT, causes it slower than the prepared SELECT

Open
#66,609 0 comments 0 reactions 0 assignees View on GitHub
contribution severity/minor sig/planner type/bug
Dominant language
Go
Stars
40.5k
Forks
6.2k
PR merge metrics
PR metrics pending

Description

## Bug Report

Please answer these questions before submitting your issue. Thanks!

### 1. Minimal reproduce step (Required)

Hi,

In the following test case, there are two equivalent queries, one is a normal SELECT, and the other is a prepared SELECT. However, I found that the normal SELECT is slower than the prepared SELECT. After analyzing the query plan, I found that there is redundant Selection and Projection operation in the query plan of the normal SELECT. Given that prepared statements typically result in suboptimal query plans, the query plan generation process for normal SELECT statements may still have room for further optimization.

```
CREATE TABLE t1(c0 NUMERIC , c1 BOOL, c2 CHAR);
INSERT INTO t1 (c0, c1, c2)
WITH RECURSIVE seq AS (
SELECT 1 AS n
UNION ALL
SELECT n + 1 FROM seq WHERE n < 1000
)
SELECT
RAND() * 10000,
FLOOR(RAND() * 2),
CHAR(FLOOR(65 + RAND() * 26))
FROM seq;
CREATE VIEW v0(c0) AS SELECT DEFAULT(t1.c1) FROM t1 WHERE 1406734557 ORDER BY ((DEFAULT(t1.c1))AND('0.31072613690367223'));
EXPLAIN ANALYZE SELECT true FROM t1 RIGHT JOIN v0 ON t1.c2 WHERE true GROUP BY t1.c1, '1?\'#1R?l', v0.c0 HAVING t1.c1;
id estRows actRows task access object execution info operator info memory disk
Projection_15 5120.00 0 root time:24.9ms, open:226.3µs, close:101.6µs, loops:1, RU:9.98, Concurrency:5 1->Column#13 3.71 KB N/A
└─Selection_17 5120.00 0 root time:24.8ms, open:225.2µs, close:76.8µs, loops:1 database31o.t1.c1 380 Bytes N/A
└─HashAgg_21 6400.00 1 root time:24.8ms, open:221.6µs, close:76.2µs, loops:2, partial_worker:{wall_time:24.407128ms, concurrency:5, task_num:1, tot_wait:24.03059ms, tot_exec:245.419µs, tot_time:121.632109ms, max:24.342888ms, p95:24.342888ms}, final_worker:{wall_time:24.432768ms, concurrency:5, task_num:5, tot_wait:45.101µs, tot_exec:239ns, tot_time:121.9959ms, max:24.405518ms, p95:24.405518ms} group by:Column#11, database31o.t1.c1, funcs:firstrow(database31o.t1.c1)->database31o.t1.c1 80.4 KB 0 Bytes
└─HashJoin_33 80000000.00 1000 root time:24.2ms, open:113.4µs, close:70.8µs, loops:2, build_hash_table:{total:23.4ms, fetch:23.4ms, build:0s}, probe:{concurrency:5, total:117.3ms, max:23.8ms, probe:442.6µs, fetch and wait:116.8ms} CARTESIAN right outer join, left side:TableReader_36 0 Bytes 0 Bytes
├─TableReader_36(Build) 8000.00 0 root time:23.4ms, open:71.3µs, close:15.6µs, loops:1, cop_task: {num: 1, max: 20.3ms, proc_keys: 0, tot_proc: 19ms, copr_cache_hit_ratio: 0.00, build_task_duration: 19.4µs, max_distsql_concurrency: 1}, fetch_resp_duration: 22.7ms, rpc_info:{Cop:{num_rpc:1, total_time:20.2ms}}, backoff{regionMiss: 2ms} data:Selection_35 43.2 KB N/A
│ └─Selection_35 8000.00 0 cop[tikv] tikv_task:{time:19.2ms, loops:0}, time_detail: {total_process_time: 19ms} database31o.t1.c2 N/A N/A
│ └─TableFullScan_34 10000.00 1000 cop[tikv] table:t1 tikv_task:{time:19.2ms, loops:0} keep order:false, stats:pseudo N/A N/A
└─Projection_37(Probe) 10000.00 1000 root time:3.54ms, open:38.9µs, close:33.3µs, loops:2, Concurrency:5 ->Column#11 20.1 KB N/A
└─TableReader_40 10000.00 1000 root time:3.4ms, open:37.8µs, close:1.83µs, loops:2, cop_task: {num: 4, max: 362.1µs, min: 119.1µs, avg: 237.1µs, p95: 362.1µs, copr_cache_hit_ratio: 0.00, build_task_duration: 2.05µs, max_distsql_concurrency: 1}, fetch_resp_duration: 3.23ms, rpc_info:{Cop:{num_rpc:5, total_time:915.7µs}, rpc_errors:{epoch_not_match:1}}, backoff{regionMiss: 8ms}data:TableFullScan_39 7.05 KB N/A
└─TableFullScan_39 10000.00 1000 cop[tikv] table:t1 tikv_task:{proc max:296.8µs, min:86.1µs, avg: 194.3µs, p80:296.8µs, p95:296.8µs, iters:0, tasks:4} keep order:false, stats:pseudo N/A N/A

SET @a = true; -- 0ms;
SET @b = true; -- 1ms;
PREPARE explain_prepare_query FROM "EXPLAIN ANALYZE SELECT ? FROM t1 RIGHT OUTER JOIN v0 ON t1.c2 WHERE ? GROUP BY t1.c1, '1?\'#1R?l', v0.c0 HAVING t1.c1;";
EXECUTE explain_prepare_query USING @a,@b;
id estRows actRows task access object execution info operator info memory disk
Projection_10 6400.00 1 root time:21.1ms, open:133.4µs, close:62.7µs, loops:2, RU:9.18, Concurrency:5 1->Column#12 3.71 KB N/A
└─HashAgg_14 6400.00 1 root time:21ms, open:132.5µs, close:41.1µs, loops:2, partial_worker:{wall_time:20.706175ms, concurrency:5, task_num:1, tot_wait:20.432455ms, tot_exec:171.719µs, tot_time:103.169104ms, max:20.640604ms, p95:20.640604ms}, final_worker:{wall_time:20.774064ms, concurrency:5, task_num:5, tot_wait:11.479µs, tot_exec:200ns, tot_time:103.39195ms, max:20.692624ms, p95:20.692624ms} group by:database31o.t1.c1, funcs:firstrow(1)->Column#15 66.8 KB 0 Bytes
└─HashJoin_26 80000000.00 1000 root time:20.6ms, open:98.6µs, close:35.9µs, loops:2, build_hash_table:{total:19.9ms, fetch:19.9ms, build:0s}, probe:{concurrency:5, total:100ms, max:20.2ms, probe:330.2µs, fetch and wait:99.7ms} CARTESIAN right outer join, left side:TableReader_29 0 Bytes 0 Bytes
├─TableReader_29(Build) 8000.00 0 root time:19.9ms, open:66.2µs, close:14.4µs, loops:1, cop_task: {num: 1, max: 19.2ms, proc_keys: 0, tot_proc: 18ms, copr_cache_hit_ratio: 0.00, build_task_duration: 16.8µs, max_distsql_concurrency: 1}, fetch_resp_duration: 19.2ms, rpc_info:{Cop:{num_rpc:1, total_time:19.1ms}} data:Selection_28 43.2 KB N/A
│ └─Selection_28 8000.00 0 cop[tikv] tikv_task:{time:18.2ms, loops:0}, time_detail: {total_process_time: 18ms} database31o.t1.c2 N/A N/A
│ └─TableFullScan_27 10000.00 1000 cop[tikv] table:t1 tikv_task:{time:18.2ms, loops:0} keep order:false, stats:pseudo N/A N/A
└─TableReader_31(Probe) 10000.00 1000 root time:868.6µs, open:30.1µs, close:2.18µs, loops:2, cop_task: {num: 4, max: 296.8µs, min: 98.4µs, avg: 235.3µs, p95: 296.8µs, copr_cache_hit_ratio: 0.00, build_task_duration: 6.44µs, max_distsql_concurrency: 1}, fetch_resp_duration: 710.6µs, rpc_info:{Cop:{num_rpc:4, total_time:857.9µs}} data:TableFullScan_30 7.05 KB N/A
└─TableFullScan_30 10000.00 1000 cop[tikv] table:t1 tikv_task:{proc max:256.9µs, min:69.6µs, avg: 187.6µs, p80:256.9µs, p95:256.9µs, iters:0, tasks:4} keep order:false, stats:pseudo N/A N/A
```

### 2. What did you expect to see? (Required)
The normal SELECT should be faster or the same with the prepared SELECT.

### 3. What did you see instead (Required)
The normal SELECT is slower than the prepared SELECT.

### 4. What is your TiDB version? (Required)

Release Version: v9.0.0-beta.2.pre-1118-gfc6f7b2e42\nEdition: Community\nGit Commit Hash: https://github.com/pingcap/tidb/commit/fc6f7b2e42be08957a9b80e5761bfa4793066353\nGit Branch: master\nUTC Build Time: 2026-01-26 12:27:51\nGoVersion: go1.25.6\nRace Enabled: false\nCheck Table Before Drop: false\nStore: unistore\nKernel Type: Classic

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.