我正在try 使用表上的复合(多列)索引来帮助创建每日报告计数.我使用的是Postgres 13,我的表格如下所示:

CREATE TABLE inquiries (
    id bigint NOT NULL,
    identity_id bigint NOT NULL,
    received_at timestamp(0) without time zone NOT NULL,
    purpose_id bigint NOT NULL,
    location_id bigint NOT NULL
);

CREATE INDEX "inquiries_DATE_index" ON inquiries USING btree
   (date(received_at), location_id, purpose_id, identity_id);

我的查询看起来像这样:

SELECT DATE(received_at), location_id, purpose_id, COUNT(DISTINCT identity_id)
FROM inquiries
WHERE (DATE(received_at) >= $1)
  AND (DATE(received_at) <= $2)
GROUP BY 1, 2, 3

解释输出如下所示:

GroupAggregate  (cost=43703.28..45785.49 rows=10950 width=19)
  Group Key: (date(received_at)), location_id, purpose_id
  ->  Sort  (cost=43703.28..44092.34 rows=155627 width=16)
        Sort Key: (date(received_at)), location_id, purpose_id
        ->  Bitmap Heap Scan on inquiries  (cost=5243.60..27622.21 rows=155627 width=16)
              Recheck Cond: ((date(received_at) >= '2023-11-01'::date) AND (date(received_at) <= '2023-11-30'::date))
              ->  Bitmap Index Scan on "inquiries_DATE_index"  (cost=0.00..5204.70 rows=155627 width=0)
                    Index Cond: ((date(received_at) >= '2023-11-01'::date) AND (date(received_at) <= '2023-11-30'::date))

索引似乎没有帮助,执行查询需要很长时间.如果我向表中添加一个日期列,并使用该列而不是date(received_at),则查询会更好地工作,并且查询计划更改为:

GroupAggregate  (cost=0.43..85199.58 rows=10980 width=19)
  Group Key: pacific_date, location_id, purpose_id
  ->  Index Only Scan using inquiries_pacific_date_index on inquiries  (cost=0.43..77813.12 rows=727666 width=16)
        Index Cond: ((pacific_date >= '2023-11-01'::date) AND (pacific_date <= '2023-11-30'::date))

如果我找不到更好的方法,我想我可以这样做,但这似乎是多余的.有没有一种方法可以编写我的原始查询,使其更好地利用索引?

推荐答案

即插即用修复

就像Laurenz解释的那样,目前仅限索引的扫描(第16页)在Postgres中受到角例限制.The manual:

然而,PostgreSQL的规划者目前对此并不是很聪明 案子.它认为查询可能只通过索引执行 仅当查询所需的全部columns个可用时进行扫描 索引.

该手册有更多细节.一种解决方法是将column本身"包括"在索引中(替换旧索引):

CREATE INDEX inquiries_date_plus_idx ON inquiries
   (date(received_at), location_id, purpose_id, identity_id) INCLUDE (received_at);

允许对原始查询进行仅索引扫描.但它也会增加索引的大小--在本例中为每行增加8个字节.

fiddle个个

更好

在不带表达式的裸列上创建索引:

CREATE INDEX inquiries_received_at_plus_idx ON inquiries
   (received_at, location_id, purpose_id, identity_id);

(A plain index also often has additional utility for other purposes.)
And adjust your query slightly, to be exactly equivalent:

SELECT received_at::date, location_id, purpose_id, COUNT(DISTINCT identity_id)
FROM   inquiries
WHERE  received_at >= $1
AND    received_at <  $2 + 1  -- !
GROUP  BY 1, 2, 3;

输入$1$2必须是类型datereceived_at timestamp,如问题中所示.

在我的经验中,count(DISTINCT col)是典型的缓慢.这可能会更快,但:

SELECT received_at::date, location_id, purpose_id, count(*) AS dist_identities
FROM  (
   SELECT DISTINCT ON (1,2,3,4)
          received_at::date, location_id, purpose_id, identity_id
   FROM   inquiries
   WHERE  received_at >= $1
   AND    received_at <  $2 + 1
   ) sub
GROUP  BY 1, 2, 3;

fiddle个个

如果每(received_at::date, location_id, purpose_id, identity_id)个文件中有许多重复文件,那么模拟的索引跳过扫描可能会快得多.参见:

Upgrade

在过go 的几年里,Postgres在每一个主要版本中都提高了 Big Data 的性能.考虑升级到latest version Postgres 16(在 compose 本文时).会给你带来立竿见影的额外提振.

Sql相关问答推荐

如何在case语句中使用条件来计算成对变量

如何退回当年的所有参赛作品?""

出现5次后,将所有正斜杠替换为连字符

LEFT JOIN不显示计数0我期望的方式

如何在postgres函数中插入后返回布尔值?

SQL:如何查找聚合满足条件的连续日期

PostgreSQL:按小时查看调整日期

如何设计一个调用嵌套函数并仅在所有被调用的嵌套函数都提交时才提交的事务,例如,如果一个子函数失败则中止?

如何计算给定日期前三个月的值以及月初数据?

无法将发票与产品价格相关联

在xml.Modify方法中使用子字符串和可能的替代方法

在Netezza SQL中将字符DataType转换为整型DataType

带日期函数的复合索引不允许只扫描索引吗?

如何在AWS Athena中 Select JSON数组的最后一个元素?

根据标识符将两行合并为一行

使用SQLAlchemy和Postgres数据库创建新行时,为什么我的创建日期比更新日期晚?

COBOL\DB2作业(job)需要帮助?快来获取专业指导!

HIVE SQL where 子句未按预期工作

SQL Server 分区和 Run Case 语句

有没有一种方法可以将始终遵循序列的单个字段的值组合起来,以创建每个 ID 的所有移动?