使用 current_date::date 而不是硬编码日期的 Postgres 查询非常慢

Posted

技术标签:

【中文标题】使用 current_date::date 而不是硬编码日期的 Postgres 查询非常慢【英文标题】:Postgres query is very slow with current_date::date instead of hardcoded date 【发布时间】:2009-09-29 23:01:31 【问题描述】:

我有相当长且复杂的 SQL 查询,它是针对 PostgreSQL 8.3 运行的。部分查询涉及过滤以今天结尾的日期范围,如下所示:

where ...
  and sp1.price_date between current_date::date - '1 year'::interval and current_date::date
  and sp4.price_date between current_date::date - '2 weeks'::interval and current_date::date
  and sp5.price_date = (select sp6.price_date 
                          from stock_prices sp6 
                         where sp6.stock_id = s.stock_id 
                           and sp6.price_date < current_date::date 
                      order by sp6.price_date desc 
                         limit 1)
  ...

此查询运行(第一次)大约需要 5 分钟,第二次运行大约需要 1.5 分钟。从 EXPLAIN ANALYZE 输出看来,current_date 似乎是问题所在。所以我尝试用硬编码的日期替换它,如下所示:

where ...
  and sp1.price_date between '2009-09-30'::date - '1 year'::interval and '2009-09-30'::date
  and sp4.price_date between '2009-09-30'::date - '2 weeks'::interval and '2009-09-30'::date
  and sp5.price_date = (select sp6.price_date 
                          from stock_prices sp6 
                         where sp6.stock_id = s.stock_id 
                           and sp6.price_date < '2009-09-30'::date 
                      order by sp6.price_date desc 
                         limit 1)
  ...

查询然后在半秒内运行!这很好,除了日期在查询中总共出现 10 个位置,当然,我不希望用户必须在 10 个位置手动更改它。在 MS SQL Server 中,我会简单地用当前日期的值声明一个变量并使用它,但是 apparently 在 Postgres 的普通 SQL 中是不可能的。

如何在自动使用当前日期的同时使该查询快速运行?

【问题讨论】:

current_date::date 这很奇怪,如果 ::date 已经是 date 类型,为什么还需要它呢?此外,如果您从日期中添加/减去间隔,则结果无论如何都会是时间戳 【参考方案1】:

首先,在两个变体上发布EXPLAIN ANALYZE,以便我们查看。弄清楚为什么一个比另一个慢的第一步。查看整个查询可能也很有用。

第一个变体应该是可优化的。

为了不让您的用户在多个地方更改您的查询,请考虑编写stored procedure,或者如果/当您的第一个变体得到优化时,请考虑编写view。

编辑:注意到您的 current__date - '...'::interval 将返回一个不带时区的时间戳。我假设您想改为日期: (current_date - '2 week'::interval)::date

【讨论】:

抱歉,我确实做了“解释分析”,而不是“解释”(已编辑)。不过,我不确定如何优化它。我尝试编写一个“RETURNS SETOF RECORD”函数来执行查询并在该函数中使用一个参数,但该函数在 10 分钟后没有返回,此时我取消了它。 @Evgeny:对自身运行解释分析没有帮助。您必须阅读它的输出并找到问题。 @Evgeny:也许我有点不清楚。请在此处发布解释分析的结果。【参考方案2】:

编辑:以下内容已经过测试,但运行速度比原始查询还要慢!。 关于这一点的教训可能是所有类型转换(::date、::interval 等)都会导致性能下降。也许这些显式转换可以以某种方式替换为其他内容,并且还可以预先计算一些表达式,例如 'D.RightNow::date - '1 year'::interval'。

--原始回复-- 您可以将当前日期插入一个空表并加入该表...

换句话说,假设创建了这样一个表并命名为 tblNow,带有日期相关过滤器的查询可能会变成这样:

UPDATE tblNow SET RightNow = TIMEOFDAY();  
-- note: above could use CURRENT_DATE  or whatever date function matches the
-- semantics of the date fields in other tables.

-- and now the original query can become

from ...
join tblNow AS D ON 1=1 -- added join
                   -- then all current_date::date below changed to D.RightNow

where ...
  and sp1.price_date between D.RightNow::date - '1 year'::interval and D.RightNow::date
  and sp4.price_date between D.RightNow::date - '2 weeks'::interval and D.RightNow::date
  and sp5.price_date = (select sp6.price_date 
                          from stock_prices sp6 
                         where sp6.stock_id = s.stock_id 
                           and sp6.price_date < D.RightNow::date 
                      order by sp6.price_date desc 
                         limit 1)
  ...

每次我们希望在当前时刻运行查询时,实际上都需要编辑查询。我不熟悉 postgreSQL,但对于在 SELECT 语句中使用变量的任何限制,这种方法将是一种自然的解决方法。

【讨论】:

这对他有什么帮助?他必须在单独的表中添加/更新行,而不是更改查询? @etlerant :OP提到必须在查询中的10多个位置更改日期,这种方法将允许1)单个位置和2)根本不更改查询,如果使用 timeofday() 的 UPDATE 查询在与工作相关的查询之前。 谢谢,我试过了,但没有帮助 - 查询运行了 10 分钟,然后我放弃并取消了它。 @Evgeny。对不起,我带你走错了路。我相应地编辑了我的回复以记录这种见解(如此缓慢)。也许缓慢与大量类型转换有关,也许我们可以通过在 tblNow 的唯一记录中存储一些可以预先计算某些日期表达式的附加字段来提供帮助。

以上是关于使用 current_date::date 而不是硬编码日期的 Postgres 查询非常慢的主要内容,如果未能解决你的问题,请参考以下文章

在 bigquery 中循环查询

Hive date/timestamp/date_sub/date_add/date_format/日期时间格式转换

Hive date/timestamp/date_sub/date_add/date_format/日期时间格式转换

Hive date/timestamp/date_sub/date_add/date_format/日期时间格式转换

Hive date/timestamp/date_sub/date_add/date_format/日期时间格式转换

如何 用mysql语句获取本季度的第一天 本月