Oracle DatabaseのVARCHAR2データ型は可変長文字列を扱うための基本的な型ですが、その定義と挙動には「バイト単位」と「文字単位」という2つの異なる長さセマンティクスが存在します。公式ドキュメントによると、VARCHAR2列を作成する際には最大長をバイトまたは文字数で指定しますが、データ型としての物理的な上限は常に4000バイトです。例えば、VARCHAR2(4000 CHAR)と定義しても、実際には1文字が複数バイト(AL32UTF8では最大3〜4バイト)を使用する場合、格納できる文字数は4000バイトを超えられないため減少します。また、文字列結合関数などを使用する際も、この4000バイトの制約が厳密に適用されます。
以下に、検証環境の設定、検証用テーブルの作成、およびマルチバイト文字を使用したデータ挿入時の挙動確認を示します。
検証環境の確認
まず、データベースのバージョンとNLS_LENGTH_SEMANTICSパラメータの設定を確認します。このパラメータは、列定義時にBYTEまたはCHARを明示しなかった場合のデフォルトの動作を決定します。
SELECT * FROM v$version WHERE rownum < 2;
BANNER
--------------------------------------------------------------------------------
Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production
SELECT PARAMETER, VALUE FROM v$nls_parameters WHERE PARAMETER = 'NLS_LENGTH_SEMANTICS';
PARAMETER VALUE
---------------------- --------------------
NLS_LENGTH_SEMANTICS BYTE
検証用テーブルの作成
異なる長さセマンティクスを持つカラムを持つテーブルを作成します。明示的にBYTEとCHARを指定したカラムと、デフォルト設定に依存するカラムを定義します。
CREATE TABLE tbl_varchar2_limit (
col_4000_byte VARCHAR2(4000 BYTE),
col_4000_char VARCHAR2(4000 CHAR),
col_default VARCHAR2(4000),
col_2000_byte VARCHAR2(2000 BYTE),
col_2000_char VARCHAR2(2000 CHAR)
);
データ挿入テスト
全角文字(日本語)を含むデータを生成し、各カラムの制限をテストします。AL32UTF8環境では全角文字は通常1文字あたり3バイトを消費します。ここでは、LISTAGG関数を用いて文字列を結合し、バイト数が制限を超えた場合の挙動を確認します。
まず、約2000文字の全角文字列を生成し、その長さを確認します。
-- 結合対象となるダミーデータの生成
WITH raw_data AS (
SELECT '検証' AS val FROM dual CONNECT BY level <= 1000
)
SELECT
LENGTH(LISTAGG(val) WITHIN GROUP (ORDER BY val) || '追加テキスト') AS char_length,
LENGTHB(LISTAGG(val) WITHIN GROUP (ORDER BY val) || '追加テキスト') AS byte_length
FROM raw_data;
CHAR_LENGTH BYTE_LENGTH
----------- ------------
2001 6003
生成された文字列(約6003バイト)を、2000バイト制限のカラム(col_2000_byte)および、デフォルト設定(BYTE)のカラム(col_2000_char)に挿入を試みます。
INSERT INTO tbl_varchar2_limit (
col_4000_byte,
col_4000_char,
col_2000_byte,
col_2000_char
)
SELECT
long_str,
long_str,
long_str,
long_str
FROM (
SELECT LISTAGG(val) WITHIN GROUP (ORDER BY val) || '追加テキスト' AS long_str
FROM (SELECT '検証' AS val FROM dual CONNECT BY level <= 1000)
);
ORA-12899: 列"USER"."TBL_VARCHAR2_LIMIT"."COL_2000_BYTE"の値が大きすぎます(実際: 6003, 最大: 2000)
次に、制限を超えない4000バイト以下のデータを作成し、挿入を試みます。例として、1333文字(約3999バイト相当)の文字列を生成します。
INSERT INTO tbl_varchar2_limit (col_4000_char)
SELECT RPAD('試験', 1333, '験') FROM dual;
1行が作成されました。
COMMIT;
VARCHAR2(4000 CHAR)と定義されていても、内部的なバイトサイズ(4000バイト)を超えるデータは格納できないことが確認できます。
関数による文字列結合の制限
SQL関数内での文字列結合結果が4000バイトを超える場合も、ORA-01489エラーが発生します。これは、STRING型のバッファサイズが上限を超えたためです。
-- 4000バイトを大幅に超える結合を試みる
WITH large_dataset AS (
SELECT 'abcdefghij' AS txt FROM dual CONNECT BY level <= 500
)
SELECT LISTAGG(txt) WITHIN GROUP (ORDER BY txt)
FROM large_dataset;
ORA-01489: 文字列の連結結果が長すぎます
このように、Oracle DatabaseにおけるVARCHAR2の取り扱いでは、論理的な文字数定義(CHAR)を使用した場合であっても、物理的なバイト数上限(4000バイト)が絶対的なボトルネックとなります。大量のテキストデータを扱う必要がある場合は、CLOBやNCLOBなどのLarge Object型の使用を検討する必要があります。また、非推奨となったWM_CONCAT関数の代わりに、LISTAGG関数を使用することが推奨されますが、LISTAGGでも同様の4000バイト制限が適用される点には注意が必要です。