如何比较 Postgresql 中日期时间字段中的日期?
Posted
技术标签:
【中文标题】如何比较 Postgresql 中日期时间字段中的日期?【英文标题】:How to compare dates in datetime fields in Postgresql? 【发布时间】:2013-10-28 10:44:53 【问题描述】:在比较 postgresql(Windows 中的版本 9.2.4)中的日期时,我遇到了一个奇怪的情况。 我的表中有一个列说 update_date 类型为“没有时区的时间戳”。 客户可以仅使用日期(即:2013-05-03)或日期与时间(即:2013-05-03 12:20:00)搜索此字段。此列的值为时间戳当前所有行具有相同的日期部分(2013-05-03)但时间部分不同。
当我比较这个专栏时,我得到了不同的结果。像下面这样:
select * from table where update_date >= '2013-05-03' AND update_date <= '2013-05-03' -> No results
select * from table where update_date >= '2013-05-03' AND update_date < '2013-05-03' -> No results
select * from table where update_date >= '2013-05-03' AND update_date <= '2013-05-04' -> results found
select * from table where update_date >= '2013-05-03' -> results found
我的问题是如何使第一个查询能够获得结果,我的意思是为什么第三个查询有效但第一个查询无效?
【问题讨论】:
【参考方案1】:当您比较 update_date >= '2013-05-03'
时,postgres 会将值转换为相同的类型以比较值。因此,您的“2013-05-03”被转换为“2013-05-03 00:00:00”。
所以对于 update_date = '2013-05-03 14:45:00' 你的表达式将是:
'2013-05-03 14:45:00' >= '2013-05-03 00:00:00' AND '2013-05-03 14:45:00' <= '2013-05-03 00:00:00'
这总是false
要解决此问题,请将 update_date 转换为 date
:
select * from table where update_date::date >= '2013-05-03' AND update_date::date <= '2013-05-03' -> Will return result
【讨论】:
强制转换表中的每个update_date
与强制转换查询参数的单个值的效率都非常低,并确保服务器无法利用该列上的索引。我很想 -1 这个。
是的,我同意每个值的转换效率低下,您可以为此解决方案给出 -1。但我描述了问题的原因并给出了说明问题的例子。现在 user2866264 知道为什么他的查询没有返回预期的行,并将决定哪种解决方案更适合他的独特情况。
@Nicolai:非常感谢您的回答。它通过遵循您的答案来工作。也感谢您的解释。
@Nicolai – 鉴于您所说的 Postgres 将日期文字扩展到午夜的中风,如果目标是查找标记在单个日期(5 月 3 日)的记录,那么这段代码是否正确且更有效:SELECT * FROM my_table WHERE update_date >= '2013-05-03' AND update_date < '2013-05-04';
(注意使用 5 月 4 日而不是 3 日,并且使用 LESS-THAN SIGN 而不是小于或等于。)【参考方案2】:
使用range
类型。如果用户输入日期:
select *
from table
where
update_date
<@
tsrange('2013-05-03', '2013-05-03'::date + 1, '[)');
如果用户输入时间戳,则不需要 ::date + 1
部分
http://www.postgresql.org/docs/9.2/static/rangetypes.html
http://www.postgresql.org/docs/9.2/static/functions-range.html
【讨论】:
这是一个简洁有趣的答案!【参考方案3】:@Nicolai 关于强制转换以及为什么任何数据的条件都是错误的都是正确的。我想您更喜欢第一种形式,因为您想避免对输入字符串进行日期操作,对吗?你不必害怕:
SELECT *
FROM table
WHERE update_date >= '2013-05-03'::date
AND update_date < ('2013-05-03'::date + '1 day'::interval);
【讨论】:
这个语法('2013-05-03'::date
和 '1 day'::interval
)是 PostgreSQL 特有的吗?
@FrozenFlame 是的。标准语法是CAST('2013-05-03' AS DATE) + CAST('1 day' AS INTERVAL)
(IIRC)。 YMMV 关于DATE
和INTERVAL
的存在和行为。
@FrozenFlame 是正确的,如果不将字符串转换为日期类型,答案将不起作用。一名演员仍然失踪。需要在 where 子句的第一部分添加一个 ::DATE
WHERE update_date::date = '2013-05-03'
不也能工作,而且可读性稍微好一点?
@MikeF OP 说 update_date
是 timestamp without timezone
。我假设该列上有一个索引。您的谓词不会使用该索引。【参考方案4】:
使用日期转换与日期进行比较: 试试这个:
select * from table
where TO_DATE(to_char(timespanColumn,'YYYY-MM-DD'),'YYYY-MM-DD') = to_timestamp('2018-03-26', 'YYYY-MM-DD')
【讨论】:
【参考方案5】:您也可以使用BETWEEN
运算符。
这是一个简单的例子:
SELECT
customer_id,
payment_id,
amount,
payment_date
FROM
payment
WHERE
payment_date BETWEEN '2007-02-07' AND '2007-02-15';
您还可以选择不在这些日期之间的所有内容:
SELECT
customer_id,
payment_id,
amount,
payment_date
FROM
payment
WHERE
payment_date NOT BETWEEN '2007-02-07' AND '2007-02-15';
这是一个更高级的示例,涉及基于天的时间戳增量:
SELECT
api_project.name,
api_project.created,
survey_response.created AS response_date,
CASE
WHEN survey_response.created
BETWEEN api_project.created AND
(api_project.created + INTERVAL '180 days')
THEN 'first_6_months'
ELSE '6_months_after'
END AS when_it_was_answered,
EXTRACT(DAYS FROM survey_response.created - api_project.created)
AS days_since_response
FROM
bfb_survey_surveyresponseppent
【讨论】:
以上是关于如何比较 Postgresql 中日期时间字段中的日期?的主要内容,如果未能解决你的问题,请参考以下文章
如何在 postgresql 视图中获取两个日期时间字段的天数和小时数差异?