Oracle 19cでINSERTの方法を選ぶ
Oracle配下のINSERT教材です。1行の新規登録、既存集合のコピー、照合更新は別の用途です。後続のMySQL/PostgreSQL等の旧比較例は製品差を付けて保持しています。まずOracle 19c用の条件と手順を確認してください。
- 新規1行は列名付きINSERT VALUES。少数の固定行を1文で追加する場合はINSERT ALL、元データが表にあるならINSERT SELECTを検討します。どちらも既存行を自動更新する操作ではありません。
- 照合して更新/追加する要件ならMERGEを検討します。照合キーと制約は別なので、ON条件だけで一意性や並行書込みの競合処理まで保証しません。
- 保存先schema・列型・通常DEFAULT/identity・必須列・一意制約・外部キー・trigger、必要なINSERT/SELECT権限を確認します。既存表の制約を外すのではなく、投入値を要件と照合します。
- 専用接続で他の未確定作業がないことを確認し、clientの自動COMMITを把握します。Oracleは最初のDMLでtransactionを開始し、SQLのBEGINは不要です。SQL*PlusのSET AUTOCOMMIT、JDBC等の接続API、MySQLのSET autocommitは別の設定です。
専用教材表で2行を追加する
以下は未実行のOracle 19c構文例です。事前に管理された専用schemaの通常表w43_insert_demoがあり、列はid NUMBER主キー(自動採番なし)、label VARCHAR2(40)通常列、created_at DATE DEFAULT SYSDATE、余分な必須列・trigger・外部キーなしとします。作成SQLは実習へ混ぜません。id=43001/43002が未使用で他writerがない条件を先に確認してください。既存名・データへ流用しません。
INSERT ALL
INTO w43_insert_demo (id, label) VALUES (43001, 'first')
INTO w43_insert_demo (id, label) VALUES (43002, 'second')
SELECT 1 FROM DUAL;
SELECT id, label, created_at
FROM w43_insert_demo
WHERE id IN (43001, 43002)
ORDER BY id;
元SELECTが1行なので2つのINTOが各1回動き、条件通りなら2行追加します。元SELECTが複数行なら各INTOも繰り返されます。クライアントのDML結果件数と同一接続の上記2行・値を照合します。省略したcreated_atは定義済みDEFAULTを使います。通常列labelにDEFAULTがなければ省略/DEFAULTはNULLであり、必須値が自動生成されるわけではありません。
自動COMMITが無効で途中にDDL等の確定要因がなければ、この時点は未確定です。試行を戻すなら同一接続でROLLBACK、業務として保存するなら件数・値・要件の一致を確認してCOMMITを選びます。両方を順番に実行しません。COMMIT後の通常ROLLBACKは復元になりません。SQL*PlusはAUTOCOMMIT OFFでも既定の正常EXITで確定するため、接続を閉じる前に明示的に終了します。通常表/索引DDLは先行DMLを確定させ得るので途中へ追加しません。
単行の値をRETURNING INTOで受け取る
同じ教材表で未使用id=43003、SQL*Plusを使う場合の独立例です。VARIABLEはSQL*PlusコマンドでありDB共通SQLではありません。アプリでは対応driverの出力bindを使います。ここでは明示IDを返し、採番の成功実測ではありません。identity等で採番する表でも返却は可能ですが、その定義を別途確認します。
VARIABLE w43_id NUMBER
INSERT INTO w43_insert_demo (id, label)
VALUES (43003, 'single')
RETURNING id INTO :w43_id;
PRINT w43_id
受取値だけでなく行の内容と成功件数を照合してから確定/取消を選びます。INSERT ALLはmultitable insertの構文なのでRETURNINGを指定できません。複数行DMLの返却変数・配列対応をこの単行例から推測しないでください。
MERGEは照合条件と再送条件を先に決める
同じ教材表、id=43004をこの実習だけに予約し、他writerがない条件で使う独立例です。更新を許すのはlabelだけとします。Oracle 19cでは表aliasにASを付けず、定数SELECTにFROM DUAL、ONに括弧を使います。
MERGE INTO w43_insert_demo tgt
USING (SELECT 43004 AS id, 'merged' AS label FROM DUAL) src
ON (tgt.id = src.id)
WHEN MATCHED THEN
UPDATE SET tgt.label = src.label
WHEN NOT MATCHED THEN
INSERT (id, label) VALUES (src.id, src.label);
条件通りなら未存在で1行追加、既存ならlabelを更新します。更新側はcreated_atを書き換えません。同一target行をsourceの複数行で更新できず、ONに使う列も更新できません。sourceの重複・主キー以外の一意制約・並行要求を別途評価します。この代入は加算例とは異なりますが、triggerや外部副作用を含む処理全体の冪等性まで保証しません。重複違反やCOMMIT応答不明では業務キー・処理記録を照合し、同じ要求を闇雲に再送しません。
Oracle INSERT SELECTでは入力列数/型、日付境界、コピー元の重複を確認し、対象となる元SELECTと挿入結果を照合します。事前SELECTだけでは同時変更下の集合を固定できません。3つの新例・後続の旧例とも、Oracle/他DBでの実行、driver、競合、速度、障害復旧は未検証です。公式仕様との照合と実測は分けています。
| この記事の要点 |
|
INSERT 文の基本
INSERT 文は SQL の DML (Data Manipulation Language) のうち最も基本的な命令で、テーブルに新しい行を追加します。データの登録に使う中核 SQL です。
旧例1(基本形): usersの列・制約・未使用IDを確認する比較例です。Oracle 19cではNOW()を使わず、DATE列ならSYSDATE、TIMESTAMP列ならSYSTIMESTAMP等を型に合わせます。列名省略の例は表定義順・列数に依存します。通常の非生成列でDEFAULT未定義ならDEFAULTはNULLとなり、NOT NULLなら拒否されます。3文を続けて試す手順ではありません。未実行です。
-- 列名を明示(推奨)
INSERT INTO users (id, name, email)
VALUES (1, 'Alice', 'alice@example.com');
-- 列名省略(全列に値を指定、テーブル定義順)
INSERT INTO users VALUES (1, 'Alice', 'alice@example.com', NOW());
-- DEFAULT で既定値を使う
INSERT INTO users (id, name, created_at)
VALUES (2, 'Bob', DEFAULT);
列名を明示すると列順への依存を減らせます。ただしDEFAULTのない必須列が追加されれば既存INSERTも失敗し得ます。Oracleの通常の非生成列では、省略列は定義済みDEFAULT、未定義ならNULLです。明示NULLは通常DEFAULTを発動しません。DEFAULT ON NULLやidentity・triggerのある列は別途定義を確認してください。
複数行 INSERT(マルチバリュー)
複数行の一括投入は通信往復を減らせますが、速度は条件依存です。Oracle 19cの方法は序盤のINSERT ALL例を参照してください。
旧例2(マルチバリュー): MySQL/PostgreSQL等の比較で、Oracle 19cの構文ではありません。Oracle 23aiの公式教材に同形の拡張があっても19cへ転用できません。500〜1000行は固定の最適値ではなく、packet・パラメータ数・行サイズ・ロック・業務上の取消単位を考慮して計測します。未実行です。
INSERT INTO users (id, name, email) VALUES
(1, 'Alice', 'alice@example.com'),
(2, 'Bob', 'bob@example.com'),
(3, 'Carol', 'carol@example.com'),
(4, 'Dave', 'dave@example.com');
-- 大量データの場合は 500〜1000 行ずつに分割
-- (MySQL の max_allowed_packet、PostgreSQL のパラメータ数制限を考慮)
一括投入の倍率や最適行数は実測なしに決められません。分割ごとにCOMMITすると途中まで確定した行は全体ROLLBACKで戻りません。通信回数だけでなく、同じ投入結果・制約検査・取消境界も比較します。
INSERT ... SELECT
SELECT の結果を別テーブル(または同テーブル)へ追加できます。ETL等ではコピー元の条件・型・重複キーを先に確認します。SELECTが0行なら追加も0行です。
旧例3(SELECT挿入): コピー先の列数・型・制約、コピー元の読取権限が前提です。Oracle 19cではDATE(created_at)を日単位にまとめる関数として使いません。DATE列ならTRUNC(created_at)、日付境界はDATE '2024-01-01'やDATE '2026-06-01'等の型付きリテラルを使い、NLS依存の暗黙変換を避けます。TIMESTAMPや時差付き型は集計する日・タイムゾーンも決めます。自己コピーは再実行でも行を増やすため、履歴の重複防止を別に設計します。旧3文は独立例で未実行です。
-- 別テーブルからコピー
INSERT INTO users_archive (id, name, email, created_at)
SELECT id, name, email, created_at
FROM users
WHERE created_at < '2024-01-01';
-- 集計結果を保存
INSERT INTO daily_sales (date, total)
SELECT DATE(created_at), SUM(amount)
FROM orders
WHERE created_at >= '2026-06-01'
GROUP BY DATE(created_at);
-- 同テーブルへの自己コピー(履歴生成など)
INSERT INTO logs (level, message)
SELECT 'ERROR', message FROM logs WHERE level = 'WARN';
Upsert(あれば更新、無ければ挿入)
Upsertは「既存なら更新、無ければ挿入」という意図の呼称で、再試行の必須条件でも、冪等性の保証でもありません。Oracle MERGEはON条件による照合、MySQL/PGの構文は指定された重複・競合の扱いに従い、競合時の意味は同一ではありません。成功応答を失った場合も、業務要求ID・既存記録・確定状態を照合してから再試行を判断します。MySQLのREPLACEは重複する旧行の削除と新行の挿入という意味で、UPDATEの代用品ではありません。省略列のDEFAULT化、DELETE/INSERT triggerや外部キーへの影響を確認し、エラーを隠すために置換しません。
MySQL: ON DUPLICATE KEY UPDATE
旧例4: MySQL用です。user_idに対応する主キー/UNIQUEとpointの型・NULL方針が必要です。2文は代替例で、続けると再加算します。VALUES(point)は8.0.20以降非推奨で、下段の新行alias形式と区別してください。加算式は成功済み要求の再送でも加算するため冪等ではありません。重複や切断を理由に無条件再送しないでください。未実行です。
INSERT INTO user_points (user_id, point)
VALUES (1, 100)
ON DUPLICATE KEY UPDATE
point = point + VALUES(point); -- 既存にあれば加算
-- MySQL 8.0.20+ は VALUES() より new エイリアスが推奨
INSERT INTO user_points (user_id, point) VALUES (1, 100) AS new
ON DUPLICATE KEY UPDATE point = user_points.point + new.point;
PostgreSQL: ON CONFLICT
旧例5: PostgreSQL用です。ON CONFLICT(user_id)が推論できる適切な一意索引等が必要です。加算版とDO NOTHING版は代替で、加算版の再送は二重加算し得ます。DO NOTHINGも業務処理済みの証明ではないため既存行の意味を確認します。Oracleにこの構文を転用しません。未実行です。
INSERT INTO user_points (user_id, point)
VALUES (1, 100)
ON CONFLICT (user_id) DO UPDATE
SET point = user_points.point + EXCLUDED.point;
-- EXCLUDED は INSERT しようとした行を指す疑似テーブル
-- 何もしないバージョン(重複なら無視)
INSERT INTO user_points (user_id, point)
VALUES (1, 100)
ON CONFLICT (user_id) DO NOTHING;
SQL 標準: MERGE 文
旧例6: MERGE機能の比較資産であり、全製品共通の実行コードではありません。Oracle 19c用にはtarget/sourceの表alias直前のASを外し、SELECTのFROM DUALとON条件の括弧が必要です。序盤のOracle専用例と区別してください。sourceが同じtargetを複数回更新しない条件が必要で、MERGEという名前だけで同時挿入競合の成功や安全な再送は保証できません。この加算例も再送で二重加算し得ます。旧コードの各製品互換は未実行です。
-- SQL Server / Oracle / DB2 / PostgreSQL 15+ で利用可能
MERGE INTO user_points AS tgt
USING (SELECT 1 AS user_id, 100 AS point) AS src
ON tgt.user_id = src.user_id
WHEN MATCHED THEN
UPDATE SET point = tgt.point + src.point
WHEN NOT MATCHED THEN
INSERT (user_id, point) VALUES (src.user_id, src.point);
RETURNING で採番された ID を取得
旧例7: 冒頭のRETURNING id, created_atだけの結果セット形式はPostgreSQL/MariaDB向けで、Oracle 19cではINTOと宣言済み受取変数またはbindが必要です。OracleがRETURNING非対応という意味ではありません。序盤の単行例を参照し、INSERT ALLにはRETURNINGを付けません。MySQL LAST_INSERT_ID()は同一接続で対象の自動採番INSERT成功後に取得し、SQL Server OUTPUTとは構文を分けます。各表の採番/created_at定義が前提で、返却値取得はCOMMIT成功の証明ではありません。未実行です。
-- PostgreSQL / Oracle / MariaDB 10.5+
INSERT INTO users (name, email)
VALUES ('Alice', 'alice@example.com')
RETURNING id, created_at;
-- MySQL は LAST_INSERT_ID() を別途呼ぶ
INSERT INTO users (name, email) VALUES ('Alice', 'alice@example.com');
SELECT LAST_INSERT_ID();
-- SQL Server は OUTPUT
INSERT INTO users (name, email)
OUTPUT inserted.id, inserted.created_at
VALUES ('Alice', 'alice@example.com');
パフォーマンスのコツ
| シーン | 推奨 |
|---|---|
| 1 万行以下の単発投入 | 行サイズ・通信上限・取消単位からbatch量を計測。Oracle 19cはINSERT ALL/SELECT等を選び、1000行固定にしない |
| 10 万行以上のバルクロード | MySQL LOAD DATA INFILE / PostgreSQL COPY 等を製品別に検討。行数だけで移行を決めず、権限・入力検査・制約・取消範囲を確認 |
| 大量 INSERT 中に INDEX が遅い | まずSQL・投入方法・索引保守コストを測定。通常運用で索引DROPを自動推奨しない。一意性検査の喪失、他利用者への影響、再作成失敗を評価する。Oracleの通常索引DDLは先行DMLを確定させ得る。初期bulk loadで後から索引作成する設計は専用の移行計画として判断 |
| 外部キー制約が遅い | PostgreSQLでは現在transaction内のDEFERRABLEな対象制約だけ検査時点を遅延可能。NOT DEFERRABLE・CHECK・NOT NULLへ一律適用不可。検査をなくす機能でも高速化保証でもなく、通常のFKエラー対処に定義変更しない |
| 同時 INSERT で競合 | 競合キー・処理順・transaction長を調査。clientの確定設定と業務単位を合わせる。長いtransactionは競合を悪化させ得るため、OFFやbatch化だけで解決としない |
プレースホルダ(SQL インジェクション対策)
アプリからINSERTする値はプレースホルダにbindし、入力値をSQLへ連結しません。表名・列名は値bindでは置換できないため許可リスト等で選びます。
旧例8: 最初のexecは実行してはいけない危険例です。PDO接続・driver、Laravelのimport/接続、変数・列定義は省略されています。安全な値bindの考え方を示す比較で、PDOの?やLaravelのbulk出力をOracle 19cのdriver/grammarで検証した例ではありません。transactionとcommitはアプリの管理単位に従い、framework管理へ独自COMMITを混ぜません。未実行です。
// ❌ 危険: SQL インジェクション
$pdo->exec("INSERT INTO users (name) VALUES ('$name')");
// ✅ プレースホルダ
$stmt = $pdo->prepare('INSERT INTO users (name, email) VALUES (?, ?)');
$stmt->execute([$name, $email]);
// ✅ Laravel
DB::table('users')->insert(['name' => $name, 'email' => $email]);
// ✅ バルク INSERT
DB::table('users')->insert([
['name' => 'Alice', 'email' => 'alice@example.com'],
['name' => 'Bob', 'email' => 'bob@example.com'],
]);
よくあるトラブル
| 症状 | 原因 | 対処 |
|---|---|---|
Duplicate entry '...' for key 'PRIMARY' | 主キー重複 | MySQLのエラー文例。OracleのORA-00001等では対象制約・業務キー・既存行・処理済み記録を確認。無条件再送やUPSERTへの変更で隠さず、挿入/更新/拒否を業務要件で選ぶ |
Column count doesn't match value count | 列数と値数の不一致 | 明示した列順・値数・SELECTの列数/型を照合。エラー文の表記は製品で異なる |
Field 'xxx' doesn't have a default value | NOT NULL 列に値が無い | 必須値と既存DEFAULT/identity/trigger定義を確認。Oracleの通常非生成列でDEFAULT未定義ならNULLとなりNOT NULL違反。安易なDEFAULT追加やNULL許容化で回避しない |
Data too long for column 'xxx' | VARCHAR 長超過 | 入力の意味・列型/長さ・文字/byte単位を確認。黙った切捨てや自動DDL変更はしない |
| 大量 INSERT が遅い | 1 行ずつ実行 / インデックス過多 | 同一結果を保ち通信・実行計画・lock・索引保守を計測。Oracle 19cへ複数行VALUESを転用せず、索引/制約の解除を自動対処にしない |
FAQ
Q: INSERT IGNORE と INSERT ... ON DUPLICATE KEY UPDATE の違いは?
A: MySQLの比較です。INSERT IGNOREは重複行をskipするほか、一部不正値を調整して挿入しwarningを出す場合もあります。warningsと実投入結果を確認し、単なる無害な重複対策としません。ON DUPLICATE KEY UPDATEは重複時に更新します。Oracle 19cにこの構文を転用せず、REPLACEの削除+挿入とも区別します。
Q: AUTO_INCREMENT の値を予測できる?
A: 予測すべきでありません。LAST_INSERT_ID() / RETURNING / OUTPUT で挿入直後に取得するのが正解です。
Q: トランザクション内で INSERT してロールバックすると AUTO_INCREMENT は戻る?
A: 戻りません。MySQL InnoDB の場合、AUTO_INCREMENT カウンタは進んだまま。歯抜けの ID が発生するのは仕様です。
公式資料と対象版
2026-10-27確認。中心はOracle Database 19cです。次の資料は構文・条件の根拠であり、本記事SQLの実行証明ではありません。
- Oracle 19c INSERT: 列省略/DEFAULT、SELECT/ALL、RETURNING INTOとmultitable制限、必要権限。
- Oracle 19c VALUES構文: 単一行の値リスト。Oracle 23ai公式教材のmultivalue拡張とは版を区別。
- Oracle 19c CREATE TABLE: 通常DEFAULT・DEFAULT ON NULL・identity定義。
- Oracle 19c TRUNC(date)とLiterals: 日単位の集計・型付き日付境界。SQL*Plus VARIABLE: NUMBERの受取bind宣言。
- Oracle 19c MERGEと構文テキスト: alias、ON、更新制限。
- Oracle 19c COMMIT: 確定、DDL境界、成功応答を失った場合。SQL*Plus 19c SET: AUTOCOMMITとEXITCOMMIT。
- MySQL 8.4 INSERTとON DUPLICATE KEY UPDATE: IGNOREのwarning/調整挿入、VALUES()非推奨、新行alias。
- MySQL 8.4 REPLACE: 削除+挿入の意味、省略列と既存値。
- MariaDB INSERT RETURNING: 結果セット形式。PHP PDO prepared statements: 値bindとSQL文字列連結の区別。
- PostgreSQL 18 INSERT: ON CONFLICTと返却形式。SET CONSTRAINTS: 現transaction・DEFERRABLE限定。初期bulk loadの公式説明: 索引後作成は条件付き移行計画であり通常運用の自動DROP推奨ではない。
子ページはありません
人気ページ
- 1 Eclipseで「サーバーに追加または除去できるリソースがありません。」の原因と対処法
- 2 tomcat の起動 / 停止ログと catalina.log・catalina.out の違い
- 3 JavaScript で base URL を取得する方法|window.location.origin
- 4 YouTube Data API v3 エラー一覧|403・400・404 の原因と対処
- 5 Laravel エラー一覧|500/Blade/DB 接続/ルーティングの代表エラー
- 6 3Dグラフィックスのソフト一覧|Blender・Metasequoiaと用途別の選び方
- 7 Spring Frameworkのアノテーション一覧
- 8 【Spring】@Valueアノテーションとは
- 9 CATALINA_HOME の確認方法 (Linux / Mac)
- 10 【Spring】@Autowiredアノテーションとは
最近更新/作成されたページ
- Visual Studio「デザイナーを表示できません」エラーの原因と対処 2026-10-28 12:06:00
- SSH 認証に失敗しました。再試行してください の対処 2026-10-28 12:06:00
- SSH2 秘密鍵の読み込みに失敗しました の原因と対処 2026-10-28 12:06:00
- TeraTerm のよくあるエラーと対処法完全ガイド — Connection refused / 2026-10-28 12:06:00
- Visual Studio エラー一覧|コンパイルエラー・警告の確認方法と対処 2026-10-28 11:39:57
- Whitelabel Error Page の原因と消し方|Spring Boot のエラーページ 2026-10-28 11:39:57
- Eclipseで「サーバーに追加または除去できるリソースがありません。」の原因と対処法 2026-10-28 11:39:57
- SQL WHERE 句完全ガイド 2026-10-28 11:39:57
- SQL JOIN 完全ガイド (INNER/LEFT/RIGHT/FULL OUTER) 2026-10-28 11:39:57
- tomcat の起動 / 停止ログと catalina.log・catalina.out の違い 2026-10-28 10:24:23
- Tomcat の起動確認方法 5 つ|localhost:8080・ポート・プロセス・ログ 2026-10-28 10:24:23
- Oracle 19c COMMITの使い方: 暗黙コミット・自動確定・応答不明時の確認 2026-10-28 09:59:09
- Oracle 19c DELETE文|削除対象の確認・COMMIT前の判断・ROLLBACKの条件 2026-10-28 09:59:09
- Oracle 19c INSERT文|INSERT ALL・SELECT・RETURNINGとMERGEの使い分け 2026-10-28 09:59:09
- Oracle 19cのトランザクション制御|COMMIT・ROLLBACK・分離レベルと接続設定 2026-10-28 09:59:09