sql - 为什么 SQL 引擎在使用 LIMIT 时扫描索引列上的整个表?

标签 sql postgresql primary-key sql-execution-plan

我有一个像这样的 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/

相关文章:

java - 无法验证新建立的连接

MySQL:优化无主键的表(索引、外键)

postgresql - 如何查询自指定点(时间戳或事务 ID)以来的 postgres 增量更新?

sql - Laravel 连接查询中的逗号分隔列值

sql - SQL中的菱形结构

php - mysql 服务器不接受 id 和 datetime 作为新记录

SQLite 语法错误(从 PostgreSQL 转换)

MySQL 自动增量不适用于 INT(12)

sqlite - SQLite表中的列vs表主键约束

sql - 如何计算时差(HH :MM:SS) from date_time (ISO format) in Postgres