« 2025年12月 | トップページ | 2026年2月 »

2026年1月27日 (火)

Oracle Database - Multi Row INSERT、バインド変数を使うと、リテラル値を使う場合では見える景色が変わるんだよね #1 - バグなのか現時点の仕様なのか?

Previously on Mac De Oracle
前回は、
帰ってきた! 標準はあるにはあるが癖の多いSQL #22 - Multi Row INSERT でした。
TVC(Table Value Constractor)の流れではあったのですが、通常やるとは思えないリテラルを使ったMulti row insertで大量の行を詰め込んでみました!
TVCはともかく、Multi row insertをリテラル値で、かつ、大量の行を詰め込むのはリーズナブルな方法ではないですよね。特にOracle Databaseでは。というあたりに気づければそれで良いと思います:)

PostgreSQL/MySQLは幾分マイルドなw 結果でしたが、Oracle Databaseは、らしい特徴がでていましたよね。
ハードパースコストがめちゃめちゃ高くて。 

 

そういえば、
以前、
パースが重いぞーというネタをやってたことありましたが、取り上げたハードパースがキツくなるネタは、Oracle Databaseで索引のある列に対してIN句を使い大量の値をセットするネタでした。(これ意外と見かけます)
それ以外のケースだと、MySQLで結合する表が多くて考え過ぎてるケース。
どちらも実行計画を立てるためにオプティマイザが考え過ぎてしまう(得られる実行計画は良いのですが)ことでパース時間が異常に長くなる症状でしたよね!
悩ませ過ぎは及ばざるがごとし #3
悩ませ過ぎは及ばざるがごとし #4
悩ませ過ぎは及ばざるがごとし #7 - おまけ
悩ませ過ぎは及ばざるがごとし (MySQL 8.0.32編)

ただ、今回取り上げたOracle DatabaseのINSERT(Multi row insert)は、どちらかというとメモリリソース確保の影響が色濃い状況でした。(パース時の帯域イベントもほぼそれだけ)
リテラル値を使い、大量の行を詰め込んだ巨大なmulti row insert文を実行するのは考えものですよね。まじでw


前置きはそれぐらいにして、
前回、そんなことやる人いないでしょーっ、というノリで、リテラル値を使い巨大なMulti row insertを行いましたが、
通常なら、バインド変数使いますよね! 絶対!w

ということで、バインド変数を利用したMulti row insertの準備をしていた際に気づいたバグというか現時点の仕様というかなんというかw にハマった記録です! が今回のネタ。
(Oracle Database 23ai FREEのPL/SQLからの実行だと、現時点では、EXECUTE IMMEDIATEを利用してバインド変数化することぐらいしかできなそう!)
まじで?! という状況なので、細かい確認は次のリリース以降で再度試してみようかなと。

 

 

不具合?なのか現時点の仕様なのか、制限っぽいエラーについてのログは以下。
運良く見つけた、現状、唯一の逃げ道も最後に記載しています。
(PL/SQLだと以前から他の方法(Bulk Insert)もあるので困ることはないとは思いますが。。。)

前回作成した表を利用します。(SCOTTスキーマに作成)

create table meows_ins_tab
(
id integer not null primary key,
col1 varchar2(1000),
col2 varchar2(1000),
col3 varchar2(1000),
col4 varchar2(1000),
col5 varchar2(1000),
col6 varchar2(1000),
col7 varchar2(1000),
col8 varchar2(500)
);

 

前提 PL/SQLを利用しリテラル値のままのMulti row insert、バインド変数を利用したMulti row insertが対象です。(PL/SQLで利用する場合だけで発生していると思われるエラー。それ以外の言語ではどうかは未確認)

以下のような構文で、バインド変数を使ういたかったわけです。PL/SQLなので直接!

INSERT INTO mrows_ins_tab(id, col8) VALUES (...), (...) ...;

 

と書きたいところでしたが、
これが。。。。いろいろと引き当ててしまう原因になるとは。。。知る由もなかった。。。。
いろいろあります。。。よ! (^^ ;;;;;

 

バグ? or 現時点の仕様、または制限? その1

後で気づいたのですが、このエラーには2つの要素が絡んでいるようでした。
ひとつは、INSERTする表の列指定。もう一つは、バインド変数として利用しているVARRAY (可変サイズの配列)の二つ。(詳細は後続のエラーにて)
SQL文が無効ですというエラーに惑わされますが、エラー自体はORA-00600です。

SCOTT@localhost:1521/freepdb1> !cat bug1.sql
DECLARE
c11 NUMBER;
c12 NUMBER;
c81 VARCHAR2(500);
c82 VARCHAR2(500);
TYPE c1_type IS VARRAY(2) OF NUMBER;
TYPE c8_type IS VARRAY(2) OF VARCHAR2(500);
c1 c1_type := c1_type();
c8 c8_type := c8_type();
BEGIN
c1.extend(2);
c8.extend(2);
c1(1) := 1;
c1(2) := 2;
c8(1) := LPAD(TO_CHAR(c1(1)),373,'x');
c8(2) := LPAD(TO_CHAR(c1(2)),373,'x');
INSERT INTO mrows_ins_tab(id,col8) VALUES
(c1(1), c8(1))
, (c1(2), c8(2))
;
END;
/

SCOTT@localhost:1521/freepdb1> @bug1
c82 VARCHAR2(500);
*
行5でエラーが発生しました。:
ORA-00900: SQL文が無効です。
ORA-00600: 内部エラー・コード, 引数: [qcsTVCApplyqbc:nameres], [], [], [], [], [], [], [], [], [], [], [] ヘルプ:
https://docs.oracle.com/error-help/db/ora-00900/

 

.trcファイルにも同様のログあり

Incident 222887 created, dump file: /opt/oracle/diag/rdbms/free/FREE/incident/incdir_222887/FREE_ora_4633_i222887.trc
ORA-00600: 内部エラー・コード, 引数: [qcsTVCApplyqbc:nameres], [], [], [], [], [], [], [], [], [], [], []

 

上記無名PL/SQLブロックスクリプトを以下のように書き換えてみます。 違いはVARRAY配列を利用していないという1点です。
VARRAY配列の要素をmulti row insert文で直接使うことはできない(現状)ことがわかります!(まじかー!)
なお、このケースではINSERT文で表の列を指定しても指定しなくても影響はありません。(他のケースではこの表の列指定が影響する問題がありました。後述)

SCOTT@localhost:1521/freepdb1> !cat bug1_1.sql
DECLARE
c11 NUMBER;
c12 NUMBER;
c81 VARCHAR2(500);
c82 VARCHAR2(500);
BEGIN
c11 := 1;
c12 := 2;
c81 := LPAD(TO_CHAR(c11),373,'x');
c82 := LPAD(TO_CHAR(c12),373,'x');
INSERT INTO mrows_ins_tab(id,col8) VALUES
(c11, c81)
, (c12, c82)
;
END;
/

SCOTT@localhost:1521/freepdb1> @bug1_1

PL/SQLプロシージャが正常に完了しました。

 

さらに書き換えてみます。このテストケースでは、VARRAY配列の要素を使うことが原因でエラーになっているようです。

SCOTT@localhost:1521/freepdb1> !cat bug1_2.sql
DECLARE
c11 NUMBER;
c12 NUMBER;
c81 VARCHAR2(500);
c82 VARCHAR2(500);
BEGIN
c11 := 1;
c12 := 2;
c81 := LPAD(TO_CHAR(c11),373,'x');
c82 := LPAD(TO_CHAR(c12),373,'x');
INSERT INTO mrows_ins_tab(id, col1, col2, col3, col4, col5, col6, col7, col8) VALUES
(c11, null, null, null, null, null, null, null, c81)
, (c12, null, null, null, null, null, null, null, c82)
;
END;
/

SCOTT@localhost:1521/freepdb1> @bug1_2

PL/SQLプロシージャが正常に完了しました。

 

さらに書き換えて、表の列指定を行わない構文(このような構文はほとんどの現場では使えないと思いますが、。。。)でも正常に実行できています。ポイントは、VARRAY配列の要素を直接渡せない(現状)って部分だけです(ここまでのテストケースでは)。
なお、ここまでは表の所有者であるSCOTTユーザで接続して検証を行いました。
次のテストケースでは、SCOTT2ユーザで接続し、DB Link経由でSCOTTの表へMulti row insertするテストを行ってみます。。。そこでも色々おきます!

SCOTT@localhost:1521/freepdb1> !cat bug1_3.sql
DECLARE
c11 NUMBER;
c12 NUMBER;
c81 VARCHAR2(500);
c82 VARCHAR2(500);
BEGIN
c11 := 1;
c12 := 2;
c81 := LPAD(TO_CHAR(c11),373,'x');
c82 := LPAD(TO_CHAR(c12),373,'x');
INSERT INTO mrows_ins_tab VALUES
(c11, null, null, null, null, null, null, null, c81)
, (c12, null, null, null, null, null, null, null, c82)
;
END;
/

SCOTT@localhost:1521/freepdb1> @bug1_3

PL/SQLプロシージャが正常に完了しました。

 

バグ? or 現時点の仕様、または制限? その2 PL/SQLではローカルのデータベースへアクセスことになるため、通常はSQL*Netを経由するNetwork Round Tripは発生しません。
ただ、一般的なアプリケーションではアプリケーションサーバーとデータベースサーバー間では、SQL*Net経由でNetwork Round Tripが発生するほうが普通です。
そのような状況を想定してDB Linkを経由させることで、意図的にSQL*Net経由でNetwork Round Tripを発生させるテストケースも試したいなーと、準備していた時に気づいた、とうかハマったバグ?、または現時点の仕様、制限です。

まず、scott.emp表を単一インスタンではありますが、無理やりscott2ユーザーからdb link経由でリモート表としてMulti row insertする準備から。

権限は雑に許可しちゃってます(特にシステム権限あたり)
準備

SYSTEM@localhost:1521/freepdb1> create user scott2 identified by [password] default tablespace users temporary tablespace temp quota unlimited on users;

ユーザーが作成されました。
SYSTEM@localhost:1521/freepdb1> grant select any dictionary to scott2;

権限付与が成功しました。
SYSTEM@localhost:1521/freepdb1> grant drop any table to scott2;

権限付与が成功しました。
SYSTEM@localhost:1521/freepdb1> grant connect , resource , create database link to scott2;

権限付与が成功しました。
SCOTT@localhost:1521/freepdb1> grant select on scott.mrows_ins_tab to scott2;

権限付与が成功しました。
SCOTT@localhost:1521/freepdb1> grant insert on mrows_ins_tab to scott2;

権限付与が成功しました。
SCOTT@localhost:1521/freepdb1> grant delete on mrows_ins_tab to scott2;

権限付与が成功しました。
SCOTT@localhost:1521/freepdb1> grant update on mrows_ins_tab to scott2;

権限付与が成功しました。
[oracle@arm64-oraclelinux8u10 ~]$ sqlplus scott2@localhost:1521/freepdb1

SCOTT2@localhost:1521/freepdb1> create database link link2scott connect to scott identified by [password] using 'localhost:1521/freepdb1';

データベース・リンクが作成されました。
COTT2@localhost:1521/freepdb1> select * from mrows_ins_tab@link2scott;

レコードが選択されませんでした。
SCOTT2@localhost:1521/freepdb1> create synonym mrows_ins_tab for mrows_ins_tab@link2scott;

シノニムが作成されました。
SCOTT2@localhost:1521/freepdb1> select * from mrows_ins_tab;

レコードが選択されませんでした。
SCOTT2@localhost:1521/freepdb1> select table_name from user_tables;

レコードが選択されませんでした。
SCOTT2@localhost:1521/freepdb1> truncate table scott.mrows_ins_tab;

表が切り捨てられました。

 

scott2に接続して、その1と同じことを試してみます。(scott.mrows_ins_tabをDB Link経由でアクセスしている点が異なります)
以下は、ローカル表アクセスと同じ結果なので想定通りの結果です。

SCOTT2@localhost:1521/freepdb1> !cat bug1.sql
DECLARE
c11 NUMBER;
c12 NUMBER;
c81 VARCHAR2(500);
c82 VARCHAR2(500);
cTYPE c1_type IS VARRAY(2) OF NUMBER;
cTYPE c8_type IS VARRAY(2) OF VARCHAR2(500);
cc1 c1_type := c1_type();
cc8 c8_type := c8_type();
BEGIN
c1.extend(2);
c8.extend(2);
c1(1) := 1;
c1(2) := 2;
c8(1) := LPAD(TO_CHAR(c1(1)),373,'x');
c8(2) := LPAD(TO_CHAR(c1(2)),373,'x');
INSERT INTO mrows_ins_tab(id,col8) VALUES
(c1(1), c8(1))
, (c1(2), c8(2))
;
END;
/


SCOTT2@localhost:1521/freepdb1> @bug1
c82 VARCHAR2(500);
*
行5でエラーが発生しました。:
ORA-00900: SQL文が無効です。
ORA-00600: 内部エラー・コード, 引数: [qcsTVCApplyqbc:nameres], [], [], [], [], [], [], [], [], [], [], [] ヘルプ:
https://docs.oracle.com/error-help/db/ora-00900/

 

問題はローカル表へのMulti row insertでは正常に実行できた残りの3ケース。 以下のケース。ローカル表では正常に実行されましたが、VARRAY配列を利用した場合と異なる点は、ORA-07445 になっているということです。SIGSEGVなので、これはバグの類です。

SCOTT2@localhost:1521/freepdb1> !cat bug1_1.sql
DECLARE
c11 NUMBER;
c12 NUMBER;
c81 VARCHAR2(500);
c82 VARCHAR2(500);
BEGIN
c11 := 1;
c12 := 2;
c81 := LPAD(TO_CHAR(c11),373,'x');
c82 := LPAD(TO_CHAR(c12),373,'x');
INSERT INTO mrows_ins_tab(id,col8) VALUES
(c11, c81)
, (c12, c82)
;
END;
/

SCOTT2@localhost:1521/freepdb1> @bug1_1
c82 VARCHAR2(500);
*
行5でエラーが発生しました。:
OORA-03113: 通信チャネルでend-of-fileが検出されました プロセスID:
5150
セッションID: 17、シリアル番号: 17264 ヘルプ:
https://docs.oracle.com/error-help/db/ora-03113/

 

.trcファイルから本当の原因を確認してみると。。。。 ORA-07445!

Incident 222926 created, dump file: /opt/oracle/diag/rdbms/free/FREE/incident/incdir_222926/FREE_ora_5225_i222926.trc
OORA-07445: 例外が検出されました: コア・ダンプ [qcsorcqb()+784] [SIGSEGV] [ADDR:0x0] [PC:0xF0F3084] [Address not mapped to object] []

 

次のケースでは、また別の原因でエラーに? この文もローカル表では正常に実行できていましたよね?(前述)

SCOTT2@localhost:1521/freepdb1> !cat bug1_2.sql
DECLARE
c11 NUMBER;
c12 NUMBER;
c81 VARCHAR2(500);
c82 VARCHAR2(500);
BEGIN
c11 := 1;
c12 := 2;
c81 := LPAD(TO_CHAR(c11),373,'x');
c82 := LPAD(TO_CHAR(c12),373,'x');
INSERT INTO mrows_ins_tab(id, col1, col2, col3, col4, col5, col6, col7, col8) VALUES
(c11, null, null, null, null, null, null, null, c81)
, (c12, null, null, null, null, null, null, null, c82)
;
END;
/


SCOTT2@localhost:1521/freepdb1> @bug1_2
INSERT INTO mrows_ins_tab(id, col1, col2, col3, col4, col5, col6, col7, col8) VALUES
*
行11でエラーが発生しました。:
ORA-06550: 行11、列77:
PL/SQL: ORA-00904: "COL8": 無効な識別子です。

ORA-06550: 行11、列5:
PL/SQL: SQL Statement ignored
ヘルプ: https://docs.oracle.com/error-help/db/ora-06550/

 

.trcファイルから本当の原因を探ると。。でてました。また別のログが!!!! 
これって、現時点では実装されてない? ってこと? だとすると現時点の仕様という制限だろうか。。

 fsd_notify_cb: FsDirect not implemented

 


さて、最後に、留め! (違 以下のケースもローカル表へ実行した場合は正常に実行できていました(前述)
これ、おそらく、実装されてない感じですかね。
PL/SQLのエンジンがOracle Databaseのカーネルに組み込まれていなかったOracle 7の頃とか、この類の新規構文の実装時期がズレるって珍しくなかったのですが、久々にこのような状況を見てノスタルジーを感じる、セピア色www

 

これどうやって回避できんのかなぁ!?

SCOTT2@localhost:1521/freepdb1> !cat bug1_3.sql
DECLARE
c11 NUMBER;
c12 NUMBER;
c81 VARCHAR2(500);
c82 VARCHAR2(500);
BEGIN
c11 := 1;
c12 := 2;
c81 := LPAD(TO_CHAR(c11),373,'x');
c82 := LPAD(TO_CHAR(c12),373,'x');
INSERT INTO mrows_ins_tab VALUES
(c11, null, null, null, null, null, null, null, c81)
, (c12, null, null, null, null, null, null, null, c82)
;
END;
/

SCOTT2@localhost:1521/freepdb1> @bug1_3
DECLARE
*
行1でエラーが発生しました。:
ORA-00900: SQL文が無効です。
ORA-00600: 内部エラー・コード, 引数: [qctcopn_internal: null_colkcc], [0], [0], [0], [0], [0], [1], [0], [], [], [], [] ヘルプ:
https://docs.oracle.com/error-help/db/ora-00900/

 

.trcファイルでも同じ

Incident 222671 created, dump file: /opt/oracle/diag/rdbms/free/FREE/incident/incdir_222671/FREE_ora_5572_i222671.trc
ORA-00600: 内部エラー・コード, 引数: [qctcopn_internal: null_colkcc], [0], [0], [0], [0], [0], [1], [0], [], [], [], []

 

 


さて、ほんとうに試した事とから随分と遠くにきてしまった感じがしますが、、逃げ道を見つけましたw

 

対策をしばし考え中。。。。現状回避できる方法はあるのでは?。。。。わずかな期待。。。

PL/SQLには、他の方法として、 EXECUTE IMMEDIATEを使う方法はあるか。。。その分のオーバーヘッドは乗ってしまうわけだが。。。。

試してみる! (全テストケース NGとなったDB Link経由のケースで確認する。このケースで実行できればローカルでも動作するはずだ。。。。と)

 

EXECUTE IMMEDIATEでバイント変数を利用するためにUSING句を使ってみました。ためにし、VARRAY配列を使っています。また、表も列指定しています。ここでローカル表でのテストでも発生した
fsd_notify_cb: FsDirect not implementedがトレースファイルに記録されていました。!!! VARRAYはEXECUTE IMMEDIATEでMulti row insertする場合でもダメなのか??。

SCOTT2@localhost:1521/freepdb1> !cat bug1_4.sql
DECLARE
sql_text CLOB := EMPTY_CLOB();
TYPE c1_type IS VARRAY(2) OF NUMBER;
TYPE c8_type IS VARRAY(2) OF VARCHAR2(500);
c1 c1_type := c1_type();
c8 c8_type := c8_type();
BEGIN
c1.extend(2);
c8.extend(2);
c1(1) := 1;
c1(2) := 2;
c8(1) := LPAD(TO_CHAR(c1(1)),373,'x');
c8(2) := LPAD(TO_CHAR(c1(2)),373,'x');
sql_text := 'INSERT INTO mrows_ins_tab(id, col8) VALUES'
|| '(:c11, :c81)'
|| ', (:c12, :c82);';
EXECUTE IMMEDIATE sql_text
USING c1(1), c8(1), c1(2), c8(2);
END;
/

SCOTT2@localhost:1521/freepdb1> @bug1_4
DECLARE
*
行1でエラーが発生しました。:
ORA-00904: "COL8": 無効な識別子です。
ORA-06512: 行17
ヘルプ: https://docs.oracle.com/error-help/db/ora-00904/

 

.trcファイル

fsd_notify_cb: FsDirect not implemented

 

まずは、切り分けのために、VARRAY配列の使用をやめてみます。表の列指定だけは残してありますが、これでも同じエラーが発生します!!

SCOTT2@localhost:1521/freepdb1> !cat bug1_5.sql
DECLARE
sql_text CLOB := EMPTY_CLOB();
c11 NUMBER;
c12 NUMBER;
c81 VARCHAR2(500);
c82 VARCHAR2(500);
BEGIN
c11 := 1;
c12 := 2;
c81 := LPAD(TO_CHAR(c11),373,'x');
c82 := LPAD(TO_CHAR(c12),373,'x');
sql_text := 'INSERT INTO mrows_ins_tab(id, col8) VALUES'
|| '(:c11, :c81)'
|| ', (:c12, :c82);';
EXECUTE IMMEDIATE sql_text
USING c11, c81, c12, c82;
END;
/

SCOTT2@localhost:1521/freepdb1> @bug1_5
DECLARE
*
行1でエラーが発生しました。:
ORA-00904: "COL8": 無効な識別子です。
ORA-06512: 行15
ヘルプ: https://docs.oracle.com/error-help/db/ora-00904/

 

.trcファイル

fsd_notify_cb: FsDirect not implemented

 

気を取り直してw
USING句ではVARRAY配列を利用し、表の列指定だけを行わないようにしてみます。

 

おおおおおおお、やっと逃げ道発見! w 変数は配列でもOKなので、取り合えず、行数増やしてもプログラミングで捌ける:)

SCOTT2@localhost:1521/freepdb1> !cat bug1_6.sql
DECLARE
sql_text CLOB := EMPTY_CLOB();
TYPE c1_type IS VARRAY(2) OF NUMBER;
TYPE c8_type IS VARRAY(2) OF VARCHAR2(500);
c1 c1_type := c1_type();
c8 c8_type := c8_type();
BEGIN
c1.extend(2);
c8.extend(2);
c1(1) := 1;
c1(2) := 2;
c8(1) := LPAD(TO_CHAR(c1(1)),373,'x');
c8(2) := LPAD(TO_CHAR(c1(2)),373,'x');
sql_text := 'INSERT INTO mrows_ins_tab VALUES'
|| '(:c11, null, null, null, null, null, null, null, :c81)'
|| ', (:c12, null, null, null, null, null, null, null, :c82);';
EXECUTE IMMEDIATE sql_text
USING c1(1), c8(1), c1(2), c8(2);
END;
/

SCOTT2@localhost:1521/freepdb1> @bug1_6

PL/SQLプロシージャが正常に完了しました。

 

 

この方法でもいける! 
ただ、配列使えるなら配列のほうが便利なので、一つ前の方式に決定 EXECUTE IMMEDIATEだと全部リテラルというケースも試せるからEXECUTE IMMEDIATEでのるある程度のオーバーヘッドは見えて良いのかも。
ただ、EXECUTE IMMEDIATEを利用しない場合のポテンシャルは確認できないのが、ちょっと残念。

SCOTT2@localhost:1521/freepdb1> !cat bug1_7.sql
DECLARE
c11 NUMBER;
c12 NUMBER;
c81 VARCHAR2(500);
c82 VARCHAR2(500);
sql_text CLOB := EMPTY_CLOB();
BEGIN
c11 := 1;
c12 := 2;
c81 := LPAD(TO_CHAR(c11),373,'x');
c82 := LPAD(TO_CHAR(c12),373,'x');
sql_text := 'INSERT INTO mrows_ins_tab VALUES'
|| '(:c11, null, null, null, null, null, null, null, :c81)'
|| ', (:c12, null, null, null, null, null, null, null, :c82);';
EXECUTE IMMEDIATE sql_text
USING c11, c81, c12, c82;
END;
/

SCOTT2@localhost:1521/freepdb1> @bug1_7

PL/SQLプロシージャが正常に完了しました。

 

いや、まじで、Multi row insert構文をPL/SQLで使おうとすると結構な壁というか問題残ってそうですね。
とはいえ、PL/SQLでやるなら FORALLのバルクインサートもあるわけで、困ることはないだろうなとも思われ(現状)
とはいえ、PL/SQLでやるならローカル表の場合なら、FORALLでバルクインサートでもよいのだろうけども、
リモート表だとFORALLは使えないのでまあ、DB Link使ってる環境では注意した方が良いよね。ローカルからにする方法にするとか。

そもそもエラーで挙動周りの差異を見切れてないのは心残りではあるけども、次回りリースのai新版以降のお楽しみということで.... :)

ほんとうに試したかったのとはちょっとちがうが、Multi row insert文、リテラル値じゃなくてバインド変数を使えば、メモリ消費は抑えられるよっていう、ネタ含めた次の話題は書けそうな気がしてきた。

以下の方式(EXECUTE IMMEDIATEを利用する方法を代替策にして)でバインド変数化とリテラル値を利用したMulti row insertのメモリ消費を見るネタへ続く。(予定w)

DECLARE
sql_text CLOB := EMPTY_CLOB();
TYPE c1_type IS VARRAY(2) OF NUMBER;
TYPE c8_type IS VARRAY(2) OF VARCHAR2(500);
c1 c1_type := c1_type();
c8 c8_type := c8_type();
BEGIN
c1.extend(2);
c8.extend(2);
c1(1) := 1;
c1(2) := 2;
c8(1) := LPAD(TO_CHAR(c1(1)),373,'x');
c8(2) := LPAD(TO_CHAR(c1(2)),373,'x');
sql_text := 'INSERT INTO mrows_ins_tab VALUES'
|| '(:c11, null, null, null, null, null, null, null, :c81)'
|| ', (:c12, null, null, null, null, null, null, null, :c82);';
EXECUTE IMMEDIATE sql_text
USING c1(1), c8(1), c1(2), c8(2);
END;
/

 

いや、まじでいろいろハマり過ぎだろ、ってぐらいハマったw

 

ではまた。

 


関連エントリ
帰ってきた! 標準はあるにはあるが癖の多いSQL #20 - Table Value Constructer (TVC)
帰ってきた! 標準はあるにはあるが癖の多いSQL #21 - Table Value Constructer(TVC)- ハードパース時間とメモリ消費量 / BONUS TRACK
帰ってきた! 標準はあるにはあるが癖の多いSQL #22 - Multi Row INSERT

| | | コメント (0)

2026年1月 8日 (木)

帰ってきた! 標準はあるにはあるが癖の多いSQL #22 - Multi Row INSERT

Previously on Mac De Oracle
と言いたいところですが、前回は、読了ネタだったのでw 前々回の話に関連して。。。

前々回は、帰ってきた! 標準はあるにはあるが癖の多いSQL #21 - Table Value Constructer(TVC)- ハードパース時間とメモリ消費量 / BONUS TRACKでした。
おまけのおまけ的な内容で前々回のネタと関連はあるのですが、TVCではなく、Multi Row INSERTの癖の確認というか、メモリ消費量などの傾向を、肌感覚で覚えておきましょうね。 というネタです。

Oracle Databaseで以下のような表を作成、PostgreSQL/MySQLでも同様に作成しておきます。

create table mrows_ins_tab
(
id integer not null primary key,
col1 varchar2(1000),
col2 varchar2(1000),
col3 varchar2(1000),
col4 varchar2(1000),
col5 varchar2(1000),
col6 varchar2(1000),
col7 varchar2(1000),
col8 varchar2(500)
);


Oracle Databaseの方言、multi table insertの対象を単一表にしてmulti row insertのPGA消費傾向を見る。
行数を変えつつ以下のようなINSERT ALL文にて検証。

SCOTT@localhost:1521/freepdb1> !cat sql_mrows_ins_5000.sql
INSERT ALL
INTO mrows_ins_tab(id, col8) VALUES(1,
'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx1')

...略...

INTO mrows_ins_tab(id, col8) VALUES(5000,
'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx5000')
SELECT * FROM dual;

手始めに5000行をガツンとINSERT ALLにて。この程度の行サイズと行数でも300MB超えのPGA消費ですね。

Oracle Database 23ai Free Release 23.0.0.0.0 - Develop, Learn, and Run for Free
Version 23.8.0.25.04
に接続されました。

SCOTT@localhost:1521/freepdb1> @show_mystats.sql

SID NAME VALUE
---------- ---------------------------------------------------------------- ----------
39 CPU used by this session 4
39 CPU used when call started 3

...略...

39 session pga memory 4254712
39 session pga memory max 5123744
39 session uga memory 1844408
39 session uga memory max 3053904

...略...

SCOTT@localhost:1521/freepdb1> @sql_mrows_ins_5000

5000行が作成されました。

経過: 00:00:05.95
SCOTT@localhost:1521/freepdb1> @show_mystats.sql

SID NAME VALUE
---------- ---------------------------------------------------------------- ----------
39 CPU used by this session 550
39 CPU used when call started 550

...略...

39 session logical reads 26402
39 session pga memory 88534008
39 session pga memory max 339274744
39 session uga memory 84055376
39 session uga memory max 85103896

...略...


行数を3倍の15,000行に。こんなことする方はいないと思いますけども。。。予想以上にPGAを消費しますね。

SCOTT@localhost:1521/freepdb1> @show_mystats.sql

SID NAME VALUE
---------- ---------------------------------------------------------------- ----------
205 CPU used by this session 5
205 CPU used when call started 4

...略...

205 session pga memory 4123640
205 session pga memory max 5123744
205 session uga memory 1839072
205 session uga memory max 3053904

...略...

SCOTT@localhost:1521/freepdb1> @sql_mrows_ins_15000

15000行が作成されました。

経過: 00:01:45.96
SCOTT@localhost:1521/freepdb1> @show_mystats.sql

SID NAME VALUE
---------- ---------------------------------------------------------------- ----------
205 CPU used by this session 9762
205 CPU used when call started 9762

...略...

205 session pga memory 257551352
205 session pga memory max 1402072056
205 session uga memory 248118456
205 session uga memory max 252312704

...略...


前述のINSERT ALLで 5,000行、15,000行の一括インサートでのPGA/UGAの最大サイズは以下の通りでした。
session pga memory max

  • 318.7 MB / 5,000rows
  • 1,332.23MB / 15,000rows

session uga memory max

  • 78.2 MB / 5,000rows
  • 237.71MB / 15,000rows


今回の環境とINSERT ALLの行数、行サイズでは 20,000行で、PGA_AGGREGATE_LIMITを超える結果となりました。
23ai FREEなので使えるメモリサイズが元々少ないので比較的簡単に制限を超過しちゃいますね

SCOTT@localhost:1521/freepdb1> @show_mystats.sql

SID NAME VALUE
---------- ---------------------------------------------------------------- ----------
203 CPU used by this session 5
203 CPU used when call started 4

...略...

203 session pga memory 4254712
203 session pga memory max 5123744
203 session uga memory 1844408
203 session uga memory max 3053904

...略...

SCOTT@localhost:1521/freepdb1> @sql_mrows_ins_20000
INTO mrows_ins_tab(id, col8) VALUES(1,
*
行2でエラーが発生しました。:
ORA-04036: インスタンスまたはPDBにより使用されるPGAメモリーがPGA_AGGREGATE_LIMITを超えています。 ヘルプ:
https://docs.oracle.com/error-help/db/ora-04036/

上記で利用したINSERT文を生成したスクリプトです。参考まで。

set feed off
set timi off
set head off
set termout off
set veri off
set trimspool on

col col1 for a20
col col2 for a20
col col3 for a20
col col4 for a20
col col5 for a20
col col6 for a20
col col7 for a20
col col8 for a20
set linesize 400
set pagesize 1000
SET SERVEROUTPUT ON
spool sql_mrows_ins_&1..sql
DECLARE
c_max_rows CONSTANT NUMBER := &1;
BEGIN
DBMS_OUTPUT.PUT_LINE('INSERT ALL ');
FOR i IN 1..c_max_rows LOOP
DBMS_OUTPUT.PUT_LINE(
'INTO mrows_ins_tab(id, col8) VALUES('
|| TO_CHAR(i)
|| ', ''' || LPAD(TO_CHAR(i),373,'x') || ''')'
);
END LOOP;
DBMS_OUTPUT.PUT_LINE('SELECT * FROM dual;');
END;
/
spool off
SET SERVEROUTPUT OFF
UNDEFINE 1

set head on
set termout on
set feed on
set veri on
set timi on
set trimspool off


ここまでは、Oracle Databaseのmulti table insert(Oracle Databaseの方言)をつかった multi row insertでしたが、
次はPostgreSQLでも実行できるmulti row insert構文で同様の検証を行ってみます!


multi row insertを利用した場合のPGA消費量確認

見ての通り、Multi table insert文より、PGAの消費は緩やかではあるものの、それなりの消費量ですね。

これらの結果から、適当な行数(行サイズにもよるが)、10行〜多くても1000行程度ぐらいの範囲で一括INSERTするのが
性能とメモリ消費ではバランスが良さそうな感じに思えますね。
物理メモリが大量にあってPGAにそれなりに割り振れるとしても、ハードパース時間はかなり伸びですしそれを回避するのは難しいので。

こんな感じのmulti row insert文を生成(PostgreSQLも同じ文を利用できます)。なお、行数は適宜調整。

[oracle@arm64-oraclelinux8u10 ~]$ cat sql_mrows_ins_65535.sql
INSERT INTO mrows_ins_tab(id, col8) VALUES
(1, 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx1')

...略...

, (65535, 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx65535')
;


まずは、5,000行から。
147MB程度のPGA消費なので、INSERT ALLにくらべると半分ぐらいの消費になってますね。

SCOTT@localhost:1521/freepdb1> @show_mystats.sql

SID NAME VALUE
---------- ---------------------------------------------------------------- ----------
203 CPU used by this session 2
203 CPU used when call started 1

...略...

203 session pga memory 3419808
203 session pga memory max 3747488
203 session uga memory 1456416
203 session uga memory max 1769824

...略...

SCOTT@localhost:1521/freepdb1> @sql_mrows_ins_5000

5000行が作成されました。

経過: 00:00:04.91
SCOTT@localhost:1521/freepdb1> @show_mystats.sql

SID NAME VALUE
---------- ---------------------------------------------------------------- ----------
203 CPU used by this session 491
203 CPU used when call started 491

...略...

203 session pga memory 3730424
203 session pga memory max 147647480
203 session uga memory 2297424
203 session uga memory max 4523744

...略...


同様に、3倍の15,000行。
INSERT ALL文と比べPGAの消費量は少なく、行数の割に緩やかに増加しているようですね。興味深い。
410MB程度のPGA消費。INSERT ALLで同様の行数を扱った検証では、1.3GB程度だったので50%以下に抑えられています。

23ai以降で、Multi row insertを行うなら、INSERT ALLは避けるべきですね。

SCOTT@localhost:1521/freepdb1> @show_mystats.sql

SID NAME VALUE
---------- ---------------------------------------------------------------- ----------
39 CPU used by this session 5
39 CPU used when call started 3

...略...

39 session pga memory 4254712
39 session pga memory max 5123744
39 session uga memory 1844408
39 session uga memory max 3053904

...略...

SCOTT@localhost:1521/freepdb1> @sql_mrows_ins_15000

15000行が作成されました。

経過: 00:01:03.97
SCOTT@localhost:1521/freepdb1> @show_mystats.sql

SID NAME VALUE
---------- ---------------------------------------------------------------- ----------
39 CPU used by this session 6381
39 CPU used when call started 6381

...略...

39 session pga memory 11660280
39 session pga memory max 412019704
39 session uga memory 4589264
39 session uga memory max 11204640

...略...


次のケース。
INSERT ALLでは、PGA_AGGREGATE_LIMITの制限サイズを超過してしまいましたが、PGA消費はおさえられ、本環境でも正常に処理されますね。ほう!
まだ、行けますね。
とはいえ、処理時間も随分長くなってます。これTVC同様ハードパース時間の影響が大きそうですよね。

SCOTT@localhost:1521/freepdb1> @show_mystats.sql

SID NAME VALUE
---------- ---------------------------------------------------------------- ----------
38 CPU used by this session 5
38 CPU used when call started 3

...略...

38 session pga memory 4254712
38 session pga memory max 5123744
38 session uga memory 1844408
38 session uga memory max 3053904

...略...

SCOTT@localhost:1521/freepdb1> @sql_mrows_ins_20000

20000行が作成されました。

経過: 00:02:18.09
SCOTT@localhost:1521/freepdb1> @show_mystats.sql

SID NAME VALUE
---------- ---------------------------------------------------------------- ----------
38 CPU used by this session 13538
38 CPU used when call started 13538

...略...

38 session pga memory 7007224
38 session pga memory max 550956024
38 session uga memory 5571464
38 session uga memory max 14349248

...略...


では、思い切って、50,000行にしてみましょう! なんとなくギリギリな感じですが、正常終了!
1,359MBほど使っちゃってますね。
この傾向、Table Value Constructorの傾向に近いですよね。はやり。

SCOTT@localhost:1521/freepdb1> @show_mystats.sql

SID NAME VALUE
---------- ---------------------------------------------------------------- ----------
40 CPU used by this session 5
40 CPU used when call started 3

...略...

40 session pga memory 4254712
40 session pga memory max 5123744
40 session uga memory 1844408
40 session uga memory max 3053904

...略...

SCOTT@localhost:1521/freepdb1> @sql_mrows_ins_50000

50000行が作成されました。

経過: 00:29:08.74
SCOTT@localhost:1521/freepdb1> @show_mystats.sql

SID NAME VALUE
---------- ---------------------------------------------------------------- ----------
40 CPU used by this session 173596
40 CPU used when call started 173596

...略...

40 session pga memory 31714296
40 session pga memory max 1359735800
40 session uga memory 11073624
40 session uga memory max 33220312

...略...


Table Value Constractorの傾向に類似しているので、行数上限がありそうだと思い。試してみた!
想像通り!!!!! エラーメッセージも同じです!!

SCOTT@localhost:1521/freepdb1> @show_mystats.sql

SID NAME VALUE
---------- ---------------------------------------------------------------- ----------
38 CPU used by this session 5
38 CPU used when call started 4

...略...

38 session pga memory 4254712
38 session pga memory max 5123744
38 session uga memory 1844408
38 session uga memory max 3053904

...略...

SCOTT@localhost:1521/freepdb1> @sql_mrows_ins_65535
INSERT INTO mrows_ins_tab(id, col8) VALUES
*
行1でエラーが発生しました。:
ORA-63805: 表値コンストラクタのタプルの最大数を超えました ヘルプ:
https://docs.oracle.com/error-help/db/ora-63805/


経過: 00:00:01.07


上記で利用したINSERT文を生成したスクリプトです。参考まで。
Oracle Database 23ai以降で利用できるMulti row insert文を生成するスクリプトは以下のとおり。

set feed off
set timi off
set head off
set termout off
set veri off
set trimspool on

col col1 for a20
col col2 for a20
col col3 for a20
col col4 for a20
col col5 for a20
col col6 for a20
col col7 for a20
col col8 for a20
set linesize 400
set pagesize 1000
SET SERVEROUTPUT ON
spool sql_mrows_ins_&1..sql
DECLARE
c_max_rows CONSTANT NUMBER := &1;
BEGIN
DBMS_OUTPUT.PUT_LINE('INSERT INTO mrows_ins_tab(id, col8) VALUES');
FOR i IN 1..c_max_rows LOOP
DBMS_OUTPUT.PUT_LINE(
CASE WHEN i > 1
THEN ', '
END
|| '('
|| TO_CHAR(i)
|| ', ''' || LPAD(TO_CHAR(i),373,'x') || ''')'
);
END LOOP;
DBMS_OUTPUT.PUT_LINE(';');
END;
/
spool off
SET SERVEROUTPUT OFF
UNDEFINE 1

set head on
set termout on
set feed on
set veri on
set timi on
set trimspool off


では、同様の方法で、PostgreSQL/MySQLのざっくりとしたメモリ消費などを確認しておきましょう。

次は、PostgreSQL

PostgreSQL、メモリ構造がOracle Databaseとは違うといっても、65535行登録で、302MB程度。随分違う。
とはいっても行数が多くなれば消費量は増えると思われ、Oracle Database同様、詰めすぎず、性能とメモリ消費のバランスが取れる単位にまとめるってのは、同じだろうと思います。

Password for user scott: 
psql (17.7)
Type "help" for help.

...略...

perftestdb=> \d+ mrows_ins_tab
Table "scott.mrows_ins_tab"
Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
--------+-------------------------+-----------+----------+---------+----------+-------------+--------------+-------------
id | integer | | not null | | plain | | |
col1 | character varying(1000) | | | | extended | | |
col2 | character varying(1000) | | | | extended | | |
col3 | character varying(1000) | | | | extended | | |
col4 | character varying(1000) | | | | extended | | |
col5 | character varying(1000) | | | | extended | | |
col6 | character varying(1000) | | | | extended | | |
col7 | character varying(1000) | | | | extended | | |
col8 | character varying(500) | | | | extended | | |
Indexes:
"mrows_ins_tab_pkey" PRIMARY KEY, btree (id)
Access method: heap



2026-01-07 20:45:03.765 JST [14222] LOG: PARSER STATISTICS
2026-01-07 20:45:03.765 JST [14222] DETAIL: ! system usage stats:
! 0.063931 s user, 0.000000 s system, 0.069344 s elapsed
! [0.063931 s user, 0.008076 s system total]
! 114684 kB max resident size
! 0/0 [0/0] filesystem blocks in/out
! 0/4816 [12/5839] page faults/reclaims, 0 [0] swaps
! 0 [0] signals rcvd, 0/0 [0/0] messages rcvd/sent
! 0/0 [6/0] voluntary/involuntary context switches
2026-01-07 20:45:03.765 JST [14222] STATEMENT: INSERT INTO mrows_ins_tab(id, col8) VALUES
(1, 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx1')
, (2,

...略...

, (65535, 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx65535')
;
2026-01-07 20:45:03.853 JST [14222] LOG: PARSE ANALYSIS STATISTICS
2026-01-07 20:45:03.853 JST [14222] DETAIL: ! system usage stats:
! 0.017787 s user, 0.000000 s system, 0.019316 s elapsed
! [0.144499 s user, 0.008076 s system total]
! 166148 kB max resident size
! 0/0 [0/0] filesystem blocks in/out
! 44/3185 [56/11332] page faults/reclaims, 0 [0] swaps
! 0 [0] signals rcvd, 0/0 [0/0] messages rcvd/sent
! 0/0 [120/0] voluntary/involuntary context switches
2026-01-07 20:45:03.853 JST [14222] STATEMENT: INSERT INTO mrows_ins_tab(id, col8) VALUES

...略...

2026-01-07 20:45:03.922 JST [14222] LOG: REWRITER STATISTICS
2026-01-07 20:45:03.922 JST [14222] DETAIL: ! system usage stats:
! 0.001438 s user, 0.000081 s system, 0.001645 s elapsed
! [0.203669 s user, 0.011451 s system total]
! 209480 kB max resident size
! 0/0 [0/0] filesystem blocks in/out
! 0/1 [56/13938] page faults/reclaims, 0 [0] swaps
! 0 [0] signals rcvd, 0/0 [0/0] messages rcvd/sent
! 0/0 [234/1] voluntary/involuntary context switches
2026-01-07 20:45:03.922 JST [14222] STATEMENT: INSERT INTO mrows_ins_tab(id, col8) VALUES

...略...

2026-01-07 20:45:04.183 JST [14222] LOG: PLANNER STATISTICS
2026-01-07 20:45:04.183 JST [14222] DETAIL: ! system usage stats:
! 0.176192 s user, 0.003606 s system, 0.195045 s elapsed
! [0.436538 s user, 0.018692 s system total]
! 235792 kB max resident size
! 0/0 [0/0] filesystem blocks in/out
! 2/3555 [58/17991] page faults/reclaims, 0 [0] swaps
! 0 [0] signals rcvd, 0/0 [0/0] messages rcvd/sent
! 0/1 [338/2] voluntary/involuntary context switches
2026-01-07 20:45:04.183 JST [14222] STATEMENT: INSERT INTO mrows_ins_tab(id, col8) VALUES

...略...

2026-01-07 20:45:04.346 JST [14222] LOG: XECUTOR STATISTICS
2026-01-07 20:45:04.346 JST [14222] DETAIL: ! system usage stats:
! 0.050664 s user, 0.022169 s system, 0.093750 s elapsed
! [0.542249 s user, 0.048167 s system total]
! 302548 kB max resident size
! 0/91376 [0/91376] filesystem blocks in/out
! 7272/5528 [7330/25997] page faults/reclaims, 0 [0] swaps
! 0 [0] signals rcvd, 0/0 [0/0] messages rcvd/sent
! 17/2 [474/4] voluntary/involuntary context switches
2026-01-07 20:45:04.346 JST [14222] STATEMENT: INSERT INTO mrows_ins_tab(id, col8) VALUES

...略...


最後に、MySQL

MySQLでは列値コンストラクタが必要なこと以外、Oracle Database/PostgreSQLと同じです。

[oracle@arm64-oraclelinux8u10 ~]$ cat sql_mrows_ins_65535_mysql.sql
INSERT INTO mrows_ins_tab(id, col8) VALUES
ROW(1, 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx1')

...略...

, ROW(65535, 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx65535')
;

MySQLもPostgreSQL同様、Oracle Databaseのような行数上限は無いですが、メモリ消費はそれなりにありますね。
やはり注意しておきたい部分ではありますね。無邪気に詰め込みすぎないのがリーズナブルだと思います。
65535行で、263MBぐらい。

[master@Oracle-Linux-8u10-arm64-2 ~]$ mysql -u scott -D perftestdb -p -h localhost 
Enter password:
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 8
Server version: 8.4.7 MySQL Community Server - GPL

...略...

mysql> desc mrows_ins_tab;
+-------+---------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+---------------+------+-----+---------+-------+
| id | int | NO | PRI | NULL | |
| col1 | varchar(1000) | YES | | NULL | |
| col2 | varchar(1000) | YES | | NULL | |
| col3 | varchar(1000) | YES | | NULL | |
| col4 | varchar(1000) | YES | | NULL | |
| col5 | varchar(1000) | YES | | NULL | |
| col6 | varchar(1000) | YES | | NULL | |
| col7 | varchar(1000) | YES | | NULL | |
| col8 | varchar(500) | YES | | NULL | |
+-------+---------------+------+-----+---------+-------+
9 rows in set (0.00 sec)

mysql> SELECT * from performance_schema.users WHERE USER='scott';
+-------+---------------------+-------------------+-------------------------------+--------------------------+
| USER | CURRENT_CONNECTIONS | TOTAL_CONNECTIONS | MAX_SESSION_CONTROLLED_MEMORY | MAX_SESSION_TOTAL_MEMORY |
+-------+---------------------+-------------------+-------------------------------+--------------------------+
| scott | 1 | 1 | 1465424 | 2914540 |
+-------+---------------------+-------------------+-------------------------------+--------------------------+
1 row in set (0.00 sec)

mysql> SELECT MAX_TOTAL_MEMORY from performance_schema.events_statements_history WHERE SQL_TEXT LIKE 'INSERT INTO mrows_ins_tab(id, col8) VALUES%';
Empty set (0.00 sec)

mysql> \. sql_mrows_ins_65535_mysql.sql
Query OK, 65535 rows affected (0.59 sec)
Records: 65535 Duplicates: 0 Warnings: 0

mysql> SELECT MAX_TOTAL_MEMORY from performance_schema.events_statements_history WHERE SQL_TEXT LIKE 'INSERT INTO mrows_ins_tab(id, col8) VALUES%';
+------------------+
| MAX_TOTAL_MEMORY |
+------------------+
| 263061721 |
+------------------+
1 row in set (0.00 sec)

mysql> SELECT * from performance_schema.users WHERE USER='scott';
+-------+---------------------+-------------------+-------------------------------+--------------------------+
| USER | CURRENT_CONNECTIONS | TOTAL_CONNECTIONS | MAX_SESSION_CONTROLLED_MEMORY | MAX_SESSION_TOTAL_MEMORY |
+-------+---------------------+-------------------+-------------------------------+--------------------------+
| scott | 1 | 1 | 235981664 | 263061721 |
+-------+---------------------+-------------------+-------------------------------+--------------------------+
1 row in set (0.00 sec)


では、また。


デスクワークだと日中動かないので、手の部分がやたら冷える。。。
軽くBeat Saberで体動かすと、一時的には温まるのだけどもw

冬らしい、寒さの、東京より。





関連エントリー
標準はあるにはあるが癖の多いSQL 全部俺 #1 Pagination
標準はあるにはあるが癖の多いSQL 全部俺 #2 関数名は同じでも引数が逆の罠!
標準はあるにはあるが癖の多いSQL 全部俺 #3 データ型確認したい時あるんです
標準はあるにはあるが癖の多いSQL 全部俺 #4 リテラル値での除算の内部精度も違うのよ!
標準はあるにはあるが癖の多いSQL 全部俺 #5 和暦変換機能ある方が少数派
標準はあるにはあるが癖の多いSQL 全部俺 #6 時間厳守!
標準はあるにはあるが癖の多いSQL 全部俺 #7 期間リテラル!
標準はあるにはあるが癖の多いSQL 全部俺 #8 翌月末日って何日?
標準はあるにはあるが癖の多いSQL 全部俺 #9 部分文字列の扱いでも癖が出る><
標準はあるにはあるが癖の多いSQL 全部俺 #10 文字列連結の罠(有名なやつ)
標準はあるにはあるが癖の多いSQL 全部俺 #11 デュエル、じゃなくて、デュアル
標準はあるにはあるが癖の多いSQL 全部俺 #12 文字[列]探すにも癖がある
標準はあるにはあるが癖の多いSQL 全部俺 #13 あると便利ですが意外となかったり
標準はあるにはあるが癖の多いSQL 全部俺 #14 連番の集合を返すにも癖がある
標準はあるにはあるが癖の多いSQL 全部俺 #15 SQL command line client
標準はあるにはあるが癖の多いSQL 全部俺 #16 SQLのレントゲンを撮る方法
標準はあるにはあるが癖の多いSQL 全部俺 #17 その空白は許されないのか?
標準はあるにはあるが癖の多いSQL 全部俺 #18 (+)の外部結合は方言
標準はあるにはあるが癖の多いSQL 全部俺 #19 帰ってきた、部分文字列の扱いでも癖w
標準はあるにはあるが癖の多いSQL 全部俺 #20 結果セットを単一列に連結するにも癖がある
標準はあるにはあるが癖の多いSQL 全部俺 #21 演算結果にも癖がある
標準はあるにはあるが癖の多いSQL 全部俺 #22 集合演算にも癖がある
標準はあるにはあるが癖の多いSQL 全部俺 #23 複数行INSERTにも癖がある
標準はあるにはあるが癖の多いSQL 全部俺 #24 乱数作るにも癖がある
標準はあるにはあるが癖の多いSQL 全部俺 #25 SQL de Fractalsにも癖がある:)
標準はあるにはあるが癖の多いSQL 全部俺 おまけ SQL de 湯婆婆やるにも癖がでるw
帰ってきた! 標準はあるにはあるが癖の多いSQL #1 SQL de ROT13 やるにも癖が出るw
帰ってきた! 標準はあるにはあるが癖の多いSQL #2 Actual Plan取得中のキャンセルでも癖が出る
帰ってきた! 標準はあるにはあるが癖の多いSQL #3 オプティマイザの結合順評価テーブル数上限にも癖が出る
帰ってきた! 標準はあるにはあるが癖の多いSQL #4 Optimizer Traceの取得でも癖がでる
帰ってきた! 標準はあるにはあるが癖の多いSQL #5 - Optimizer Hint でも癖が多い
帰ってきた! 標準はあるにはあるが癖の多いSQL #6 - Hash Joinの結合ツリーにも癖がでる
帰ってきた! 標準はあるにはあるが癖の多いSQL #7 - Hash Joinの実行計画にも癖がでる
帰ってきた! 標準はあるにはあるが癖の多いSQL #8 - Hash Joinさせるにも癖が出る
帰ってきた! 標準はあるにはあるが癖の多いSQL #9、BOOLEAN型にも癖が出る
帰ってきた! 標準はあるにはあるが癖の多いSQL #10、BOOLEAN型にも癖が出る(後編)
帰ってきた! 標準はあるにはあるが癖の多いSQL #10、BOOLEAN型にも癖が出る(後編)の おまけ - SQL*PlusのautotraceでSQL Analysis Reportが出力される! (23ai〜)
帰ってきた! 標準はあるにはあるが癖の多いSQL #11 - 引用符にも癖がでるし、NULLのソート構文にも癖がある!(前編)
帰ってきた! 標準はあるにはあるが癖の多いSQL #12 - 引用符にも癖がでるし、NULLのソート構文にも癖がある!(後編)ー 列エイリアスの扱いにも癖がある!
帰ってきた! 標準はあるにはあるが癖の多いSQL #13 - コメント書くにも癖がある
帰ってきた! 標準はあるにはあるが癖の多いSQL #14 - コメントを書く位置にも癖がでる (SQL Clientにも癖がある)
帰ってきた! 標準はあるにはあるが癖の多いSQL #15 - 実行計画でスカラー副問合せの見せ方にも癖がでる
帰ってきた! 標準はあるにはあるが癖の多いSQL #16 - FROM句のインラインビューのエイリアスにもクセがある(必須だったり、任意だったり)
帰ってきた! 標準はあるにはあるが癖の多いSQL #17 - ANY_VALUE() ってなかなかいいじゃん、癖無さそう!
帰ってきた! 標準はあるにはあるが癖の多いSQL #18 - t_alias と c_alias にも癖が出る
帰ってきた! 標準はあるにはあるが癖の多いSQL #19 - c_alias の癖(おまけ)
帰ってきた! 標準はあるにはあるが癖の多いSQL #20 - Table Value Constructer (TVC)
帰ってきた! 標準はあるにはあるが癖の多いSQL #21 - Table Value Constructer(TVC)- ハードパース時間とメモリ消費量 / BONUS TRACK

| | | コメント (0)

2026年1月 2日 (金)

クリスマスに届けられた「NewSQL徹底入門」読了

新年、最初のエントリは、珍しく、読書の話題からw

昨年、クリスマスに届けられた「NewSQL徹底入門」

G862fsdbsaa1tfx_20260102165901
正月のお楽しみに残しておいたのですが、駅伝往路も終わり、Netflixでストレンジャーシングスも観終り読み始めたら、
一気読みしてしまったw 

NewじゃないSQLerの方もNewSQL怖くない!、という程度にNewSQLの基本を把握できるような一冊ではないだろうか。

ただ、今のところNewSQLもいろいろと癖が強かったり、多いのも事実なので、
Multi-purpose databasesの一つとしての選択肢と考えるのもよいだろうし、
未来の? Converged Databaseかも?なんてワクワクしながら追いかけるのもよいだろうし。


データベース界隈は相変わらず、飽きないネタを提供し続けてくれて楽しいですね。ほんとに。

| | | コメント (0)