【问题标题】:Match PotsgreSQL timestamptz by exact value按确切值匹配 PotsgreSQL timestamptz
【发布时间】:2023-11-18 22:45:02
【问题描述】:

我通过 Supbase.io 使用 PostgreSQL 13.3。我有一个表,其中包含一个名为 modified_at 的字段,该字段的类型为 timestamptz:

CREATE TABLE IF NOT EXISTS knowledge_views (
  id uuid NOT NULL DEFAULT uuid_generate_v4() PRIMARY KEY,
  modified_at timestamp with time zone DEFAULT timezone('utc'::text, now()) NOT NULL
);

我有一个条目,其中 modified_at 是:2021-09-27T20:55:25.625Z(编辑:不是,Supabase 目前隐藏了微秒)。以下陈述均无效:

SELECT * FROM knowledge_views WHERE modified_at = timestamptz '2021-09-27T20:55:25.625Z';

SELECT * FROM knowledge_views WHERE modified_at = '2021-09-27T20:55:25.625Z'::timestamptz;

SELECT * FROM knowledge_views WHERE modified_at = to_timestamp(1632776125.625);

SELECT * FROM knowledge_views WHERE modified_at >= to_timestamp(1632776125.625) AND modified_at <= to_timestamp(1632776125.625);

但是,如果执行最后一个查询并将&lt;= 的毫秒数增加一到1632776125.626,那么它会正确找到该行:

SELECT * FROM knowledge_views WHERE modified_at >= to_timestamp(1632776125.625) AND modified_at <= to_timestamp(1632776125.626);

有没有办法通过精确的 timestamptz 值(包括毫秒)来选择一行?

如果这是不可能的,那么将最大毫秒值加 1 的(hacky)方法是否稳健?或者我也应该从最小值减一,例如:&gt;= to_timestamp(1632776125.624) AND modified_at &lt;= to_timestamp(1632776125.626)

** 编辑 **

使用以下显示该字段实际上以微秒精度存储:

SELECT modified_at as original, cast(extract(epoch from modified_at) * 1000000 as bigint) FROM knowledge_views;
original int8
2021-09-27T20:55:25.625Z 1632776125625535

我意识到它在客户端保持时间戳值字符串不变之前可以工作,即:

supabase.from("knowledge_views").select("*").eq("modified_at", "2021-09-27T20:55:25.625535")

这在我使用时坏了:new Date("2021-09-27T20:55:25.625535") 减少了微秒。

【问题讨论】:

  • 您的时区设置为?你在运行社区 PostgreSQL 还是一些 fork?你是怎么安装的?您有哪些非默认设置?什么操作系统和版本?

标签: javascript postgresql timestamp equality supabase


【解决方案1】:

我有一个条目,其中 modified_at 是:2021-09-27T20:55:25.625Z

你怎么知道那是确切的值?我问是因为 Postgres 时间戳具有 微秒分辨率(6 个小数位)。默认情况下不显示尾随零,但now()(在您的奇数列DEFAULT)产生一个精确到毫秒的时间戳的机会是千分之一。基础知识:

我的猜测是您的客户报告时间戳四舍五入到毫秒(可能是由于不幸的设置?)。在像default PostgreSQL interactive terminal psql 这样的理智客户端中运行SELECT * FROM knowledge_views; 以获得实际值。然后你会发现= 操作符对timestamptz 的工作正常。

【讨论】:

  • 这可能会澄清具有“不幸设置”的客户的情况。 select to_char(modified_at, '.FF3') mils, to_char(modified_at, '.US') micros, modified_at from knowledge_views;。它清楚地证实了上述 - 一个截断问题。
  • sane client 是这里的日常任务!谢谢。
  • @Stefanov.sm 这非常有用。谢谢你。它显示:| .625 | .625535 | 2021-09-27T20:55:25.625Z |
  • 我将在插入、更新时使用now()::timestamp(3),以便将日期时间字符串与缺少微秒的 javascript Date 一起使用。
【解决方案2】:

我会放弃DEFAULT timezone('utc'::text, now())timestamptz 值已存储在 UTC 中,您所做的只是以您不想要的方式转置该值:

 show timezone;
  TimeZone  
------------
 US/Pacific

create table tstz_test(id integer, tstz_fld timestamptz);

insert into tstz_test values (1, now()), (2, timezone('UTC', now()));

select * from tstz_test ;
 id |           tstz_fld            
----+-------------------------------
  1 | 2021-09-27 16:56:35.964202-07
  2 | 2021-09-27 23:56:35.964202-07

select tstz_fld AT TIME ZONE 'UTC' from tstz_test ;
          timezone          
----------------------------
 2021-09-27 23:56:35.964202
 2021-09-28 06:56:35.964202

 select * from tstz_test where tstz_fld = '2021-09-27 23:56:35.964202Z'::timestamptz;
 id |           tstz_fld            
----+-------------------------------
  1 | 2021-09-27 16:56:35.964202-07

所以毫秒不是问题。

【讨论】: