0

问题:查询1在语义上与查询2有什么不同?日期与时区相关的语法和语义差异

背景:

  1. 要在分贝这是在我本地时间区(AT TIME ZONE '美国/纽约')提取从表中的数据。
  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) 
 
The query 1 and query 2 do not produce consistent results. 
As per my tests, the below query 3, semantically addresses my requirement. 
Your critical feedback is welcome. 
 
Query 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; 
+0

第一个问题:你为什么不用UTC保存所有的时间信息,并用一个代表该tupel时区的附加字段? – GottZ

+0

关于任何关系数据库的一般规则:请勿对where子句中的表字段使用函数。它会使你的查询[非可搜索](https://en.wikipedia.org/wiki/Sargable),这意味着它不能使用索引,所以它会扫描整个表,将你的函数应用到每一行。这对性能非常不利,特别是在大型桌面上。 –

+0

@MattJohnson:除非你用那个(函数)表达式创建一个索引。 –

回答

0

不要转换时间戳字段。相反,做一个范围查询。由于您的数据已使用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'); 

这样可以正确处理夏令时和可控性。

+0

感谢SET选项建议。使用SET LOCAL或SET SESSION选项可以最大限度地减少出错的机会,消除与类型转换不一致并允许使用范围查询。 – WorkerBee