日期与时区相关的语法和语义差异

时间:2017-05-24 14:21:46

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

问题:查询1"语义如何"不同于查询2?

背景:

  1. 从位于我当地时区的数据库中的表中提取数据(AT TIME ZONE' America / New_York')。
  2. 该表包含各种时区的数据,例如' America / Los_Angeles',America / North_Dakota / New_Salem以及此类时区。 (Postgres在我当地时区存储各种时区的表格数据)
  3. 因此,每当我检索除本地时间以外的其他位置的数据时,我会将其转换为相关的时区以进行评估..
  4. 查询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;
    

1 个答案:

答案 0 :(得分: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');

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