具有两个参数的 PostgreSQL 中的间隔(天)

Interval (days) in PostgreSQL with two parameters

在此查询中:

Select * from table where future_date - CURRENT_DATE <= '10';

它returns间隔10天以内。

如何添加两个参数?示例:

Select * from table where future_date - CURRENT_DATE <= '10' AND >= '30';

我已经试过了:

Select * from table where future_date - CURRENT_DATE BETWEEN '10' AND '30'

但是没用。

SELECT  *
FROM    mytable
WHERE   future_date BETWEEN CURRENT_DATE + '10 days'::INTERVAL AND CURRENT_DATE + '30 days'::INTERVAL

说明

您的第一个 示例 是语法上的废话,但是您的 第二个示例是有效的 并且如果您的专栏实际上是date 就像列名所暗示的那样:

Select * from table where future_date - CURRENT_DATE BETWEEN '10' AND '30'

表达式 future_date - CURRENT_DATE 的结果取决于 future_date 的实际未公开的 数据类型

如果 future_datedatefuture_date - CURRENT_DATE 的结果是一个 integer 数字,表示天数差异,而您的未键入的 字符串文字 '10''30' 每次赋值转换都会转换为 integer:查询是 valid(即使效率低下)并涵盖21 天(不是 20 天)的范围。

如果future_date是一个timestamp (or timestamptz),结果是一个interval表示一个时间间隔。在这种情况下使用 CURRENT_DATE 会很奇怪但合法。假定一天中的 00:00 小时,该值被强制为 timestamp(或 timestamptz)。

但是,您的无类型字符串文字现在被转换为 interval 并且没有任何给定的时间单位 an integer number means seconds by default,因此谓词有效地从 [= 中选择了 10 到 30 秒的狭窄时间范围97=]现在.

自己看看:

SELECT '10'::interval

interval
---------
'00:00:10'

为了澄清错误信息CURRENT_DATE就好了。这是一个标准的 SQL 函数,由于遗留原因没有括号。曾经在 Postgres 内部实现为 now()::date。两者都是 STABLE 函数(所以是“运行时常量”)。您的表达式的另一个问题:它非常低效,因为它是 而不是 sargable.

正确的解决方案

对于 date 列:

SELECT *
FROM   mytable
WHERE  future_date BETWEEN CURRENT_DATE + 10
                       AND CURRENT_DATE + 30;  -- 21 days (!)

您只需 integer 添加到 date 即可增加/减少天数。

这为您提供了 21(不是 20!)天的范围,因为 BETWEEN 包括 下限和上限。通常,您希望包括下限但 排除 上限。

对于 timestamptimestamptz 列:

SELECT *
FROM   mytable
WHERE  future_date >= now() + interval '10 days'
AND    future_date <  now() + interval '30 days';  -- 20 days (!)

这涵盖了 20 天 (!) 的时间范围,分布在 21 个日历日 (!) 除非您从午夜开始 正好 ,在这种情况下正好是 20 个日历日完全覆盖。

通常,您希望使用 日历日作为界限:

... 
WHERE  future_date >= (CURRENT_DATE + 10)
AND    future_date <  (CURRENT_DATE + 30);

timestamptimestamptz 的这些表达式之一:

now()::date               + interval '10 days'  -- returns timestamp
CURRENT_DATE              + interval '10 days'  -- returns timestamp
date_trunc('day', now())  + interval '10 days'  -- returns timestamptz

数据类型转换为 future_date 的类型,因此它适用于任何一种类型。

请注意,日期 由其时区定义。所以这些表达式取决于会话的当前 timezone 设置。

现在应该很明显了,为什么 BETWEEN .. AND .. 通常 错误的 带有时间戳。大多数情况下,您想要 include 下限和 exclude 上限。 BETWEEN .. AND .. 将在最后一个示例中包括第二天的 00:00,从而打开第 21 天的角落案例。

相关:

  • Ignoring time zones altogether in Rails and PostgreSQL