「この商品だけ消したい」「在庫がない商品をまとめて消したい」。そんなときに使うのがSQLのDELETE文です。DELETE FROMでテーブルを指定し、WHEREで削除する行を絞り込みます。

まず気をつけたいこと

WHEREを省略すると、テーブル内の全行が削除対象になります。 いきなりDELETEを実行せず、同じ条件のSELECTで対象を確認しましょう。この記事の実行例はSQL Server LocalDB+SSMS向けです。

DELETE文の基本構文:消えるのは「行」

DELETE FROM テーブル名
WHERE 削除する条件;

例えばProductIdが3の商品を指定すると、その行の商品名・価格・在庫も一緒に消えます。「在庫だけを0にする」操作ではありません。 値を変更したい場合はUPDATE文の書き方を確認してください。

図解:IDが3の「行」を取り除く
削除前|5件
1 ノートパソコン
2 マウス
3 キーボード ← 対象
4 モニター
5 USBメモリ
削除直後|4件
1 ノートパソコン
2 マウス
4 モニター
5 USBメモリ

テーブルの列や構造は残る

条件は ProductId = 3。ほかの4行は残ります。架空の商品データを使った説明図で、実際のSSMS画面ではありません。

条件に一致する行が複数あれば、まとめて削除されます。逆に一致する行がなければ0件です。1件だけ消したいときは、今回のProductIdのような主キーなど、一意に決まる値を使うと対象を確認しやすくなります。

SQL Server+SSMSで練習用データを準備する

Windows 11、SQL Server LocalDB、SQL Server Management Studio(SSMS)を使う前提です。接続先は (localdb)\MSSQLLocalDB。まだ準備できていない方は、LocalDBとSSMSの接続手順から進めてください。C#からSQLを実行したい方も、まずSSMSで処理を確認すると追いやすくなります。

業務用のデータでは試さない

以下は新しく作る練習用DBだけで実行します。DeleteDBがすでにある場合は、用途を確認せずに使ったり削除したりしないでください。別名で作るなら、後のUSEに書く名前もすべてそろえます。

1.練習用のDeleteDBを作る

SSMSで「新しいクエリ」を開き、次の1文を実行します。すでに同じ名前のDBがあるというエラーが出たら、ここで止めて別名を決めてください。

CREATE DATABASE DeleteDB;

左側の一覧に表示されないときは、「データベース」を右クリックして「最新の情報に更新」を選びます。続いて、新しく作ったDeleteDBを選びます。

USE DeleteDB;
SELECT DB_NAME() AS CurrentDatabase;

結果がDeleteDBであることを確認してください。クエリエディター上部のDB選択欄も確認しておきましょう。

2.Productsテーブルに5件の商品を登録する

次は初回に1回だけ実行します。すでにProductsがある・主キーが重複していると表示されたら、繰り返し実行せず、登録済みのデータを確認してください。

USE DeleteDB;

CREATE TABLE dbo.Products (
    ProductId INT PRIMARY KEY,
    ProductName NVARCHAR(100) NOT NULL,
    Price INT NOT NULL,
    Stock INT NOT NULL
);

INSERT INTO dbo.Products
    (ProductId, ProductName, Price, Stock)
VALUES
    (1, N'ノートパソコン', 120000, 10),
    (2, N'マウス', 3000, 50),
    (3, N'キーボード', 8000, 30),
    (4, N'モニター', 35000, 15),
    (5, N'USBメモリ', 2000, 0);

SELECT ProductId, ProductName, Price, Stock
FROM dbo.Products
ORDER BY ProductId;

dboはテーブルが属するスキーマ名です。ProductIdは商品を区別するID、Priceは価格、Stockは在庫数。商品名はNVARCHAR型にし、日本語の文字列にNを付けています。

ID商品名価格(円)在庫
1ノートパソコン12000010
2マウス300050
3キーボード800030
4モニター3500015
5USBメモリ20000

この5件が表示されれば準備完了です。以下の練習では、最後にROLLBACKして5件の状態に戻します。例ごとにデータを入れ直す必要はありません。

SSMS画像では、結果欄の商品と件数を確認してください。SQLを実行するときは、本文のコードを使ってください。

SSMSでProductsテーブルの初期データ5件を表示した結果
初期データの5件。ID・商品名・価格・在庫を確認します。

1件削除して、ROLLBACKで元に戻してみる

1.同じWHERE条件のSELECTで対象を確認

USE DeleteDB;

SELECT ProductId, ProductName, Price, Stock
FROM dbo.Products
WHERE ProductId = 3;

IDが3のキーボードが1件だけ出ることを確認します。想定と違う行が出たら、まだDELETEには進みません。SELECTの書き方はSELECT文の基本でも説明しています。

ProductIdが3のキーボード1件をSELECTで確認した結果
削除前の確認。ProductIdが3のキーボードだけが対象です。

2.削除・件数確認・取り消しをひと続きで実行

練習専用のクエリウィンドウで、下のブロック全体を実行してください。BEGIN TRANSACTIONからROLLBACKまでを同じ接続で実行するのがポイントです。他の作業のトランザクションが開いたウィンドウでは試さないでください。

USE DeleteDB;

BEGIN TRANSACTION;

DELETE FROM dbo.Products
WHERE ProductId = 3;

-- DELETEのすぐ後に件数を読む
SELECT @@ROWCOUNT AS DeletedCount;

-- 削除直後:ID 3が消えて4件になる
SELECT ProductId, ProductName, Price, Stock
FROM dbo.Products
ORDER BY ProductId;

-- 練習なので削除を取り消す
ROLLBACK TRANSACTION;

-- 取り消し後:ID 3も含めた5件に戻る
SELECT ProductId, ProductName, Price, Stock
FROM dbo.Products
ORDER BY ProductId;
結果の順番確認すること
① DeletedCount1(削除した行数)
② 削除直後の一覧ID 1・2・4・5の4件
③ ROLLBACK後の一覧ID 1〜5の5件。キーボードも戻る

@@ROWCOUNTは、直前の文で処理した行数です。別のSELECTやCOMMITなどを挟むと値が変わるため、DELETEの直後に読みます。この例では、3つの結果を順に見比べられます。

ID3を削除し、ID1・2・4・5の4件が残ったSSMSの結果
削除直後は4件。ID3のキーボードが一覧から消えています。
ROLLBACKでキーボードが戻り、5件になったSSMSの結果
ROLLBACK後は5件。ID3のキーボードも元に戻っています。
途中でエラーが出たら

続きを繰り返さず、エラー内容と接続先を確認します。この練習で開始したトランザクションが残っている場合は、同じ練習専用の接続でROLLBACKして終了してください。開いたまま放置すると、ほかの処理を待たせる原因になります。

WHEREの条件を変える:数値・文字列・AND・OR・IN

使い方に慣れたら、先ほどの事前確認SELECTと、練習ブロックのDELETEのWHEREを同じ条件に差し替えてみましょう。最後のROLLBACKは残してください。下の件数は、毎回5件の初期データから始めた場合の値です。

消したい商品WHEREに書く条件対象ID/件数
価格が5000円未満Price < 50002・5/2件
商品名がモニターProductName = N’モニター’4/1件
1万円未満かつ在庫0Price < 10000 AND Stock = 05/1件
IDが2または4ProductId = 2 OR ProductId = 42・4/2件
同じ列の値を複数指定ProductId IN (2, 4)2・4/2件

「未満」は <、「以下」は <= です。5000円ちょうどを含めたいかどうかで使い分けます。商品名で絞るときはシングルクォートで囲み、日本語にはNを付けます。同じ商品名が複数あれば、複数行が対象になる点にも注意してください。

複数IDを指定する例:INを使う

USE DeleteDB;

-- まずこのSELECTだけを実行して対象を確認
SELECT ProductId, ProductName, Price, Stock
FROM dbo.Products
WHERE ProductId IN (2, 4);

-- 対象が正しければ、以下をまとめて実行
BEGIN TRANSACTION;
DELETE FROM dbo.Products
WHERE ProductId IN (2, 4);
SELECT @@ROWCOUNT AS DeletedCount;
ROLLBACK TRANSACTION;

マウスとモニターの2件が対象です。INは「同じ列が、この値のどれかに一致する」という条件を短く書けます。ORの例とINの例を両方続けて削除する必要はありません。

ANDとORを混ぜるときは括弧でまとめる

SQL ServerではANDがORより先に評価されます。「IDが2または4で、さらに在庫0」という条件なら、次のように括弧を付けます。

SELECT ProductId, ProductName, Price, Stock
FROM dbo.Products
WHERE (ProductId = 2 OR ProductId = 4)
  AND Stock = 0;

今回の5件では対象は0件です。括弧なしではIDが2の行が在庫数に関係なく対象になるため、意味が変わります。条件が複雑になったときほど、まずSELECTで確かめましょう。

全件削除・TRUNCATE・DROPは何が違う?

WHEREなしのDELETEは全件削除です。エラーにならず実行できてしまうので、「WHEREを書き忘れたら止まる」とは考えないでください。違いを先に整理します。

命令消すものテーブルの構造
DELETE+WHERE条件に合う行残る
DELETE(WHEREなし)全行残る
TRUNCATE TABLE通常は全行残る
DROP TABLEテーブルそのもの残らない

全件削除の動きを試す場合も、練習用DeleteDBだけで、次のブロック全体を実行します。結果が一度0件になり、最後はROLLBACKで5件に戻ります。

USE DeleteDB;

BEGIN TRANSACTION;
DELETE FROM dbo.Products;
SELECT COUNT(*) AS RowsAfterDelete FROM dbo.Products;
ROLLBACK TRANSACTION;
SELECT COUNT(*) AS RowsAfterRollback FROM dbo.Products;

SQL ServerのTRUNCATEとの違い

  • TRUNCATEにはWHEREを付けられません。条件付きの削除はDELETEを使います。
  • IDENTITYによる自動採番は、DELETEではリセットされず、テーブル全体のTRUNCATEではシード値に戻ります。今回のProductsはIDENTITYを使っていません。
  • TRUNCATEはDELETEトリガーを起動しません。外部キーから参照されるテーブルなど、使えない条件もあります。
  • SQL Serverでは、明示的なトランザクション内のTRUNCATEも、確定前ならROLLBACKできます。「TRUNCATEは必ず取り消せない」と覚えないようにしましょう。

ここはDB製品で挙動が違います。この記事ではSQL Serverに限定しています。DROPも含め、単に速そうだからという理由で置き換えず、消す範囲・制約・トリガーを確認してください。詳しい条件はMicrosoftのTRUNCATE TABLE公式資料で確認できます。

削除を確定する前に知っておきたいこと

図解:確認してから、確定するか戻すかを選ぶ
① 対象を確認同じWHEREのSELECTで
商品・ID・件数を見る
② 削除して確認BEGIN TRANSACTION
→ DELETE
→ 件数・残った行を確認
③ 終了する練習・想定外 → ROLLBACK
意図どおり確定 → COMMIT
この記事の練習はROLLBACKまで実行
各例の最後に5件へ戻すので、次の例も同じデータから試せます。
学習用の流れです。本番の同時更新・制約・復旧手順まで保証するものではありません。

BEGIN TRANSACTIONで始めた変更は、COMMITで確定し、ROLLBACKで取り消します。ただし、確定済みの削除に後からROLLBACKを書いても戻せません。 自動コミットの接続でDELETEだけを実行した場合も、通常はその文の完了時に確定します。

本当に削除を確定する作業では、確認後にCOMMITを選びます。今回の練習コードを機械的にCOMMITへ置き換えて進める必要はありません。仕組みを詳しく知りたい方は、ROLLBACKの使い方とCOMMITの使い方へ進んでください。

SELECTで確認すれば絶対に安全、ではない

本番では、確認してから削除する間に別の処理がデータを変えることもあります。対象の固定方法や同時実行、バックアップからの復旧、関連テーブルへの影響を含め、運用手順を決めてから作業してください。

DELETEで削除できない・結果が違うとき

症状まず確認すること
削除件数が0同じWHEREのSELECTで、対象が残っているか確認。前の練習をROLLBACKせず確定していないかも見る。
無効なオブジェクト名接続先DBとdbo.Productsの名前を確認。別のDBで実行していないかを見る。
文字列で一致しない実際の登録値、空白、全角・半角、照合順序を確認。比較の扱いは照合順序で変わる。
権限エラー必要なDELETE・SELECT権限を管理者に確認。権限をむやみに広げない。
処理が終わらない大量削除や、ほかのトランザクションによる待機を確認。原因不明のまま再実行しない。
ROLLBACKしても戻らない同じ接続か、すでに確定していないかを確認。確定後の復旧はバックアップなど別の手段が必要。

外部キー制約エラーは、関連データを確認する

例えば注文明細が商品を参照していると、商品だけを消せずにエラーになることがあります。まず親子関係と削除ルールを確認してください。エラーを消すためだけに外部キーを無効化するのは避けましょう。

ON DELETE CASCADEが設定されている場合は、親の削除に伴って関連する子の行も削除されます。画面で選んだ商品の件数だけで影響を判断せず、連鎖する削除やトリガーも確認する必要があります。今回の練習用Productsには、外部キーやトリガーは作っていません。

DELETEに「*」や列名は書かない

SQL Serverでは、SELECT *の感覚でDELETE *とは書きません。行を消す基本形はDELETE FROMです。列の値だけを変えるならUPDATE、列自体をなくすならALTER TABLEなど、目的が別になります。今回のStockはNOT NULLなので、NULLに更新することもできません。

まとめ:消す前に対象を見て、練習では元に戻す

DELETEは、WHEREに一致する行を削除するSQLです。最初は「SELECTで対象を見る → トランザクション内で削除 → 件数と残った行を見る → ROLLBACKで戻す」の順で試すと、どこで何が変わるかを追いやすくなります。

商品の追加も試したい方はINSERT文の使い方へ。行を消す操作と、値を変えるUPDATEを使い分けていきましょう。

参考資料