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