初心者向け:SQL ServerでINSERT/UPDATE/Delete時の戻り値を取得するOUTPUT句入門

データベース操作を行う際、INSERTやUPDATE、DELETEを実行した後に、その操作で影響を受けた行の情報(主キーや更新前後の値など)を取得したい場面がよくあります。

PostgreSQLのRETURNING句に相当する機能として、SQL ServerではOUTPUT句が用意されています。

本記事では、初心者向けにSQL ServerのOUTPUT句の基本的な使い方を解説します。最後には理解度チェック用の演習問題とその解答例を用意しましたので、ぜひ挑戦してみてください。


OUTPUT句とは何か?

OUTPUT句は、INSERT、UPDATE、DELETE、MERGEといったDML(Data Manipulation Language)文の実行結果として影響を受けた行の情報を返すためのSQL Server固有の拡張機能です。

  • INSERT時:挿入された行の値を取得
  • UPDATE時:更新前の値(deleted)および更新後の値(inserted)を取得
  • DELETE時:削除前の値(deleted)を取得
  • MERGE時:挿入・更新・削除それぞれの行情報を取得

OUTPUT句の基本構文

-- INSERTの例
INSERT INTO テーブル名 (列1, 列2, ...)
OUTPUT inserted.列A, inserted.列B, ...
VALUES (値1, 値2, ...);

-- UPDATEの例
UPDATE テーブル名
SET 列1 = 新しい値1, ...
OUTPUT deleted.列A AS oldA, inserted.列A AS newA, ...
WHERE 条件;

-- DELETEの例
DELETE FROM テーブル名
OUTPUT deleted.列A, deleted.列B, ...
WHERE 条件;
  • inserted:INSERTまたはUPDATE後の行データを参照
  • deleted:DELETEまたはUPDATE前の行データを参照

INSERT時のOUTPUT句

単純なINSERTと戻り値取得

従来、INSERT後に主キーを取得するにはSCOPE_IDENTITY()や@@IDENTITYを使っていましたが、複数行INSERT時には対応できません。

OUTPUT句を使えば、一度のINSERTで挿入された全行の主キーをまとめて取得できます。

-- サンプルテーブル作成
CREATE TABLE Employees (
    EmployeeID INT IDENTITY(1,1) PRIMARY KEY,
    Name NVARCHAR(50),
    Department NVARCHAR(50)
);

-- 複数行INSERTして主キーを取得
INSERT INTO Employees (Name, Department)
OUTPUT inserted.EmployeeID, inserted.Name
VALUES
    (N'山田太郎', N'営業部'),
    (N'佐藤花子', N'総務部');

実行結果として、挿入された2行分のEmployeeIDとNameが返されます。


UPDATE時のOUTPUT句

UPDATE前後の値を同時に取得できるので、変更履歴の保存や処理結果の検証に便利です。

-- サンプルデータ
INSERT INTO Employees (Name, Department) VALUES (N'田中一郎', N'企画部');

-- 部署を「営業部」へ変更し、変更前後を取得
UPDATE Employees
SET Department = N'営業部'
OUTPUT
    deleted.EmployeeID    AS EmployeeID,
    deleted.Department    AS OldDept,
    inserted.Department   AS NewDept
WHERE Name = N'田中一郎';
実行結果
EmployeeIDOldDeptNewDept
3企画部営業部

DELETE時のOUTPUT句

削除前のデータを取得し、別テーブルへアーカイブしたりログに残したりできます。

-- 削除前の情報を取得しながらDELETE
DELETE FROM Employees
OUTPUT deleted.EmployeeID, deleted.Name, deleted.Department
WHERE EmployeeID = 2;

MERGE文とOUTPUT句

MERGE文でINSERT/UPDATE/DELETEを組み合わせた複雑なバッチ処理を行う際にもOUTPUT句が使えます。

MERGE INTO TargetTable AS T
USING SourceTable AS S
    ON T.Key = S.Key
WHEN MATCHED THEN
    UPDATE SET T.Value = S.Value
WHEN NOT MATCHED BY TARGET THEN
    INSERT (Key, Value) VALUES (S.Key, S.Value)
WHEN NOT MATCHED BY SOURCE THEN
    DELETE
OUTPUT
    $action AS ActionType,
    inserted.Key, inserted.Value, deleted.Value;

$action:INSERT/UPDATE/DELETEのいずれかの文字列が返る。


実践的な使いどころ

  1. バッチ処理での一括操作ログ記録
    更新前後の値を別テーブルにINSERTして変更履歴を管理。
  2. トリガー不要でのキー取得
    複数行INSERT時のIDリストをアプリケーション側に返す。
  3. エラーチェックや監査
    DELETE前のデータをログテーブルへ保管し、誤操作時に復元可能にする。

まとめ

  • SQL ServerではPostgreSQLのRETURNING句に相当する機能としてOUTPUT句を使用する。
  • INSERT/UPDATE/DELETEすべてのDML文で利用可能。
  • insertedとdeleted仮想テーブルで操作前後の行情報を参照できる。
  • 一度のSQL実行で影響を受けた複数行の情報をまとめて取得でき、トリガーや追加クエリが不要になる。

ぜひ記事の内容を参考に、実際の開発現場や学習環境でOUTPUT句を試してみてください。


演習問題

問題1

Productsテーブル(ProductID:IDENTITY、Name:NVARCHAR、Price:INT)に、以下の3商品のデータをINSERTし、挿入されたProductIDとNameを取得するSQLを書きなさい。

  • “ノートPC”、120000
  • “スマートフォン”、80000
  • “タブレット”、60000

問題2

Employeesテーブル(EmployeeID、Name、Salary)で、給与を10%アップしたい。

Name = ‘佐藤花子’の行をUPDATEし、変更前後のSalaryを両方取得するSQLを書きなさい。

問題3

Ordersテーブル(OrderID、CustomerID、OrderDate)から、OrderDate < ‘2025-01-01’の注文を削除し、削除した行のOrderIDとCustomerIDを取得するSQLを書きなさい。

問題4

Inventoryテーブル(ItemID、StockCount)について、MERGE文を使って以下のように処理するSQLを書きなさい。

  • NewStockという一時テーブル(ItemID、StockCount)をソースとし、
  • Inventoryに存在する行はStockCountを上書き、
  • 存在しない行はINSERT、
  • NewStockに含まれない既存行はDELETE
  • 各行に対し、操作種別(INSERT/UPDATE/DELETE)と、ItemID、StockCount(更新後または削除前)を取得する。

解答例

解答1
INSERT INTO Products (Name, Price)
OUTPUT inserted.ProductID, inserted.Name
VALUES
    (N'ノートPC', 120000),
    (N'スマートフォン', 80000),
    (N'タブレット', 60000);
解答2
UPDATE Employees
SET Salary = Salary * 1.1
OUTPUT
    deleted.Salary   AS OldSalary,
    inserted.Salary  AS NewSalary
WHERE Name = N'佐藤花子';
解答3
DELETE FROM Orders
OUTPUT deleted.OrderID, deleted.CustomerID
WHERE OrderDate < '2025-01-01';
解答4
MERGE INTO Inventory AS T
USING NewStock AS S
    ON T.ItemID = S.ItemID
WHEN MATCHED THEN
    UPDATE SET T.StockCount = S.StockCount
WHEN NOT MATCHED BY TARGET THEN
    INSERT (ItemID, StockCount) VALUES (S.ItemID, S.StockCount)
WHEN NOT MATCHED BY SOURCE THEN
    DELETE
OUTPUT
    $action              AS ActionType,
    COALESCE(inserted.ItemID, deleted.ItemID)    AS ItemID,
    COALESCE(inserted.StockCount, deleted.StockCount) AS StockCount;

以上で、SQL ServerのOUTPUT句(いわゆる「戻り値を取得する句」)の基本的な使い方と演習問題の解答例を紹介しました。ぜひ手を動かして理解を深めてください!