【问题标题】:date at time zone related syntax and semantic differences时区日期相关的语法和语义差异
【发布时间】:2017-05-24 14:21:46
【问题描述】:

问题:查询 1 在“语义上”与查询 2 有何不同?

背景:

  1. 从我本地时区(AT TIME ZONE 'America/New_York')的数据库中的表中提取数据。
  2. 该表包含不同时区的数据,例如“America/Los_Angeles”、America/North_Dakota/New_Salem 和此类时区。 (Postgres 将不同时区的表数据存储在我的本地时区中)
  3. 因此,每次我检索本地时间以外的其他位置的数据时,我都会将其转换为相关时区以进行评估..

查询 1:

test_db=# select count(id) from click_tb where date::date AT TIME ZONE 'America/Los_Angeles' = '2017-05-22'::date  AT TIME ZONE 'America/Los_Angeles';
 count 
-------
  1001
(1 row)

查询 2:

test_db=# select count(id) from click_tb where (date AT TIME ZONE 'America/Los_Angeles')::date = '2017-05-22'::date;
 count 
-------
     5
(1 row)

表结构:

test_db=# /d+ click_tb
                                                               Table "public.click_tb"
              Column               |           Type           |                          Modifiers                          | Storage  | Stats target | Description 
-----------------------------------+--------------------------+-------------------------------------------------------------+----------+--------------+-------------
 id                                | integer                  | not null default nextval('click_tb_id_seq'::regclass)       | plain    |              | 
 date                              | timestamp with time zone |                                                             | plain    |              | 

Indexes:
    "click_tb_id" UNIQUE CONSTRAINT, btree (id)
    "click_tb_date_index" btree (date)
查询 1 和查询 2 不会产生一致的结果。 根据我的测试,以下查询 3 在语义上满足了我的要求。 欢迎您提出批评意见。 查询 3:
test_db=# select count(id) from click_tb where ((date AT TIME ZONE 'America/Los_Angeles')::timestamp with time zone)::date = '2017-05-22'::date;

【问题讨论】:

  • 第一个问题:为什么不将所有时间信息保存在 UTC 中,并带有一个表示该元组时区的附加字段?
  • 任何关系数据库的一般规则:永远不要在 where 子句中对表的字段使用函数。它将使您的查询non-sargable,这意味着它不能使用索引,因此它将扫描整个表,将您的函数应用于每一行。这对性能非常不利,尤其是在大型表上。
  • @MattJohnson:除非您使用该(函数)表达式创建索引。

标签: postgresql datetime etl postgresql-9.3 timestamp-with-timezone


【解决方案1】:

不要转换时间戳字段。相反,请执行范围查询。由于您的数据已经在使用 timestamp with time zone 类型,因此只需相应地设置查询的时区即可。

set TimeZone = 'America/Los_Angeles';
select count(id) from click_tb
where date >= '2017-01-02'
  and date <  '2017-01-03';

注意这如何使用日期的半开间隔(在设定时区的一天开始时)。如果你想从你的第一个日期计算第二个日期,那么:

set TimeZone = 'America/Los_Angeles';
select count(id) from click_tb
where date >= '2017-01-02'
  and date <  (timestamp with time zone '2017-01-02' + interval '1 day');

这可以正确处理夏令时和可搜索性。

【讨论】:

  • 感谢 SET 选项建议。使用 SET LOCAL 或 SET SESSION 选项可以最大限度地减少出错的机会,消除与类型转换的不一致并允许使用范围查询。
猜你喜欢
  • 1970-01-01
  • 2020-10-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-30
  • 2016-02-02
  • 2010-10-06
  • 2022-01-03
相关资源
最近更新 更多