planner: the optimizer can't select the lowest-cost plan
- 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)
Use SQLs below to reproduce:
```
CREATE TABLE test.masked_event_journal (
rec_id BIGINT NOT NULL AUTO_INCREMENT,
route_code VARCHAR(32) DEFAULT NULL,
state_flag INT NOT NULL DEFAULT 0,
seen_at DATETIME DEFAULT NULL,
event_code VARCHAR(64) DEFAULT NULL,
shard_no INT DEFAULT NULL,
batch_at DATETIME DEFAULT NULL,
extra_note VARCHAR(128) DEFAULT NULL,
PRIMARY KEY (rec_id) CLUSTERED,
KEY idx_route_state_seen (route_code, state_flag, seen_at),
KEY idx_event_shard_batch_route_state (event_code, shard_no, batch_at, route_code, state_flag)
);
INSERT INTO test.masked_event_journal
(route_code, state_flag, seen_at, event_code, shard_no, batch_at, extra_note)
SELECT
CASE WHEN n % 32 = 0 THEN 'route_alpha' ELSE 'route_beta' END AS route_code,
CASE WHEN n % 2 = 0 THEN 1 ELSE 0 END AS state_flag,
TIMESTAMPADD(SECOND, n, '2026-01-01 00:00:00') AS seen_at,
CASE
WHEN n % 4 = 0 THEN 'event_target_314'
WHEN n % 7 = 0 THEN 'event_group_red'
ELSE 'event_group_blue'
END AS event_code,
n % 128 AS shard_no,
CASE
WHEN n % 1600 = 0 THEN '2026-06-27 11:56:03'
ELSE TIMESTAMPADD(SECOND, n % 86400, '2026-06-01 00:00:00')
END AS batch_at,
CONCAT('masked payload ', n) AS extra_note
FROM (
SELECT d0.i + d1.i * 10 + d2.i * 100 + d3.i * 1000 + d4.i * 10000 + 1 AS n
FROM
(SELECT 0 i UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) d0,
(SELECT 0 i UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) d1,
(SELECT 0 i UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) d2,
(SELECT 0 i UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) d3,
(SELECT 0 i UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) d4
) seq
WHERE n <= 100000;
ANALYZE TABLE test.masked_event_journal;
ANALYZE TABLE test.masked_event_journal;
EXPLAIN FORMAT='VERBOSE'
SELECT rec_id, extra_note
FROM test.masked_event_journal
WHERE route_code = 'route_alpha'
AND event_code = 'event_target_314'
AND batch_at = '2026-06-27 11:56:03'
AND state_flag = 1
ORDER BY seen_at DESC
LIMIT 1;
+----------------------------------+---------+------------+-----------+-----------------------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------+
| id | estRows | estCost | task | access object | operator info |
+----------------------------------+---------+------------+-----------+-----------------------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------+
| Limit_15 | 1.00 | 2612068.62 | root | | offset:0, count:1 |
| └─IndexLookUp_51 | 1.00 | 2612068.62 | root | | |
| ├─IndexRangeScan_48(Build) | 2209.71 | 808104.25 | cop[tikv] | table:masked_event_journal, index:idx_route_state_seen(route_code, state_flag, seen_at) | range:["route_alpha" 1,"route_alpha" 1], keep order:true, desc |
| └─Selection_50(Probe) | 1.00 | 862515.03 | cop[tikv] | | eq(test.masked_event_journal.batch_at, 2026-06-27 11:56:03.000000), eq(test.masked_event_journal.event_code, "event_target_314") |
| └─TableRowIDScan_49 | 2209.71 | 641986.11 | cop[tikv] | table:masked_event_journal | keep order:false |
+----------------------------------+---------+------------+-----------+-----------------------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------+
5 rows in set (0.015 sec)
EXPLAIN FORMAT='VERBOSE'
SELECT /*+ USE_INDEX(m idx_event_shard_batch_route_state) */ rec_id, extra_note
FROM test.masked_event_journal AS m
WHERE route_code = 'route_alpha'
AND event_code = 'event_target_314'
AND batch_at = '2026-06-27 11:56:03'
AND state_flag = 1
ORDER BY seen_at DESC
LIMIT 1;
+-------------------------------+----------+-------------+-----------+----------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| id | estRows | estCost | task | access object | operator info |
+-------------------------------+----------+-------------+-----------+----------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| TopN_10 | 1.00 | 702543.07 | root | | test.masked_event_journal.seen_at:desc, offset:0, count:1 |
| └─IndexLookUp_21 | 1.00 | 702534.97 | root | | |
| ├─Selection_19(Build) | 1.00 | 10508663.76 | cop[tikv] | | eq(test.masked_event_journal.batch_at, 2026-06-27 11:56:03.000000), eq(test.masked_event_journal.route_code, "route_alpha"), eq(test.masked_event_journal.state_flag, 1) |
| │ └─IndexRangeScan_17 | 25001.12 | 6765995.49 | cop[tikv] | table:m, index:idx_event_shard_batch_route_state(event_code, shard_no, batch_at, route_code, state_flag) | range:["event_target_314","event_target_314"], keep order:false |
| └─TopN_20(Probe) | 1.00 | 315.51 | cop[tikv] | | test.masked_event_journal.seen_at:desc, offset:0, count:1 |
| └─TableRowIDScan_18 | 1.00 | 290.53 | cop[tikv] | table:m | keep order:false |
+-------------------------------+----------+-------------+-----------+----------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
```
The optimizer selected the `Limit` plan by default, but another `TopN` plan's cost is smaller, `702543.07 vs 2612068.62`
### 2. What did you expect to see? (Required)
Should use the `TopN` plan.
### 3. What did you see instead (Required)
Use the `Limit` plan.
### 4. What is your TiDB version? (Required)
Master
Contributor guide
Assessment
This issue has not been assessed yet.