Skip to content

Commit fc99af5

Browse files
authored
Merge pull request #1427 from future-architect/feature
追記
2 parents f861b4d + c0e9455 commit fc99af5

File tree

1 file changed

+156
-2
lines changed

1 file changed

+156
-2
lines changed

source/_posts/20241113a_PostgreSQLで連番を自動生成してくれるIDENTITY列.md

Lines changed: 156 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ Technology Innovation Group真野です。
3030

3131
- 新規構築なら連番の自動採番はSERIAL/BIGSERIALの代わりに `GENERATED ALWAYS AS IDENTITY` の利用がベター
3232
- DEFAULTキーワードは利用せず、省略する
33+
- 暗黙的に作成されるシーケンスは、テーブル名やカラム名のリネームに追随しないので、合わせてリネームする運用にする
3334
- 気になった部分の調査事項と結果は下表
3435

3536
| 調査項目 | 結果 |
@@ -39,6 +40,10 @@ Technology Innovation Group真野です。
3940
| パーティションテーブルでの利用 | 利用できる |
4041
| 作成されたシーケンスの名称 | {テーブル名}_{カラム名}_seq |
4142
| 作成されたシーケンスを削除したらどうなるか | 削除不可 |
43+
| シーケンス名の上限63文字を超過したテーブル、カラム名の場合 | それぞれ29文字上限でオミットされて生成 |
44+
| テーブル名を変更した時シーケンス名はどうなるか | 変化なし |
45+
| カラム名をを変更した時シーケンス名はどうなるか | 変化なし |
46+
| 独自に作成したシーケンスとの紐づけ方法 | できない |
4247
| 文字列型とGENERATED AS IDENTITYの組み合わせ| 設定不可 |
4348
| SERIAL型とGENERATED AS IDENTITYの組み合わせ | 設定不可 |
4449

@@ -53,6 +58,19 @@ CREATE TABLE color (
5358
);
5459
```
5560

61+
テーブルの状態は以下です。`GENERATED AS IDENTITY` を付けると暗黙的にNOT NULL制約がつくことも分かります。
62+
63+
```sql
64+
postgres-# \d color;
65+
Table "public.color"
66+
Column | Type | Collation | Nullable | Default
67+
------------+-------------------+-----------+----------+----------------------------------
68+
color_id | bigint | | not null | generated by default as identity
69+
color_name | character varying | | not null |
70+
Indexes:
71+
"color_pkey" PRIMARY KEY, btree (color_id)
72+
```
73+
5674
このテーブルで`color_id` を未指定にして、2件データを登録します。
5775

5876
```sql
@@ -358,6 +376,23 @@ WHERE
358376
(1 row)
359377
```
360378

379+
上記のSQLは少し長いので、 `pg_get_serial_sequence(table text, column text)` というシステムカタログ情報関数も用意されています。
380+
381+
- https://www.postgresql.jp/document/16/html/functions-info.html#FUNCTIONS-INFO-CATALOG
382+
383+
```sql
384+
SELECT pg_get_serial_sequence('color', 'color_id') AS sequence_name;
385+
```
386+
387+
結果です。
388+
389+
```sql
390+
sequence_name
391+
---------------------------
392+
public.color_color_id_seq
393+
(1 row)
394+
```
395+
361396
### 5. 作成されたシーケンスを削除したらどうなるか
362397

363398
誤ってIDENTITY列が内部的に使用するシーケンスオブジェクトを削除したら、不正な状態にならないかテストです。当然、失敗します。
@@ -370,7 +405,126 @@ HINT: You can drop column color_id of table color instead.
370405

371406
もし、このシーケンスを削除したい場合は、colorテーブルのcolor_id列を削除する必要があるとあります。妥当なメッセージです。
372407

373-
### 6. 文字列型とGENERATED BY DEFAULT AS IDENTITYの組み合わせ
408+
### 6. シーケンス名の上限63文字を超過したテーブル、カラム名の場合
409+
410+
PostgreSQLではシーケンスに限らず、識別子の最長は63文字です。
411+
412+
- https://www.postgresql.jp/document/16/html/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS
413+
414+
そのため、IDENTITYで自動で生成されるシーケンス名の体系が、{テーブル名}_{カラム名}_seq だとすると、超過した場合にどう命名されるか気になりました。
415+
416+
試してみます。テーブル名が36文字、カラム名が39文字です。
417+
418+
```sql
419+
CREATE TABLE looooooooooooooooooooooooooooooooong (
420+
looooooooooooooooooooooooooooooooong_id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
421+
color_name VARCHAR NOT NULL
422+
);
423+
```
424+
425+
シーケンス名を確認すると、次の結果となりました。
426+
427+
```sql
428+
postgres=# SELECT pg_get_serial_sequence('looooooooooooooooooooooooooooooooong', 'looooooooooooooooooooooooooooooooong_id') AS sequence_name;
429+
sequence_name
430+
------------------------------------------------------------------------
431+
public.loooooooooooooooooooooooooooo_loooooooooooooooooooooooooooo_seq
432+
```
433+
434+
テーブル名、カラム名が長いと最長で29文字で前方からオミットされて生成されるようです。
435+
436+
直接シーケンス名を指定して `setval()` するときに困ることが多いかなと思いますので、注意が必要です。
437+
438+
### 7. テーブル名を変更した時シーケンス名はどうなるか
439+
440+
シーケンス名は自動生成されますが、ALTERでテーブル名を変えた場合にどうなるか確かめます。
441+
442+
```sql
443+
ALTER TABLE looooooooooooooooooooooooooooooooong RENAME TO color;
444+
```
445+
446+
結果は以下の通り、変化無しです。
447+
448+
```sql
449+
postgres=# SELECT pg_get_serial_sequence('color', 'looooooooooooooooooooooooooooooooong_id') AS sequence_name;
450+
sequence_name
451+
------------------------------------------------------------------------
452+
public.loooooooooooooooooooooooooooo_loooooooooooooooooooooooooooo_seq
453+
(1 row)
454+
```
455+
456+
そのため、テーブル名を変更した場合は、シーケンス名もリネームするような運用を行った方が良いでしょう。
457+
458+
### 8. カラム名をを変更した時シーケンス名はどうなるか
459+
460+
7と同様に、カラム名を変更した場合にシーケンス名がどうなるか確認します。
461+
462+
```sql
463+
ALTER TABLE color RENAME COLUMN looooooooooooooooooooooooooooooooong_id TO color_id;
464+
```
465+
466+
結果は以下の通り、テーブル名と同様、カラム名の変更も変化ありません。
467+
468+
```sql
469+
postgres=# SELECT pg_get_serial_sequence('color', 'color_id') AS sequence_name;
470+
sequence_name
471+
------------------------------------------------------------------------
472+
public.loooooooooooooooooooooooooooo_loooooooooooooooooooooooooooo_seq
473+
(1 row)
474+
```
475+
476+
結論も7と同様、テーブル名/カラム名が変更した場合は、シーケンス名もリネームする運用を行うとベターでしょう。
477+
478+
```sql シーケンスのリネーム
479+
ALTER SEQUENCE loooooooooooooooooooooooooooo_loooooooooooooooooooooooooooo_seq RENAME TO color_color_id_seq;
480+
```
481+
482+
### 9. 独自に作成したシーケンスとの紐づけ方法
483+
484+
SERIAL型であれば、以下のように指定すると独自のシーケンスと紐づけることができました。
485+
486+
```sql
487+
-- 独自シーケンス
488+
CREATE SEQUENCE custom_color_seq;
489+
490+
-- DEAULTでシーケンスと紐づける
491+
CREATE TABLE color (
492+
color_id BIGINT NOT NULL DEFAULT nextval('custom_color_seq') PRIMARY KEY,
493+
color_name VARCHAR NOT NULL
494+
);
495+
ALTER SEQUENCE custom_color_seq OWNED BY color.color_id;
496+
```
497+
498+
BIGINTを指定していて、BIGSERIALを使っていないじゃない?と思うかもしれません。しかし[ドキュメント](https://www.postgresql.jp/docs/16/datatype-numeric.html#DATATYPE-SERIAL)にも記載通り、以下の2つの構文は同義ですので、これが言えます。
499+
500+
```sql
501+
CREATE TABLE tablename (
502+
colname SERIAL
503+
);
504+
```
505+
506+
```sql SERIAL型の裏側
507+
CREATE SEQUENCE tablename_colname_seq AS integer;
508+
CREATE TABLE tablename (
509+
colname integer NOT NULL DEFAULT nextval('tablename_colname_seq')
510+
);
511+
ALTER SEQUENCE tablename_colname_seq OWNED BY tablename.colname;
512+
```
513+
514+
IDENTITY列に関しては、手動で作成したシーケンスとIDENTITY列を紐づける構文は、ドキュメントを探した時点では存在しませんでした。
515+
516+
そのため、独自の名称のシーケンスにしたければ `ALTER SEQUENCE RENAME` で変更する運用になるかと思います。
517+
518+
また、次のようにシーケンスオプションも作成時に指定できます(ALTERで変更も可能)です。おそらく、困ることは無いかなと思います。
519+
520+
```sql
521+
CREATE TABLE color (
522+
color_id INT GENERATED ALWAYS AS IDENTITY (START WITH 10 INCREMENT BY 1 CACHE 100),
523+
color_name VARCHAR NOT NULL
524+
);
525+
```
526+
527+
### 10. 文字列型とGENERATED BY DEFAULT AS IDENTITYの組み合わせ
374528

375529
文字列型(text型)にGENERATED ALWAYS AS IDENTITYを指定すると、いい感じの型変換により '1'、'2'、...といった採番がされないかと思いついたので試しました。
376530

@@ -384,7 +538,7 @@ ERROR: identity column type must be smallint, integer, or bigint
384538

385539
無事エラーで、これは対応していないようです。型としては、`smallint` `integer` `bigint` のみ対応。
386540

387-
### 7. SERIAL型とGENERATED BY DEFAULT AS IDENTITYの組み合わせ
541+
### 11. SERIAL型とGENERATED BY DEFAULT AS IDENTITYの組み合わせ
388542

389543
SERIAL型であれば、型としては `integer` 型なので、いけるのではと一応チャレンジしました。結果は以下のエラーです。
390544

0 commit comments

Comments
 (0)