我有一个像这样的 SQL 查询:
select *
from customers
where customer_id > 0
order by customer_id asc
limit 500;
而customer_id是customers表的主键。当我执行此查询并检查执行计划时。我看到它扫描了整个表格:
SELECT (select) 500 23.0 0.0 Node Type = Limit;
Parallel Aware = false;
Startup Cost = 0.43;
Total Cost = 23.16;
Plan Rows = 500;
Plan Width = 237;
TRANSFORM (Limit) 500 23.0 0.0 Node Type = Limit;
Parallel Aware = false;
Startup Cost = 0.43;
Total Cost = 23.16;
Plan Rows = 500;
Plan Width = 237;
INDEX_SCAN (Index Scan) table: customers; index: pk12; 3262339 148316.0 0.0 Node Type = Index Scan;
Parent Relationship = Outer;
Parallel Aware = false;
Scan Direction = Forward;
Index Name = pk12;
Relation Name = customers;
Alias = customers;
Startup Cost = 0.43;
Total Cost = 148316.36;
Plan Rows = 3222222;
Plan Width = 237;
Index Cond = (customer_id > '0'::numeric);
根据我的直觉,主键会创建索引,而 sql 引擎可以定位下限并从索引的叶节点(b+ 树)中获取 500 个项目。这是我能想到的最快的执行计划。为什么sql引擎扫描整个DB表并先排序它只得到500项?
附言:PostgreSQL。
最佳答案
您看到的只是完整 索引扫描的估计成本和行数,但如您所见,PostgreSQL 知道它不必扫描完整索引,否则查询的估计总成本 (23.16) 不能小于索引扫描的估计成本 (148316.36)。
使用 EXPLAIN (ANAYLZE)
查看实际发生了什么:
CREATE TABLE test (id) AS SELECT * FROM generate_series(1, 100000);
ALTER TABLE test ADD PRIMARY KEY (id);
VACUUM (ANALYZE) test;
我已经从执行计划中删除了不相关的行:
EXPLAIN (ANALYZE, FORMAT json)
SELECT id FROM test WHERE id > 0 ORDER BY id LIMIT 500;
QUERY PLAN
-----------------------------------------------
[ +
{ +
"Plan": { +
"Node Type": "Limit", +
"Startup Cost": 0.29, +
"Total Cost": 14.56, +
"Plan Rows": 500, +
"Actual Rows": 500, +
"Plans": [ +
{ +
"Node Type": "Index Only Scan", +
"Index Name": "test_pkey", +
"Plan Rows": 100000, +
"Actual Rows": 500, +
"Index Cond": "(id > 0)", +
} +
] +
}, +
} +
]
(1 row)
因此索引扫描在找到前 500 行后停止。
关于sql - 为什么 SQL 引擎在使用 LIMIT 时扫描索引列上的整个表?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51623939/