更新した行を返す - RETURNING / OUTPUT
INSERT/UPDATE/DELETE の影響行を返す。PostgreSQL RETURNING、SQL Server OUTPUT、MySQL 非対応
概念図
構文
sql
INSERT ... RETURNING col1, col2;サンプル
追加で SELECT せず、INSERT と同時に生成値を取得する
sql
-- PostgreSQL: 挿入した行の id と created_at を取得
INSERT INTO users (name, email)
VALUES ('Alice', 'alice@example.com')
RETURNING id, created_at;RETURNING とは
RETURNING は INSERT / UPDATE / DELETE 文で、影響を受けた行の値を SELECT 相当で返す機能です。採番された id、デフォルト値の created_at、更新前後の比較などを 追加クエリなしで 取得できます。
レースコンディションに強く、last_insert_id() のような副作用 API への依存を減らせる点でもメリットが大きいです。
PostgreSQL の RETURNING
PostgreSQL はすべての DML(INSERT / UPDATE / DELETE / MERGE)で RETURNING をサポートします。RETURNING * で全列、特定列だけでも指定可能。
SQLite も 3.35 以降で RETURNING をサポート。MariaDB も INSERT / DELETE で対応、UPDATE は 10.5 以降で部分対応。
sql
-- 更新と同時に更新後の値を取得
UPDATE orders
SET status = 'shipped', shipped_at = CURRENT_TIMESTAMP
WHERE id = 100
RETURNING id, status, shipped_at;
-- 削除した行をログに残す
DELETE FROM sessions
WHERE expires_at < CURRENT_TIMESTAMP
RETURNING id, user_id;SQL Server の OUTPUT
SQL Server は OUTPUT 句で同等の機能を提供します。INSERTED と DELETED の 2 つの擬似テーブルを参照でき、UPDATE では 両方を同時に参照できるので「更新前後の差分」を 1 文で取得できます。
sql
-- SQL Server: 更新前後の値をログに残す
UPDATE orders
SET status = 'shipped'
OUTPUT
DELETED.id,
DELETED.status AS old_status,
INSERTED.status AS new_status
WHERE id = 100;
-- OUTPUT INTO で監査テーブルに直接書き込み
DELETE FROM users
OUTPUT DELETED.* INTO users_archive
WHERE status = 'deleted';MySQL は非対応
MySQL は 2026 年時点でも RETURNING / OUTPUT に相当する構文を持ちません。代替策として次のような手段があります。
- INSERT の採番 id:
LAST_INSERT_ID()関数、またはドライバの返却値(lastInsertId/AUTO_INCREMENT) - UPDATE / DELETE 後の値: 直前に
SELECT ... FOR UPDATEで値を取得し、トランザクション内で書き換える - MariaDB を選ぶ: MariaDB は INSERT / DELETE / UPDATE (10.5+) で RETURNING をサポート
落とし穴: トリガと制約の順序
- BEFORE トリガが値を書き換えた場合: RETURNING / INSERTED が返すのはトリガ適用後の値。「クライアントが送った値」ではない点に注意
- AFTER トリガの失敗: RETURNING が返ってきても、AFTER トリガで例外が出ればトランザクションはロールバックされる
- OUTPUT INTO と FK: SQL Server の
OUTPUT INTOは、ターゲットテーブルに外部キー・トリガ・チェック制約があると使えない制限がある
