我有两个表“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 索引,尽管它完全匹配查询中的条件?
我认为问题在于,虽然您认为索引涵盖了查询,但实际上并没有。以下是连接条件中涉及的
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
子句包含选择所需的一列。