我有一个超过 450 万行的表,而我的 SELECT
查询对于我的需求来说太慢了。
创建表时使用:
CREATE TABLE all_legs (
carrier TEXT,
dep_hub TEXT,
arr_hub TEXT,
dep_dt TIMESTAMP WITH TIME ZONE,
arr_dt TIMESTAMP WITH TIME ZONE,
price_ct INTEGER,
... 5 more cols ...,
PRIMARY KEY (carrier, dep_hub, arr_hub, dep_dt, arr_dt, ...3 other cols...)
)
当我想对某个日期的所有行进行 SELECT
时,查询速度太慢;它需要 12 秒到 20 秒。我的目标是最多需要 1 秒。我希望查询返回表中包含的行的 0.1% 到 1%。
查询非常简单:
SELECT * FROM all_legs WHERE dep_dt::date = '2017-08-15' ORDER BY price_ct ASC
EXPLAIN ANALYZE
返回:
Sort (cost=197154.69..197212.14 rows=22982 width=696) (actual time=14857.300..14890.565 rows=31074 loops=1)
Sort Key: price_ct
Sort Method: external merge Disk: 5256kB
-> Seq Scan on all_legs (cost=0.00..188419.85 rows=22982 width=696) (actual time=196.738..14581.143 rows=31074 loops=1)
Filter: ((dep_dt)::date = '2017-08-15'::date)
Rows Removed by Filter: 4565249
Planning time: 0.572 ms
Execution time: 14908.274 ms
注意:我昨天才知道这个命令,所以我仍然不能完全理解返回的所有内容。
我已经按照 here 的建议尝试使用 index-only scans
,方法是运行命令:CREATE index idx_all_legs on all_legs(dep_dt);
但我没有注意到运行时间有任何差异。我还尝试为所有列创建索引,因为我希望所有列都返回。
另一个想法是按 dep_dt
对所有行进行排序,这样搜索满足条件的所有行应该会快得多,因为它们不会分散。不幸的是,我不知道如何实现它。
有没有办法让它像我的目标一样快?
解决方案
正如 Laurenz' answer 中所建议的,通过添加索引 CREATE INDEX IF NOT EXISTS idx_dep_dt_price ON all_legs(dep_dt, price_ct);
并将 SELECT
中的条件调整为 WHERE dep_dt >= '2017-08-15 00:00:00' AND dep_dt < '2017-08-16 00:00:00'
已将运行时间减少到 1/4。即使这是一个非常好的改进,这也意味着运行时间在 2 到 6 秒之间。
任何进一步减少运行时间的额外想法将不胜感激。
最佳答案
索引无济于事。
两种解决方案:
您可以将查询更改为:
WHERE dep_dt >= '2017-08-15 00:00:00' AND dep_dt < '2017-08-16 00:00:00'
然后就可以使用索引了。
在表达式上创建索引:
CREATE INDEX ON all_legs(((dep_dt AT TIME ZONE 'UTC')::date));
(或不同的时区)并将查询更改为
WHERE (dep_dt AT TIME ZONE 'UTC')::date = '2017-08-16'
AT TIME ZONE
是必需的,否则转换结果将取决于您当前的TimeZone
设置。
第一个解决方案更简单,但第二个的优点是您可以像这样将 price_ct
添加到索引中:
CREATE INDEX ON all_legs(((dep_dt AT TIME ZONE 'UTC')::date), price_ct);
那么您就不再需要排序了,您的查询将达到理论上的最快速度。
关于PostgreSQL:在具有数百万行的表中加速 SELECT 查询,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45320455/