为什么 PostgreSQL 不使用索引进行 JOIN

问题描述 投票:0回答:1

我有两个表“Tasks”(300k 行)和“TaskAssignedRoles”(500k 行)。

CREATE TABLE public."Tasks" (
"ID" uuid NOT NULL,
"RowID" uuid NOT NULL,
"Planned" timestamptz NOT NULL,
CONSTRAINT "pk_Tasks" PRIMARY KEY ("RowID")
);
CREATE INDEX "idx_Tasks_ID" ON public."Tasks" USING btree ("ID");

CREATE TABLE public."TaskAssignedRoles" (
"ID" uuid NOT NULL,
"RowID" uuid NOT NULL,
"TaskRoleID" uuid NOT NULL,
"RoleID" uuid NOT NULL,
"RoleName" text NOT NULL,
"RoleTypeID" uuid NOT NULL,
"ParentRowID" uuid NULL,
CONSTRAINT "pk_TaskAssignedRoles" PRIMARY KEY ("RowID")
);
CREATE INDEX "idx_TaskAssignedRoles_ID" 
    ON public."TaskAssignedRoles" USING btree ("ID");
CREATE INDEX "ndx_TaskAssignedRoles_TaskRoleID" 
    ON public."TaskAssignedRoles" USING btree ("TaskRoleID") 
        INCLUDE ("ID", "RoleID", "RoleName", "RoleTypeID")
        WHERE (("ParentRowID" IS NULL) AND ("TaskRoleID" = 'f726ab6c-a279-4d79-863a-47253e55ccc1'::uuid));

我正在运行查询:

explain (analyze)
SELECT t.*, tarp."RoleID", tarp."RoleName"
FROM "Tasks" AS "t"
INNER JOIN "TaskAssignedRoles" AS tarp 
    ON "tarp"."ID" = "t"."RowID" 
        AND "tarp"."TaskRoleID" = 'f726ab6c-a279-4d79-863a-47253e55ccc1' 
        AND "tarp"."ParentRowID " IS NULL

我明白了计划:

Merge Join (cost=73.06..41145.44 rows=229928 width=434) (actual time=0.024..945.202 rows=234668 loops=1)
   Merge Cond: (t."RowID" = tarp."ID")
   -> Index Scan using "pk_Tasks" on "Tasks" t (cost=0.42..13861.52 rows=234668 width=388) (actual time=0.007..227.215 rows=234668 loops=1)
   -> Index Scan using "idx_TaskAssignedRoles_ID" on "TaskAssignedRoles" tarp (cost=0.42..23823.15 rows=229928 width=62) (actual time=0.013..541.680 rows=234668 loops=1)
         Filter: (("ParentRowID" IS NULL) AND ("TaskRoleID" = 'f726ab6c-a279-4d79-863a-47253e55ccc1'::uuid))
         Rows Removed by Filter: 280198
Planning Time: 0.353 ms
Execution Time: 953.534 ms

告诉我为什么 Postgres 不使用 ndx_TaskAssignedRoles_TaskRoleID 索引,尽管它完全匹配查询中的条件?

postgresql indexing query-optimization
1个回答
0
投票

我认为问题在于,虽然您认为索引涵盖了查询,但实际上并没有。以下是连接条件中涉及的

TaskAssignedRoles
表中的列:

ID
TaskRoleID
ParentRowID

您的索引仅包含

TaskRoleID
列。这里要意识到的是,
INCLUDE
子句仅包含 B 树叶节点处的列值;这些列在结构上不属于索引的一部分。类似地,
WHERE
子句只是在创建索引之前将表中的记录限制为某个子集。但同样,
WHERE
子句中的列不是索引的一部分。

尝试使用此版本:

CREATE INDEX "ndx_TaskAssignedRoles_TaskRoleID" 
ON public."TaskAssignedRoles" USING btree ("TaskRoleID", "RoleID", "ID")
    INCLUDE ("RoleName")
    WHERE "ParentRowID" IS NULL

这个索引似乎完全覆盖了连接条件。

INCLUDE
子句包含选择所需的一列。

© www.soinside.com 2019 - 2024. All rights reserved.