Sql Postgres不在两个比较运算符上使用索引

Sql Postgres不在两个比较运算符上使用索引,sql,postgresql,Sql,Postgresql,这是一个问题 explain analyze SELECT first_name, last_name, date_of_birth FROM employees WHERE phone_number < '989898' AND date_of_birth < '2020-01-01' 这是分析输出 "Seq Scan on employees (cost=0.00..301.00 rows=1000 width=14) (actual time=0.1

这是一个问题

explain analyze
SELECT first_name, last_name, date_of_birth 
  FROM employees
 WHERE phone_number < '989898' 
   AND date_of_birth    < '2020-01-01'

这是分析输出

"Seq Scan on employees  (cost=0.00..301.00 rows=1000 width=14) (actual time=0.110..8.644 rows=1000 loops=1)"
"  Filter: (((phone_number)::text < 'we'::text) AND (date_of_birth < '2020-01-01'::date))"
"Planning Time: 0.127 ms"
"Execution Time: 15.740 ms"
“员工序列扫描(成本=0.00..301.00行=1000宽=14)(实际时间=0.110..8.644行=1000圈=1)”
“筛选器:((电话号码)::文本<'we'::文本)和(出生日期<'2020-01-01'::日期))”
“计划时间:0.127毫秒”
“执行时间:15.740毫秒”

为什么postgres不在复合索引上使用索引。

DBMS使用索引时,它可能比读取完整表快。当您仅读取(比如)表中1%的srows时,就是这种情况。一旦DBMS认为一个查询可能会访问更多的行——这可能是表行数的5%——它可能只是按顺序读取表


这两种情况都是
当DBMS使用索引时,它可能比读取整个表快。当您仅读取(比如)表中1%的srows时,就是这种情况。一旦DBMS认为一个查询可能会访问更多的行——这可能是表行数的5%——它可能只是按顺序读取表


您的两种情况都是
问题中没有足够的信息可以确定,但以下是一些提示:

查询中包含的筛选器非常全面: 出生日期<'2020-01-01'很可能与所有行匹配,因为只有少数5个月大的婴儿拥有手机。 电话号码<'989898'也将与大多数行匹配

Postgress知道您要求(几乎)完整的表,在这种情况下,seq scan更快。这是因为索引有助于选择要从光盘读取的页面。但使用索引会有成本。因此,如果你已经知道你正在阅读所有的索引,那么使用索引是没有意义的

实际上,postgress知道您正在阅读完整的表格:(cost=0.00..301.00rows=1000width=14),这就是为什么它选择seq scan,因为它会更快。如果您创建了一个更专用的过滤器,如phone phone_number<'11'(当然取决于您的数据分布!),您应该会看到索引扫描

Postgress具有关于每个列的内部统计信息,在创建执行计划时,它将估计查询返回的行数。统计数据并不完美,Postgress假设列是独立的。这是为了提供计划时间与动力的最佳组合。因此,如果假定filter1匹配0,1行,而filter2匹配0,01行,则假定返回的行数为0,1*0001*行数。还有一些其他可用和使用的统计数据。根据这个postgres做出决定,进行seq扫描或使用索引(以及哪个索引)是否更有利

在这种情况下,Postgress需要进行seq扫描,因为它需要转到光盘以获取first_name和last_name列,因为这些列不包括在索引中

快速查询的一种方法(取决于您的使用模式!)是创建覆盖索引。查询中涉及4列: 名字、姓氏、出生日期、电话号码。如果创建索引,如: btree(出生日期、电话号码、名字、姓氏)Postgress将能够始终为此查询运行仅索引扫描,并且从不使用光盘。但请注意,这个索引可能会变大,只有在内存中能够容纳它时,它才会起作用。所以要小心

您没有添加您正在使用的Postgress版本,但是从11开始(如果我没记错的话,肯定超过10),您可以在索引中包含列。这是一个非常酷的新功能。如果您总是只筛选电话号码和出生日期,您可以执行以下操作,例如: btree(出生日期、电话号码)包括(名字、姓氏)和“仅获取索引”扫描,此处索引较小

如果在PosieLayTo和DeNeYOFA出生时的这个过滤器是非常常见的,你可以考虑在两个列上创建一个复合统计量。这将允许Postgress创建更好的查询计划。在这种情况下,这不会改变任何东西,因为此带有seq scan的计划已经是最优的,但可能有助于使用不同的过滤器值

这两个提示将取决于未添加到问题中的列的类型:

如果您有一个类似date of Borth的表,那么查看BRIN索引可能是有益的


还请注意,时间列询问出生日期<'2020-01-01'意味着您询问的是从2020年到时间开始出生的所有人:)根据列类型,提供出生日期<'2020-01-01'和出生日期>'1900-01-01'的下限可能是有益的。但您需要在一个大型数据集上测试这一点,以查看是否确实看到了差异

问题中没有足够的信息可以确定,但以下是一些提示:

查询中包含的筛选器非常全面: 出生日期<'2020-01-01'很可能与所有行匹配,因为只有少数5个月大的婴儿拥有手机。 电话号码<'989898'也将与大多数行匹配

Postgress知道您要求(几乎)完整的表,在这种情况下,seq scan更快。这是因为索引有助于选择要从光盘读取的页面。但使用索引会有成本。因此,如果你已经知道你正在阅读所有的索引,那么使用索引是没有意义的

实际上,postgress知道您正在阅读完整的表格:(cost=0.00..301.00rows=1000width=14),这就是为什么它选择seq scan,因为它会更快。如果您创建了一个更专用的过滤器,如phone phone_number<'11'(当然取决于您的数据分布!),您应该会看到索引扫描

Postgress具有关于每个列的内部统计信息,在创建执行计划时,它将估计查询返回的行数。这些统计数据并不完美,而且是事后诸葛亮
"Seq Scan on employees  (cost=0.00..301.00 rows=1000 width=14) (actual time=0.110..8.644 rows=1000 loops=1)"
"  Filter: (((phone_number)::text < 'we'::text) AND (date_of_birth < '2020-01-01'::date))"
"Planning Time: 0.127 ms"
"Execution Time: 15.740 ms"