CURRENT_TIMESTAMPを主キーとして使用できますか?


10

CURRENT_TIMESTAMPとして使用できますPRIMARY KEYか?

2つ以上の異なるINSERTが同じになる可能性はありCURRENT_TIMESTAMPますか?


3
1990年代にタイムスタンプをPKとして使用してコーディングされたアプリについて聞いたことがあります。10年後、PCはより高速になり、タイムスタンプが複製されました。アプリの機能が非常に重要であったため、これは非常に深刻な問題を引き起こしました。また、アプリ全体でPKの一意性が適切に適用されていませんでした。
ビクター

2つ以上の異なるINSERTが同じCURRENT_TIMESTAMPを取得する可能性はありますか?1つのクエリが衝突のために2つのレコードを挿入するだけで十分です。したがって、主題の質問に対する答えは「いいえ」です。
Akina


3
なぜこれが欲しいのか知りたいのですが。
2018

@Nanne私はこれを疑っています:MySQLには、自動的にインクリメントされる整数IDの非常に優れた処理があります(フィールドに対するauto_increment属性)。PostgreSQLにはありません。シリアル型なので、美しさがはるかに劣ります。
peterh-モニカを

回答:


18

ドキュメントに従って、の精度CURRENT_TIMESTAMPはマイクロ秒です。したがって、衝突の確率は低くなりますが、可能です。

今起きないバグを想像非常に稀に、データベースエラーが発生します。それをデバッグするのはどれほど難しいですか?これは、少なくとも決定論的なバグよりもはるかに悪いバグです。

より広範なコンテキスト:シーケンスでのこれらの小さなニュアンスを回避する必要がある場合があります。これは、MySQLに慣れている場合は特に厄介です。

さらに、トランザクションを使用している場合(ほとんどのWebフレームワーク、特にJavaのフレームワークは実行します)、トランザクション内のタイムスタンプは同じになります!デモ:

postgres=# begin;
BEGIN
postgres=# select current_timestamp;
       current_timestamp       
-------------------------------
 2018-08-06 02:41:42.472163+02
(1 Zeile)

postgres=# select current_timestamp;
       current_timestamp       
-------------------------------
 2018-08-06 02:41:42.472163+02
(1 Zeile)

またね?2つの選択、まったく同じ結果。私はそれほど速くタイプしません。;-)

-

シーケンスの使用を避けて簡単にIDが必要な場合は、レコードの実際の識別子からハッシュ値を生成します。たとえば、データベースに人間が含まれていて、その生年月日、母親の旧姓、本名がそれらを一意に識別していることがわかっている場合は、

md5(mother_name || '-' || given_name || '-' birthday);

IDとして。それ以外に、CreationDateテーブルにインデックスを付けた後に列を使用できますが、それはキーではありません(idです)。

Ps一般に、DBを可能な限り確定的にすることは非常に良い習慣です。つまりは、同じ操作はDBで正確に同じ変更を作成する必要があります。タイムスタンプベースのIDは、この重要な機能に失敗します。何かをデバッグまたはシミュレーションしたい場合はどうなりますか?操作を再生すると、同じオブジェクトが異なるIDで作成されます...追跡するのは難しくなく、多くの作業時間を節約できます。

Ps2上記の理由により、将来コードをチェックする人は誰でも、タイムスタンプで生成されたIDを確認することはできません。


トランザクションを使用していない場合でも、実際にはトランザクションを使用しています(Postgresには非トランザクションモードがないため、自動コミットがあるだけです)。したがってINSERT、複数の行を実行すると、それらはすべて同じになりcurrent_timestampます。そして、あなたにはトリガーがあります...
ケビン

2
2人の男が同じ名前で同じ日に生まれ、母親の名前が同じだったために壊れたアプリについて聞いたことがあります。痛い。それが発生する可能性がある場合は、遅かれ早かれ発生します。
Balazs Gunics 2018

@BalazsGunicsHelló:-)これは単なる例でした。たとえば、実際のシナリオでは、idは電子メールアドレスまたは選択したユーザー名(まだ存在しない場合にのみ登録できます)で十分だと思います。政府は1 870728 0651のような個人識別番号を使用する傾向があります。重要なことは、IDをタイムスタンプまたはランダムな値にバインドすると、DBの確定性が低下するため、悪い習慣とは言えません。
peterh-モニカを復活させる

@BalazsGunicsさらに、同じmother_name + given_name +誕生日を持つ2人のユーザーは、まだ確定的エラーを引き起こします。挿入を含む2つのトランザクションが同じマイクロ秒で発生したため、主キーの衝突が発生しましたが、それでもまだ確定的ではなく、再現性の非常に低い問題です。
peterh-モニカを復活させる

10

CURRENT_TIMESTAMPが2つの後続のINSERT(または複数の行を持つ単一のINSERT)に2つの同一の値を提供することが可能だからではありません。

代わりに、時間ベースのUUIDを使用してください:uuid_generate_v1mc()


7

厳密に言えば、いいえ。なぜなら、CURRENT_TIMESTAMP関数であり、制約を形成できるのは1つ以上のテーブルだけだからですPRIMARY KEY

あなたが作成することを意味する場合はPRIMARY KEY、デフォルト値を持つ列の制約をCURRENT_TIMESTAMP、その答えは:はい、あなたがすることができます。あなたが息子の頭からリンゴを撃つことを妨げるようなものは何もないように、あなたがそれをすることを妨げるものは何もありません。質問の目的を定義しない限り、質問はまだ意味がありません。列とテーブルはどのような種類のデータを保持することになっていますか?どのルールを実装しようとしていますか?

一般的に、アイデアがあるため、重複キーエラーに実行するためにバインドされてCURRENT_TIMESTAMPいるSTABLE同じトランザクション(トランザクションの開始時刻)に同じ値を返す関数。同じトランザクション内の複数のINSERTは、すでに説明されている他の回答と同様に、衝突するようにバインドされています。マニュアル:

これらの関数は現在のトランザクションの開始時間を返すため、それらの値はトランザクション中に変更されません。これは機能と見なされます。1つのトランザクションが「現在の」時間の一貫した概念を持つことができるようにして、同じトランザクション内の複数の変更が同じタイムスタンプを持つようにすることを目的としています。

Postgresのタイムスタンプは、最大6の小数桁(マイクロ秒の分解能)を表す8バイトの整数として実装されます。

場合は(名前のものを使用すると、マイクロあたり1個以下の行を保持することになっているテーブルを構築しているし、その条件を変更するつもりはないsensor_reading_per_microsecond)、そして、それは意味をなさないかもしれません。行が重複する、重複キー違反エラーが発生するはずです。ただし、これはエキゾチックな例外です。そして、データ型timestamptz(ではないtimestamp)がおそらく望ましいでしょう。見る:

代わりに、代わりに代理シリアル主キーを使用します。そしてUNIQUE、タイムスタンプ列に制約を追加します。RDBMSの実装の詳細に依存せずに、起こりうる複雑さを減らします。


sensor_reading_per_microsecond各読み取りのタイミングが前の読み取りと完全に同期していることを完全に保証できない場合でも、衝突する可能性があります。サブマイクロ秒の偏差(これは多くの場合不可能ではありません)はスキームを壊します。一般的に私はまだこれを完全に避けます。(あなたが指摘したように、そのような場合、結果として生じる衝突が望ましいかもしれません!)
オービットのライトネスレース

@Lightness:私は完全に同意しています。丸められた小さな偏差の後の意図しないタイムシフトの例は、別の警告を示しています。
Erwin Brandstetter 2018
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.