最近,一位 StackOverflow 用户告知我,将
BETWEEN
运算符与数据类型为 timestamp without time zone
不应该 的值一起使用。下面是引用。
Between 表示 >= 和 <= and shall not be used with ranges that contain timestamps.
当被要求解释这篇论文或 Postgres 文档的链接时,它指出我已经得到了答案
为什么这么简单的事情需要一个带有文档的网站。我相信如果你用谷歌搜索的话你可以找到很多(至少我在各个论坛上的详细帖子展示了这个案例)
好吧,我用谷歌搜索了。并且没有发现任何建议反对将此运算符与时间戳值一起使用的信息。事实上,这个答案使用了它们,这个邮件组帖子也使用了它们。
我被告知这些年来我都做错了。真的是这样吗?
据我所知,Postgres 时间戳的最大精度是
1 microsecond
- 如果我错了,请纠正我。那么下面的陈述不是等价的吗?
sample_date BETWEEN x AND y::timestamp - INTERVAL '1 microsecond'
和
sample_date >= x AND sample_date < y
编辑:示例只是对差异的考虑。我知道开发人员可能会错过时间部分,但假设人们知道它的行为方式,为什么不应该使用它?一般来说,这只是一个示例,但我想知道更大的范围。我一直在调查规划器,它似乎正在将 BETWEEN
解析为
>= AND <=
。为什么在结果
的问题上,人们更喜欢写
>= AND <=
而不是BETWEEN
——不包括翻译时间?
ts BETWEEN validfrom AND validto
代替
ts >= validform AND ts <= validto
绝对没有任何问题。他们是一样的。我只能猜测,但我想说警告的目标不同,即上面的(相同)子句是否是正确的使用方式。
现在这当然取决于您想要做什么,但很多时候像这样的子句用于识别特定时间戳的一个有效行。在这种情况下,上面的子句是错误的,因为对于
ts
的值,当行更改时,您将得到
two结果。 考虑一下:
CREATE TABLE names (
id integer PRIMARY KEY,
val text NOT NULL,
validfrom timestamptz NOT NULL,
validto timestamptz NOT NULL
);
INSERT INTO names VALUES (1, 'Smith', '1985-05-02 00:00:00', '2009-01-30 00:00:00');
INSERT INTO names VALUES (2, 'Jones', '2009-01-30 00:00:00', 'infinity');
这是一个人名的历史表。
如果您使用像上面这样的
WHERE
子句来查询在特定时间有效的名称,它会很好地用于
SELECT val FROM names
WHERE current_timestamp BETWEEN validfrom AND validto;
但这会做错事
SELECT val FROM names
WHERE '2009-01-30' BETWEEN validfrom AND validto;
这是因为名称有效期的终点不是
区间的一部分。对于这种情况,正确的写法是:
SELECT val FROM names
WHERE '2009-01-30' >= validfrom AND '2009-01-30' < validto;
...实际上可能仍然由于闰秒而被破坏。但让我们忽略这一点...
IMO 不使用 BETWEEN 的最大原因是因为它告诉数据库要做的事情实际上与我们大脑解释它的方式不匹配。如果我说“它发生在 1 月 3 日到 1 月 6 日之间的某个时间”,大多数(或至少很多)人会将其翻译为间隔“[1 月 3 日,1 月 6 日)”,这绝对不是您使用 SQL 表达式得到的结果“‘1 月 3 日’和‘1 月 6 日’之间”。因此,尽管该 SQL 表达式具有完全定义的含义,但该含义与大多数/多少人用英语阅读它的数量相冲突。但如果你使用 >= AND
<, there's absolutely no question about what the intention is.