【问题标题】:postgres json_agg() ignores index in joined tables in GROUP BY expressionspostgres json_agg() 在 GROUP BY 表达式中忽略连接表中的索引
【发布时间】:2017-04-22 09:22:39
【问题描述】:

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

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”被声明为索引,但还是显式进行了排序。
如果我将聚合“时间表”表而不是“剂量”,那么查询会变得非常快:

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():
选择 bioid,json_agg (td) jtd
FROM (时间表左加入剂量使用 (doseid)) td
按生物分类

我正在使用 Postgres 9.3

【问题讨论】:

    标签: postgresql indexing query-optimization aggregate-functions


    【解决方案1】:

    但是查询确实使用了索引,只是没有使用您期望的索引。

    PostgreSQL 估计使用合并连接可以节省成本。为此,两个表中的数据必须按连接条件排序,这是使用索引doses_pkeytimetables_doseid_idx 完成的。
    加入后,条目按doseid 排序,因此必须再次按bioidGROUP BY 子句进行排序。不能为此使用索引,因为它不是表,而是已排序的连接结果。

    我觉得没什么好担心的。

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

    【讨论】:

    • 禁用mergejoin几乎不影响最终成本。是否可以使用索引对连接结果进行排序?
    • 这不是关于成本,您应该比较实际运行时间。正如我所说,您不能使用索引对连接结果进行排序。这应该如何工作?
    猜你喜欢
    • 2012-02-05
    • 1970-01-01
    • 2020-12-08
    • 1970-01-01
    • 2013-03-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多