フューチャー技術ブログ

OracleDB マルチテーブル・インサートにおけるIDENTITY列とSEQUENCEの挙動の違い

はじめに

こんにちは、Cyber Security Innovation Group(以降CSIG)の姫路康太郎です。2025年2月から新卒としてプロジェクトに配属され、認可整理のチームでアジャイル開発を行っています。

本記事ではまず、OracleDBにおける主要な採番方法であるSEQUENCEとIDENTITY列について、基本的な使い方を説明します。続いて、複数のテーブルへ同時にデータを投入する際に利用できるOracleDB特有のマルチテーブル・インサート構文 (INSERT ALL) における、それぞれの採番の実装方法と挙動の違いに焦点を当てて解説します。

OracleDBでの開発に携わる方や、効率的な採番方法に関心のある方にとって、本記事が少しでもお役に立てれば幸いです。

OracleDBにおける採番方法の紹介

初めに、軽くIDENTITY列とSEQUENCEについて紹介します。

SEQUENCE

SEQUENCEは一意の整数値を生成するために使用されるスキーマオブジェクトであり、特定のテーブルとは独立したデータベースオブジェクトです。それにより、複数のテーブルで共有したり、SEQUENCEオブジェクト単体を操作することができます。この点で後述するIDENTITY列とは異なり、IDENTITY列よりも柔軟性があると言えます。

IDENTITY列

IDENTITY列は、テーブルの特定の列に対して自動的に一意な数値を生成する機能です。内部的にSEQUENCEオブジェクトを利用して実現されており、IDENTITY列を定義すると対応するシーケンスを暗黙的に作成し、そのシーケンスから値を取得して列に自動的に設定します。そのため、SEQUENCEよりシンプルに使うことができます。

SEQUENCEの使い方

SEQUENCEオブジェクトは独立したオブジェクトとして作成します。

CREATE SEQUENCE TEST_SEQ;

INSERTをする際に、作成しておいたSEQUENCEオブジェクトを明示的に呼び出すことで採番します。

INSERT INTO M_TEST(ID,VALUE) VALUES(TEST_SEQ.nextval,'data');

IDENTITY列の使い方

IDENTITY列は、特定のテーブルの特定のカラムに定義します。

CREATE TABLE  M_TEST (
ID NUMBER GENERATED BY DEFAULT AS IDENTITY,
VALUE VARCHAR2(32)
);

INSERTをする際に、採番列の値を明示的に示さなくても暗黙的に採番されます。

INSERT INTO M_TEST(VALUE) VALUES ('data');

2つのテーブルに共通の番号を採番をする

いくつか方法はあると思いますが、本記事では、採番にSEQUENCE / IDENTITY列を利用し、2つのテーブル同時のINSERTに、マルチテーブル・インサート構文INSERT ALLを利用して投入することを考えます。投入するデータはM_TESTの様にIDVALUEのカラムを持った複数レコードからなるテーブルを想定しています。

>> SELECT * FROM M_TEST

ID VALUE
---- -----
1 data1
2 data2
3 data3

マルチテーブル・インサート

OracleDB特有の構文で、複数のテーブルに同時にデータを投入することができます。INSERT ALLを使う基本的な構文は以下のとおりです。

INSERT ALL
INTO TABLE1 (col1, col2, …) VALUES (val1_1, val1_2, …)
INTO TABLE2 (col1, col2, …) VALUES (val2_1, val2_2, …)
SELECT文;

※詳細は以下の記事を参考ください。

SEQUENCEを使ったマルチテーブル・インサート

SEQUENCEオブジェクトを利用した場合、同じSEQUENCEオブジェクトから番号を呼び出すことで、共通の番号を採番できます。そのため、2つのテーブル間でIDは正しく保たれることとなります。

INSERT ALL
INTO M_TEST_SEQ1 (ID, VALUE) VALUES (TEST_SEQ.nextval, VALUE)
INTO M_TEST_SEQ2 (ID, VALUE) VALUES (TEST_SEQ.nextval, VALUE)
SELECT VALUE FROM M_TEST;

SEQUENCEを使ったマルチテーブル・インサートの落とし穴

INSERT ALLでSEQUENCEを使って複数のテーブルに採番を行う際には、採番するテーブルに対して すべてに nextval(SEQUENCEを増加させて次の値を返す)を使用します。公式によるとこれが正規の方法のようです。このnextvalを使用するという点が、私の直感と異なっていたので、共有しようと思いました。

直感的には、上記コードでM_TEST_SEQ2に採番する際、currval(SEQUENCEの現在の値を返す)を使うのではないかと感じました。直前の行でnextvalを実行しているため、次のVALUE句ではnextvalされたSEQUENCEを取得するものと考えたからです。

実際にcurrvalで実行したみたところ、1つの環境では成功したものの、別の環境では失敗したので、安全のためにも公式の説明から読み取れるnextvalを推奨します。

IDENTITYを使ったマルチテーブル・インサート

IDENTITY列を利用した場合、データ投入先の2つのテーブルに、それぞれにIDENTITY列を定義することになります。つまり、2つのテーブルがそれぞれ固有のSEQUENCEオブジェクトを持つことになります。そのため、2つのテーブル間でIDの対応を正しく保つためには採番をずらさないための設計や、採番のずれを許容することが必要となります。IDがずれる可能性を考慮すると、IDENTITY列の利用はあまりいい手段ではなさそうです。

INSERT ALL
INTO M_TEST_ID1 (VALUE) VALUES (VALUE)
INTO M_TEST_ID2 (VALUE) VALUES (VALUE)
SELECT VALUE FROM M_TEST;

※IDENTITY列の採番方法には、GENERATED ALWAYS AS IDENTITYGENERATED BY DEFAULT AS IDENTITYの2種類があります。詳しく説明している記事の紹介にとどめて、詳細な説明は割愛させていただきますが、本記事ではGENERATED BY DEFAULT AS IDENTITYを利用しました。

また参考までに、PostgreSQLのIDENTITY列と、基本的な使い方や振る舞いは同様のようです。

おわりに

マルチテーブル・インサートにおける採番方法について触れ、マルチテーブル・インサート構文でIDENTITY列とSEQUENCEはどちらとも利用できることが分かりました。そのうえで、採番にずれが生じないSEQUENCEを利用することが良いと考えましたが、皆さんはどのように考えられますか?

何か疑問点や問題点がある場合には、遠慮なくご指摘いただけますと幸いです。