如何比较 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 &gt;= '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 &gt;= '2013-05-03' AND update_date &lt; '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 关于DATEINTERVAL 的存在和行为。 @FrozenFlame 是正确的,如果不将字符串转换为日期类型,答案将不起作用。一名演员仍然失踪。需要在 where 子句的第一部分添加一个 ::DATE WHERE update_date::date = '2013-05-03' 不也能工作,而且可读性稍微好一点? @MikeF OP 说 update_datetimestamp 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 视图中获取两个日期时间字段的天数和小时数差异?

错误:Postgresql 中的日期/时间字段值超出范围

比较jpa中的日期

graphql prisma node js postgresql如何将创建日期/更新字段添加到graphql类型?

postgresql 将数字转换为日期和格式

Laravel Eloquent 比较日期时间字段中的日期