我在优化 SQL 查询时遇到了大问题,该查询需要很长时间才能在一组大约 300,000 行的数据上运行。
我正在包含十进制 value
和日期时间 recorded_at
列的 stat_records
表上运行查询。
我想找出以下任意时间段内的 MAX 和 MIN 值:所有时间、去年、过去 6 个月、过去 3 个月、上个月、过去 2 周。
我现在的做法是针对上面指定的每个时间间隔单独运行以下 SQL 查询:
SELECT MIN("stat_records"."value")
FROM "stat_records"
INNER JOIN "stats" ON "stats"."id" = "stat_records"."stat_id"
WHERE "stat_records"."object_id" = $1
AND "stats"."identifier" = $2
AND ("stat_records"."recorded_at" BETWEEN $3 AND $4)
[["object_id", 1],
["identifier", "usd"],
["recorded_at", "2018-10-15 20:10:58.418512"],
["recorded_at", "2018-12-15 20:11:59.351437"]]
表定义为:
create_table "stat_records", force: :cascade do |t|
t.datetime "recorded_at"
t.decimal "value"
t.bigint "coin_id"
t.bigint "object_id"
t.index ["object_id"], name: "index_stat_records_on_object_id"
t.index ["recorded_at", "object_id", "stat_id"], name: "for_upsert", unique: true
t.index ["recorded_at", "stat_id"], name: "index_stat_records_on_recorded_at_and_stat_id", unique: true
t.index ["recorded_at"], name: "index_stat_records_on_recorded_at"
t.index ["stat_id"], name: "index_stat_records_on_stat_id"
t.index ["value"], name: "index_stat_records_on_value"
end
然而,这种方法需要很长时间才能完成。我在 stat_records
表的 value
和 recorded_at
列上都有索引。
我在这里缺少什么 - 我应该做什么来优化它?
也许有一些更好的方法,我可以执行 1 个查询,并让 postgres 为我进行优化。
最佳答案
索引只能加速需要表的较小部分(或排序)的查询。因此,您永远不能指望索引能够使整个时间范围内的查询更快。
您的解决方案可以是物化 View 。这样,您可以预先聚合值,并且生成的表会小得多,因此查询速度会更快。缺点是物化 View 需要定期刷新,并且其间包含稍微陈旧的数据。
一个例子:
CREATE MATERIALIZED VIEW stats_per_month AS
SELECT stat_records.object_id,
stats.identifier
date_trunc('month', stat_records.recorded_at) AS recorded_month,
min(stat_records.value) AS minval
FROM stat_records
INNER JOIN stats ON stats.id = stat_records.stat_id
GROUP BY stat_records.object_id,
stats.identifier
date_trunc('month', stat_records.recorded_at);
如果您的查询需要月份粒度,您只需从物化 View 而不是原始表中查询即可。
您还可以使用混合解决方案,并对小范围使用原始查询,其中过时的数据可能会造成更大的伤害。使用 recorded_at
上的索引应该会很快。
关于postgresql - 优化具有多个最小和最大范围的 SQL 查询,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53797134/