pingcap / pingcap/tidb

Inefficient query plan generated by the normal SELECT than the prepared SELECT when use subquery in the SELECT clause

Open
#66,606 2 comments 0 reactions 0 assignees View on GitHub
contribution severity/moderate 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, the normal SELECT is 10x slower than the prepared SELECT. After analyzing the query plan, I found that the normal SELECT performed more operation than the prepared 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 t0(c0 FLOAT);
INSERT INTO t0 (c0)
WITH RECURSIVE nr AS (
SELECT 1 AS n
UNION ALL
SELECT n + 1 FROM nr WHERE n < 1000
)
SELECT (RAND() * 1000) + 0.01 FROM nr;
EXPLAIN ANALYZE SELECT (CASE t0.c0 WHEN t0.c0 THEN ((((false)LIKE(t0.c0)))<=>(-1.753998307E9)) WHEN (CASE 726134525 WHEN NULL THEN NULL WHEN '-462245136' THEN (SELECT '' FROM t0 WHERE 'nf&?/i<' GROUP BY (((CASE NULL WHEN '' THEN (CASE t0.c0 WHEN t0.c0 THEN 1358430503 WHEN t0.c0 THEN t0.c0 ELSE t0.c0 END ) ELSE t0.c0 END ))>>(t0.c0)) HAVING (BINARY (t0.c0))) ELSE 'H' END ) THEN 'v' ELSE 'k' END ) FROM t0 WHERE '3' ORDER BY REVERSE(-1451698824) DESC, 'b\\' ASC, (NOT ((((('#ƨ[v')&(1013865458)))NOT REGEXP(CAST(t0.c0 AS DATETIME))))) DESC;
id estRows actRows task access object execution info operator info memory disk
Projection_15 10000.00 1000 root time:19.6ms, open:78.6µs, close:115.3µs, loops:2, RU:1.97, Concurrency:5 Column#11->Column#12 20.4 KB N/A
└─Projection_22 10000.00 1000 root time:19.5ms, open:78.2µs, close:104µs, loops:2, Concurrency:5 Column#11, database69o.t0.c0 32.6 KB N/A
└─Sort_18 10000.00 1000 root time:19.4ms, open:76.9µs, close:86µs, loops:2 Column#14:desc 43.8 KB 0 Bytes
└─Projection_23 10000.00 1000 root time:18.7ms, open:63.1µs, close:84.1µs, loops:2, Concurrency:5 Column#11, database69o.t0.c0, not(not(regexp(0, cast(cast(database69o.t0.c0, datetime BINARY), var_string(19)))))->Column#14 36.5 KB N/A
└─Projection_19 10000.00 1000 root time:1.84ms, open:62.6µs, close:71.2µs, loops:2, Concurrency:5 case(eq(database69o.t0.c0, database69o.t0.c0), cast(nulleq(like(0, cast(database69o.t0.c0, var_string(87)), 92), -1753998307), var_string(20)), eq(database69o.t0.c0, 0), v, k)->Column#11, database69o.t0.c0 24.5 KB N/A
└─TableReader_21 10000.00 1000 root time:1.19ms, open:61µs, close:19.6µs, loops:2, cop_task: {num: 4, max: 317.4µs, min: 120.8µs, avg: 235.9µs, p95: 317.4µs, copr_cache_hit_ratio: 0.00, build_task_duration: 14.7µs, max_distsql_concurrency: 1}, fetch_resp_duration: 1.01ms, rpc_info:{Cop:{num_rpc:4, total_time:875.6µs}} data:TableFullScan_20 3.80 KB N/A
└─TableFullScan_20 10000.00 1000 cop[tikv] table:t0 tikv_task:{proc max:238.2µs, min:89.6µs, avg: 191.2µs, p80:238.2µs, p95:238.2µs, iters:0, tasks:4} keep order:false, stats:pseudo N/A N/A

SET @a = false;
SET @b = -1.753998307E9;
SET @c = 726134525;
SET @d = NULL;
SET @e = NULL;
SET @f = '-462245136';
SET @g = '';
SET @h = 'nf&?/i<';
SET @i = 'H';
SET @j = 'v';
SET @k = 'k';
SET @l = '3';
PREPARE explain_prepare_query FROM "EXPLAIN ANALYZE SELECT (CASE t0.c0 WHEN t0.c0 THEN ((((?)LIKE(t0.c0)))<=>(?)) WHEN (CASE ? WHEN ? THEN ? WHEN ? THEN (SELECT ? FROM t0 WHERE ? GROUP BY (((CASE NULL WHEN '' THEN (CASE t0.c0 WHEN t0.c0 THEN 1358430503 WHEN t0.c0 THEN t0.c0 ELSE t0.c0 END ) ELSE t0.c0 END ))>>(t0.c0)) HAVING (BINARY (t0.c0))) ELSE ? END ) THEN ? ELSE ? END ) FROM t0 WHERE ? ORDER BY REVERSE(-1451698824) DESC, 'b\\' ASC, (NOT ((((('#ƨ[v')&(1013865458)))NOT REGEXP(CAST(t0.c0 AS DATETIME))))) DESC;"; -- 1ms;
EXECUTE explain_prepare_query USING @a,@b,@c,@d,@e,@f,@g,@h,@i,@j,@k,@l;
id estRows actRows task access object execution info operator info memory disk
Projection_16 10000.00 1000 root time:1.63ms, open:63.4µs, close:20.1µs, loops:2, RU:1.97, Concurrency:5 case(eq(database69o.t0.c0, database69o.t0.c0), cast(nulleq(like(0, cast(database69o.t0.c0, var_string(87)), 92), -1753998307), var_string(20)), eq(database69o.t0.c0, 0), v, k)->Column#11 23.3 KB N/A
└─TableReader_18 10000.00 1000 root time:1.02ms, open:61.9µs, close:6.33µs, loops:2, cop_task: {num: 4, max: 359.6µs, min: 49.2µs, avg: 189.1µs, p95: 359.6µs, copr_cache_hit_ratio: 0.00, build_task_duration: 19.3µs, max_distsql_concurrency: 1}, fetch_resp_duration: 832.1µs, rpc_info:{Cop:{num_rpc:4, total_time:694.5µs}} data:TableFullScan_17 4.02 KB N/A
└─TableFullScan_17 10000.00 1000 cop[tikv] table:t0 tikv_task:{proc max:254.4µs, min:34.8µs, avg: 147.9µs, p80:254.4µs, p95:254.4µ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 same as 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: 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

Research direction

Start by reproducing the provided SQL case on the stated TiDB version and compare the EXPLAIN ANALYZE plans for the normal and prepared SELECT statements. Trace the query-planning path responsible for the extra Projection and Sort operations; done means the normal SELECT no longer performs materially more work than the prepared form.

Written by the indexing model from the issue text.

Assessment

Tech stack
go, sql
Domain
databases
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.