postgresql - PostgreSQL 对同一查询使用不同的索引
问题描述
我有一个 SQL 查询,它在两个表上使用内连接并根据几个参数过滤数据。按照查询计划,对于不同的查询参数值(如不同的日期范围),Postgres 使用不同的索引。
我知道 Postgres 会根据结果集中的行数来确定是否必须使用索引。但是为什么 Postgres 选择对相同的查询使用不同的索引。在这两种情况下,查询时间相差 10 倍。如何优化查询?由于 Postgres 不允许用户定义要在查询中使用的索引。
编辑:
explain (analyze, buffers, verbose) SELECT COUNT(*) FROM "bookings" INNER JOIN "hotels" ON "hotels"."id" = "bookings"."hotel_id" WHERE "bookings"."hotel_id" = 37016 AND (bookings.status in (0,1,2,3,4,5,6,7,9,10,11,12)) AND (bookings.source in (0,1,2,3,4,5,6,7,8,9,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70) or bookings.status in (0,1,2,3,4,5,6,7,8,9,10,11,13)) AND (
bookings.source in (4,66,65)
OR
date(timezone('+05:30',bookings.created_at))>checkin
OR
(
( date(timezone('+05:30',bookings.created_at))=checkin
and
extract (epoch from COALESCE(cancellation_time,NOW())-bookings.created_at)>600
)
OR
( date(timezone('+05:30',bookings.created_at))<checkin
and
extract (epoch from COALESCE(cancellation_time,NOW())-bookings.created_at)>600
and
(
extract (epoch from ((bookings.checkin||' '||hotels.checkin_time)::timestamp -COALESCE(cancellation_time,bookings.checkin))) < extract(epoch from '16 hours'::interval)
OR
(DATE(bookings.checkout)-DATE(bookings.checkin))*(COALESCE(bookings.oyo_rooms,0)+COALESCE(bookings.owner_rooms,0)) > 3
)
)
)
) AND (bookings.checkin >= '2018-11-21') AND (bookings.checkin <= '2019-05-19') AND "bookings"."hotel_id" = '37016' AND "bookings"."status" IN (0, 1, 2, 3, 12);
查询计划: https ://explain.depesz.com/s/SPeb
explain (analyze, buffers, verbose) SELECT COUNT(*) FROM "bookings" INNER JOIN "hotels" ON "hotels"."id" = 37016 WHERE "bookings"."hotel_id" = 37016 AND (bookings.status in (0,1,2,3,4,5,6,7,9,10,11,12)) AND (bookings.source in (0,1,2,3,4,5,6,7,8,9,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70) or bookings.status in (0,1,2,3,4,5,6,7,8,9,10,11,13)) AND (
bookings.source in (4,66,65)
OR
date(timezone('+05:30',bookings.created_at))>checkin
OR
(
( date(timezone('+05:30',bookings.created_at))=checkin
and
extract (epoch from COALESCE(cancellation_time,now())-bookings.created_at)>600
)
OR
( date(timezone('+05:30',bookings.created_at))<checkin
and
extract (epoch from COALESCE(cancellation_time,now())-bookings.created_at)>600
and
(extract (epoch from ((bookings.checkin||' '||hotels.checkin_time)::timestamp -COALESCE(cancellation_time,bookings.checkin))) < extract(epoch from '16 hours'::interval)
OR
(DATE(bookings.checkout)-DATE(bookings.checkin))*(COALESCE(bookings.oyo_rooms,0)+COALESCE(bookings.owner_rooms,0)) > 3
)
)
)
) AND (bookings.checkin >= '2018-11-22') AND (bookings.checkin <= '2019-05-19') AND "bookings"."hotel_id" = '37016' AND "bookings"."status" IN (0,1,2,3,4,12);
查询计划: https ://explain.depesz.com/s/DWD
解决方案
终于找到了解决这个问题的办法。我根据列的 10 多个可能值(在本例中为状态)进行查询。如果我将此查询分解为多个子查询,每个查询仅针对 1 个状态值并使用 union all 聚合结果,则执行的查询计划对每个子查询使用优化索引。
结果:查询时间因这一变化而减少了 10 倍。
这种行为的可能解释是,查询计划器为每个子查询获取较少的行数,并在这种情况下使用优化的索引。我不确定这是否是正确的解释。
推荐阅读
- css - CSS:在某些选择器之前选择伪元素之后
- java - 在 JMeter 中发送肥皂消息中的动态值
- css - Vue + 顺风手风琴与过渡
- bootstrap-4 - 强制 justify-content-around 的最后一个元素与上一行的第一个元素对齐
- php - shopify如何删除应用程序卸载页面?
- java - TestNG beforeGroups 没有被执行
- python - 用于动态变量更改的 Jinja2 案例切换
- c - 需要帮助使用 qsort 从结构中对数组进行排序
- javascript - 通过选择 Div 属性更改 Div 的宽度
- sql - Oracle max(date) 显示不同的结果