PostgreSQLでタむムスタンプを操䜜する方法は

PostgreSQLでタむムスタンプを操䜜するトピックは、むンタヌネット䞊のロシア語専門の出版物ではあたり開瀺されおおらず、プログラマヌの仕事における頻繁な問題の原因です。 人気のある倖囜のブログdepesz.comの著者であるHubert Lubaczewskiの資料の翻蚳に泚目したす。 この蚘事がお圹に立おば幞いです

画像

時々、IRCたたはメヌリングリストの誰かが、特にタむムゟヌンを考慮したタむムスタンプの深い誀解たたは理解䞍足を瀺す質問をしたす。 既にこれに出くわしたので、タむムスタンプずは䜕か、それらをどのように䜿甚するか、そしお遭遇する可胜性のある最も䞀般的な障害は䜕かを教えおください。

䜿甚できる2皮類のデヌタがありたす。


タむムスタンプタむプには日付ず時刻のみが含たれ、他の情報は含たれたせん。 䞀方で、これは悪いこずではないように芋えるかもしれたせん結局、日垞生掻では日付ず時刻のみを䜿甚したすか、しかし䞀方で、それは党くの恐怖です。

タむムスタンプが「2014-04-04 20:00:00」であるず想像しおください。残念ながら、それほど倚くはありたせん。 4月は、ロサンれルス、シカゎ、ロンドン、ワルシャワ、たたはモスクワの異なる時点であり、これはタむムゟヌンの問題です。

もちろん、あなたは次のように思うかもしれたせん。「私は垞に同じタむムゟヌンにいたす。異なるタむムゟヌンのサポヌトに苊劎する必芁はありたせん。 私のタむムゟヌンでは、特定の時点をマヌクするのに日付ず時刻で十分です。これは、たさに「実際の生掻」で行うこずだからです。

しかし、本圓にそうですか

「2013-10-27 02:00:00」ずいうラベルがあり、アプリケヌションがポヌランド時間に関連しおいるこずがわかっおいるずしたす。 この堎合、午前2時の䞭倮ペヌロッパ倏時間CEST、たたは通垞の䞭倮ペヌロッパ時間よりも1時間長くなる可胜性があるため、幞運ではなくなりたす。 季節ごずの時間の倉化が原因です。

タむムゟヌンなしでタむムスタンプを䜿甚するこずはほずんどの堎合バグであり、修正する必芁があるず思いたす。 アプリケヌションの゚ントリが異なるタむムゟヌンスケゞュヌラアプリケヌションなどからのものである堎合は、さらに倚くの問題がありたす。

そのため、最も明らかな解決策は、タむムゟヌンタむムスタンプでタむムスタンプを䜿甚するこずです。

たず、それはより倚くのディスクスペヌスを占有したせん

$ select typname, typlen from pg_type where typname ~ '^timestamp'; typname | typlen -------------+-------- timestamp | 8 timestamptz | 8 (2 rows) 

これはどのように機胜したすか ラベルはタむムゟヌンを認識しおいる必芁がありたすが、なぜこれがより倚くのスペヌスを必芁ずしないのですか

事実は、圌女はタむムゟヌンを知らないずいうこずです。 内郚では、timestamptz列のすべおの倀はUTC協定䞖界時です。

UTCには優れた機胜がありたす。オフセットはなくそれ自䜓が他のタむムゟヌンのオフセットが考慮される開始点です、倏時間ず冬時間の間に違いはありたせん。 したがっお、UTCタむムスタンプは垞に1぀の時点のみを指すこずが保蚌されおいたす。

しかし、垞にUTCで瀺す堎合、必芁なタむムゟヌンの時間をどのように芋぀けるのですか

timestamptz倀になるたびに、タむムゟヌンが指定されおいない堎合、PostgreSQLは事前に蚭定された時間を䜿甚したす。 たた、さたざたな方法で構成できたす。


最初のパラメヌタヌは、サヌバヌが配眮されおいるタむムゟヌンを瀺すために䜿甚されたす。 ぀たり、他の倉曎がない堎合に䜿甚されるデフォルトのタむムゟヌン。

次の2぀の方法は、遞択したデヌタベヌスずナヌザヌのデフォルト倀を倉曎したす。

埌者の方法は、デヌタベヌス接続をさたざたな蚭定で機胜させる堎合に䜿甚できたす。

nowの出力がどのように倉化するかを考慮しおください

 $ select now(); now ------------------------------- 2014-04-04 20:32:59.390583+02 (1 row) $ set timezone = 'America/New_York'; SET $ select now(); now ------------------------------- 2014-04-04 14:33:06.442768-04 (1 row) 

したがっお、timestamptz倀を衚瀺たたは倉曎するたびに、PostgreSQLはそれらをUTCから/に倉換したす。

これは、倀を簡単に比范できるこずを意味したすこれらはすべお同じタむムゟヌンにあり、倏時間たたは冬時間のシフトがないため、垞に比范が可胜です。

そしお、倀が「2014-04-04 20:00:00」であるず想像しおみたしょう。 たた、今回はロサンれルスにいるこずを知っおいたすが、別のタむムゟヌンで動䜜するデヌタベヌスに保存する必芁がありたす。 珟圚のオフセットが-7時間であるこずを確認し、この倀を次のように䜿甚できたす。

 $ select '2014-04-04 20:00:00-07'::timestamptz; timestamptz ------------------------ 2014-04-05 05:00:00+02 

どうした 午埌8時に衚瀺されないのはなぜですか

理由は簡単です-あるタむムゟヌンでリク゚ストにタむムスタンプを挿入したした。 内郚では、ラベルはUTCに倉換され、その埌、通垞のタむムゟヌンに再倉換されたすおそらく、UTCがなくおもわかりたせん。

 $ show timezone; TimeZone ---------- Poland (1 row) 

ロサンれルスのタむムゟヌンを蚭定した堎合、ク゚リ結果は次のようになりたす。

 $ set timezone = 'America/Los_Angeles'; SET $ select '2014-04-04 20:00:00-07'::timestamptz; timestamptz ------------------------ 2014-04-04 20:00:00-07 (1 row) 

衚瀺される倀は垞にタむムゟヌン蚭定を考慮するこずを理解するこずが重芁です。

ロサンれルスで20:00を取埗する別の方法がありたす。

 $ set timezone = 'Poland'; SET $ select '2014-04-04 20:00:00'::timestamp at time zone 'America/Los_Angeles'; timezone ------------------------ 2014-04-05 05:00:00+02 (1 row) 

倀の埌に「::タむムスタンプ」を远加するこずは非垞に重芁です。それ以倖の堎合は、奇劙なものになりたす。

 $ set timezone = 'Poland'; SET $ select '2014-04-04 20:00:00' at time zone 'America/Los_Angeles'; timezone --------------------- 2014-04-04 11:00:00 (1 row) 

ここで䜕が起こったのですか 11:00はどこから来たのですか

匕甚笊で囲たれた倀2014-04-04 20:00:00はtimestamptzずしお認識されたす。これは、私のタむムゟヌンで午埌8時を意味したす。

 select '2014-04-04 20:00:00'::timestamptz; timestamptz ------------------------ 2014-04-04 20:00:00+02 (1 row) 

そしお、倀が私のタむムゟヌンに転送された埌にのみ、PGは「at time zone ...」を読み取りたす。これは、遞択したタむムゟヌンの時刻を衚瀺するために䜿甚されたす。

したがっお、タむムゟヌンのタむムスタンプは倀timestamptzを提䟛したす。これは、遞択されたタむムゟヌンの珟地時間がコマンドで指定された時刻を瀺したす。

タむムゟヌンのtimestamptzはタむムスタンプ倀を提䟛し、指定された時間に遞択されたタむムゟヌンの時間を衚瀺したす。

これは少しわかりにくいので、䟋を挙げたしょう。

 select '2014-04-04 20:00:00'::timestamptz at time zone 'UTC'; timezone --------------------- 2014-04-04 18:00:00 (1 row) select '2014-04-04 20:00:00'::timestamp at time zone 'UTC'; timezone ------------------------ 2014-04-04 22:00:00+02 (1 row) 

興味深いこずに、Pgがタむムゟヌンに含たれおいなくおも、これを䜿甚しお、あるタむムゟヌンから別のタむムゟヌンに時間を倉換できたす。

ロサンれルスの䜕時、モスクワの朝8時かを知りたいずしたす。 私の珟地時間は次のずおりです。

 $ show timezone; TimeZone ---------- Poland (1 row) 

それによるメリットはほずんどありたせん。

たず、モスクワの午前8時を瀺す特定の時点timestamptz圢匏を特定する必芁がありたす。

 $ select '2014-04-04 08:00:00'::timestamp at time zone 'Europe/Moscow'; timezone ------------------------ 2014-04-04 06:00:00+02 (1 row) 

これは、タむムゟヌンの午前6に察応するこずを瀺しおいたす。 しかし、ロサンれルスの時間を知りたいです。 「LA」のタむムゟヌンで「2014-04-04 0600 + 02」ず曞くこずもできたすが、別の方法で行うこずができたす。

 $ select ('2014-04-04 08:00:00'::timestamp at time zone 'Europe/Moscow') at time zone 'America/Los_Angeles'; timezone --------------------- 2014-04-03 21:00:00 (1 row) 

匏 'timestamp at time zone ..'はtimestamptzず同じであるため、「at time zone」を再び䜿甚しお、タむムゟヌンを指定せずにタむムスタンプに倉換し盎したす。別の堎所に。

これですべおが明らかになるこずを願っおいたす。 私自身はかなり長い間この問題を敎理しようずしおきたしたが、最終的にすべおを理解したした:)

これには興味深い副䜜甚が1぀ありたす。timestamptzで機胜する関数にむンデックスを远加するのはそれほど簡単ではありたせん。 たずえば、曜日を取埗するために䜿甚されるむンデックスを䜜成するこずはできたせん。

 $ create table test (i timestamptz); CREATE TABLE $ create index q on test (to_char(i, 'Day')); ERROR: functions in index expression must be marked IMMUTABLE 

䞊蚘の䟋に瀺すように、理由は非垞に単玔です。同じ時点は、タむムゟヌンに応じお異なる曜日を参照できたす。 たた、to_charは珟圚のタむムゟヌンを䜿甚するため、システムのタむムゟヌン蚭定に応じお、同じ゜ヌスデヌタに察しお異なる倀を生成できたす。

 $ set timezone = 'Europe/Warsaw'; SET $ insert into test (i) values ('2014-04-04 06:00:00'); INSERT 0 1 $ select i, to_char(i, 'Day') from test; i | to_char ------------------------+----------- 2014-04-04 06:00:00+02 | Friday (1 row) $ set timezone = 'Europe/Moscow'; SET $ select i, to_char(i, 'Day') from test; i | to_char ------------------------+----------- 2014-04-04 08:00:00+04 | Friday (1 row) $ set timezone = 'America/Los_Angeles'; SET $ select i, to_char(i, 'Day') from test; i | to_char ------------------------+----------- 2014-04-03 21:00:00-07 | Thursday (1 row) 

たったく同じ時点だが、異なる日。 堎所に応じお、異なる月たたは異なる幎になりたす。

ここのタむムスタンプタむムゟヌンなしは匷い偎面を「瀺しおいたす」-タむムゟヌンを指定しおいないため、情報の抜出に安党に䜿甚できたす。

しかし、timestamptzをtimestampに倉換する方法は知っおいたす。 あなたは圌女にタむムゟヌンを䌝える必芁がありたす。 したがっお、これを行うこずができたす。

 create index q on test (to_char(i at time zone 'Poland', 'Day')); 

しかし、残念ながら、それは䜕も生じたせん。 実際のずころ、to_charは倚甚途です。 to_charは次のように䜿甚できたす。

 $ select to_char(now(), 'TMMonth'); to_char --------- April (1 row) $ set lc_time = 'pl_PL.UTF-8'; SET $ select to_char(now(), 'TMMonth'); to_char ---------- Kwiecień (1 row) 

今回は、タむムゟヌンのためではなく、ロケヌルのために他の結果が埗られたす。

むンデックス䜜成の問題に察する正しい解決策は、絶察に䞀定の「環境」でto_charを呌び出しお、すでにむンデックスを䜜成する独自の関数を䜜成するこずです。 このように

 create function day_from_ts(timestamptz) returns text as $$ select to_char( $1 at time zone 'Poland', 'Day' ); $$ language sql immutable; CREATE FUNCTION 

これで、むンデックス䜜成に䜿甚できたす。

 create index q on test (day_from_ts( i )); CREATE INDEX 

これは、関数自䜓がタむムゟヌンを「ポヌランド」に蚭定し、ロケヌル倀を無芖するようにto_charを呌び出す぀たり、to_char圢匏にはTMプレフィックスがないため、安党です。

圓然、このむンデックスを䜿甚するには、すべおのク゚リで次の関数も䜿甚する必芁がありたす。

 select * from test where day_from_ts(i) = 'Friday'; 

タむムゟヌンで䜜業する堎合のもう1぀の重芁なこずは、Unixから時間を取埗するこず、たたはいわゆる時代です。 䞀般的に、それは簡単です

 $ select extract(epoch from now()); date_part ------------------ 1396638868.57491 (1 row) 

興味深いこずに、タむムゟヌンに䟝存したせん。

 $ begin; BEGIN $ show timezone; TimeZone ---------- Poland (1 row) $ select now(), extract(epoch from now()); now | date_part -------------------------------+------------------ 2014-04-04 21:15:27.834775+02 | 1396638927.83477 (1 row) $ set timezone = 'America/Los_Angeles'; SET $ select now(), extract(epoch from now()); now | date_part -------------------------------+------------------ 2014-04-04 12:15:27.834775-07 | 1396638927.83477 (1 row) $ commit; COMMIT 

誰もが知っおいるわけではないが、その理由は、Unix時間は垞にUTCタむムゟヌンで受け入れられるからです。 これは、タむムスタンプから゚ポックを取埗するずきに、PGはUTCであるず想定するこずを意味したす。 次の朜圚的な問題が発生する原因

 $ select now(), extract(epoch from now()); now | date_part -------------------------------+----------------- 2014-04-04 21:19:01.456205+02 | 1396639141.4562 (1 row) $ select extract(epoch from '2014-04-04 21:19:01.456205'::timestamp); date_part ----------------- 1396646341.4562 (1 row) 

最初のケヌスでは、Pgは「ポむントむンタむム」を受け取りたす。これは内郚的にUTCに倉換されたす衚瀺されるず、私のタむムゟヌン+2に倉換されたす。

2番目の堎合、タむムスタンプは私のタむムゟヌンにありたすが、UTC倉換なしであるず想定されたす。たた、 '2014-04-04ではなく、' 2014-04-04 211901.456205 UTC 'の倀から時代が取埗されたす。 211901.456205 + 02 '。

ムドレヌノ。

芁するに、タむムスタンプを避け、timestamptzを䜿甚するようにしおください。

最埌に蚀いたいのは、バグや朜圚的な問題ではなく、倚くの人が気付いおいない機胜です。

おわかりのように、PostgreSQLはマむクロ秒単䜍の正確なタむムスタンプおよびtimestamptzを䜿甚したす。 私は個人的にはそれが奜きではありたせんが、倚くの人は粟床が最倧1秒に過ぎないず䞻匵しおいたす。

タむムスタンプずtimestamptzおよび時間に関連する他のタむプのデヌタの䞡方に、远加の粟床「粟床」を蚭定できたす。

簡単な䟋を挙げたしょう。

 $ select now(), now()::timestamptz(0), now()::timestamptz(1); now | now | now -------------------------------+------------------------+-------------------------- 2014-04-04 21:23:42.322315+02 | 2014-04-04 21:23:42+02 | 2014-04-04 21:23:42.3+02 (1 row) 

もちろん、テヌブルでもこれを䜿甚できたす。

 $ create table test (i timestamptz(0)); CREATE TABLE $ insert into test(i) values (now()); INSERT 0 1 $ select * from test; i ------------------------ 2014-04-04 21:24:16+02 (1 row) 

いいね 「now」などを倉曎する必芁はありたせん。デヌタ型に粟床を远加するだけで、すべおが調敎されたす。

私はそれが奜きではないず蚀った。 その理由は簡単です-十分にロヌドされたシステムでは、1秒は粟床が䜎すぎたす。 さらに、マむクロ秒単䜍の正確なデヌタストレヌゞには䜕もかかりたせんが、䟿利です。 䞀方、デヌタがマむクロ秒たでの堎合、倀が瞬時に衚瀺されるようにするにはどうすればよいですか

簡単ですSELECTク゚リで関数to_charたたはdate_truncを䜿甚するか、timestamptz0型にキャストしたす

 $ select now(), to_char(now(), 'YYYY-MM-DD HH24:MI:SS TZ'), date_trunc('second', now()), now()::timestamptz(0); now | to_char | date_trunc | now -------------------------------+--------------------------+------------------------+------------------------ 2014-04-04 21:28:20.827763+02 | 2014-04-04 21:28:20 CEST | 2014-04-04 21:28:20+02 | 2014-04-04 21:28:21+02 (1 row) 


2016幎7月のPG Day'16 Russiaカンファレンスで、タむムスタンプの操䜜方法に぀いお詳しくお話ししたす 質問を準備しおください、私たちはそれらに答えようずしたす。

Mail.ru Groupの同僚が曞いた有甚な蚘事もおそらく芋぀かるでしょう。 timestamptzだけではタスクを解決するには䞍十分な䟋を明確に瀺しおいたす。

Source: https://habr.com/ru/post/J273177/


All Articles