postgres json_agg()忽略GROUP BY表达式中连接表中的索引

时间:2016-12-07 12:20:39

标签: postgresql indexing query-optimization aggregate-functions

我有两个表(为简单起见省略了外键):

CREATE TABLE timetables (
"ttid" SERIAL4 NOT NULL,
"bioid" int4 NOT NULL,
"component" int4,
"route" int2,
"time_num" numeric,
"time_unit" char(1) COLLATE "default",
"time_shift" int2,
"time_devstage" int2,
"times_total" int2,
"every_num" numeric,
"every_unit" char(1) COLLATE "default",
"duration_num" numeric,
"duration_unit" char(1) COLLATE "default",
"doseid" int4 NOT NULL,
CONSTRAINT "timetables_pkey" PRIMARY KEY ("ttid"),
CONSTRAINT "timetables_doseid_fkey" FOREIGN KEY ("doseid") REFERENCES doses(doseid) ON DELETE CASCADE ON UPDATE CASCADE
);
CREATE INDEX "timetables_bioid_idx" ON "timetables" USING btree (bioid);
CREATE INDEX "timetables_doseid_idx" ON "timetables" USING btree (doseid);

CREATE TABLE doses (
"doseid" SERIAL4 NOT NULL,
"ai" numeric,
"conc" numeric,
"conc_unit" varchar COLLATE "default",
"vol" numeric,
"vol_unit" varchar COLLATE "default",
"amount" numeric,
"amount_unit" varchar COLLATE "default",
"area" numeric,
"area_unit" varchar COLLATE "default",
"numplants" numeric,
CONSTRAINT "doses_pkey" PRIMARY KEY ("doseid")
);

以下查询无法使用“bioid”列上的索引:

SELECT bioid, json_agg (doses) jtd
FROM timetables
LEFT JOIN doses USING (doseid)
GROUP BY bioid

EXPLAIN返回以下内容:

GroupAggregate  (cost=391.88..440.10 rows=2251 width=75)
  ->  Sort  (cost=391.88..398.57 rows=2677 width=75)
        Sort Key: timetables.bioid
        ->  Merge Right Join  (cost=0.56..239.47 rows=2677 width=75)
              Merge Cond: (doses.doseid = timetables.doseid)
              ->  Index Scan using doses_pkey on doses  (cost=0.28..93.79 rows=2367 width=75)
              ->  Index Scan using timetables_doseid_idx on timetables  (cost=0.28..106.43 rows=2677 width=8)

因此,尽管将“timetables.bioid”键声明为索引,但SORT仍然是明确的。 如果我将汇总“时间表”表而不是“剂量”,那么查询变得非常快:

GroupAggregate  (cost=0.28..147.96 rows=2251 width=77)
  ->  Index Scan using timetables_bioid_idx on timetables  (cost=0.28..106.43 rows=2677 width=77)

我应该如何优化查询以使用索引或我应该添加哪些索引?其实我需要整个输出的json_agg():
SELECT bioid,json_agg(td)jtd
FROM(时间表LEFT JOIN剂量使用(剂量))td
GROUP BY bioid

我正在使用Postgres 9.3

1 个答案:

答案 0 :(得分:0)

但查询 使用索引,它只是不使用您期望的索引。

PostgreSQL估计它通过使用合并连接变得最便宜。为此,两个表中的数据必须按连接条件排序,连接条件使用索引doses_pkeytimetables_doseid_idx完成。
在连接之后,条目按doseid排序,因此必须按bioidGROUP BY子句进行排序。索引不能用于此,因为它不是表,而是排序的连接结果。

我认为没有什么可担心的。

如果您认为PostgreSQL没有使用正确的连接方法,您可以尝试SET enable_mergejoin=off并比较当时生成的计划。使用EXPLAIN (ANALYZE)查看优化程序是否正确估算;糟糕的计划者选择通常是由错误估计引起的。