この章で得られるスキル:
- ✅ INSERT文でデータを追加できる
- ✅ 外部キー制約を考慮してデータを追加できる
- ✅ UPDATE文で条件を指定してデータを更新できる
- ✅ DELETE文で条件を指定してデータを削除できる
- ✅ トランザクションの必要性を説明できる
- ✅ COMMITとROLLBACKの役割を説明できる
Step 0: まず体験してみよう
シナリオ:銀行の送金処理
AさんからBさんへ1万円を送金する処理を考えてみよう。
この処理では、以下の2つの操作が必要である。
- Aさんの口座から1万円を引き落とす
- Bさんの口座に1万円を入金する
-- 銀行口座テーブル
CREATE TABLE accounts (
account_id INTEGER PRIMARY KEY,
owner_name VARCHAR(50) NOT NULL,
balance INTEGER CHECK (balance >= 0)
);
-- 初期データ:Aさん5万円、Bさん3万円
INSERT INTO accounts VALUES (1, 'Aさん', 50000);
INSERT INTO accounts VALUES (2, 'Bさん', 30000);
-- 送金前の残高を確認
SELECT * FROM accounts;
-- ① Aさんの口座から1万円を引き落とす
UPDATE accounts SET balance = balance - 10000 WHERE account_id = 1;
-- ② Bさんの口座に1万円を入金する
UPDATE accounts SET balance = balance + 10000 WHERE account_id = 2;
-- 送金後の残高を確認
SELECT * FROM accounts;
もし①と②の間でエラーが起きたら?
上の例ではうまくいったが、もし①の引き落としだけ成功して、②の入金が失敗したらどうなるだろうか。
- Aさんの残高:50,000 → 40,000(引き落とし済み)
- Bさんの残高:30,000 → 30,000(入金されていない)
- 1万円が消えてしまう!
複数の操作を「全部成功」か「全部失敗」にする仕組みが トランザクション である。
これについてはStep 7で学ぶ。まずはINSERT、UPDATE、DELETEの基本を理解しよう。
Step 1: データの追加(INSERT)
INSERT文の基本構文
テーブルにデータを追加するには、 INSERT INTO 文を使う。
INSERT INTO テーブル名 VALUES (値1, 値2, 値3);
INSERT INTO テーブル名 (列名1, 列名2) VALUES (値1, 値2);
-- テーブル作成
CREATE TABLE departments (
dept_id INTEGER PRIMARY KEY,
dept_name VARCHAR(50) NOT NULL UNIQUE,
location VARCHAR(50)
);
-- 方法1: 全列を指定して追加
INSERT INTO departments VALUES (1, '営業部', '東京');
INSERT INTO departments VALUES (2, '開発部', '大阪');
-- 方法2: 列名を指定して追加(locationを省略 → NULLになる)
INSERT INTO departments (dept_id, dept_name) VALUES (3, '人事部');
-- 確認
SELECT * FROM departments;
全列指定と一部列指定の違い
| 方法 | 書き方 | 特徴 |
|---|
| 全列指定 | VALUES (値1, 値2, 値3) | テーブル定義の列順に全ての値を指定する |
| 一部列指定 | (列名1, 列名2) VALUES (値1, 値2) | 指定した列だけ値を設定し、残りはNULL(またはデフォルト値) |
実務では 列名を明示する方法 が推奨される。
全列指定はテーブルの列順に依存するため、テーブル構造が変更されると動かなくなるリスクがある。
Step 2: 複数行の追加とNULLの扱い
複数行を一度に追加する
1つのINSERT文で複数行のデータを追加することもできる。
CREATE TABLE departments (
dept_id INTEGER PRIMARY KEY,
dept_name VARCHAR(50) NOT NULL UNIQUE,
location VARCHAR(50)
);
-- 複数行を一度に追加
INSERT INTO departments VALUES
(1, '営業部', '東京'),
(2, '開発部', '大阪'),
(3, '人事部', '東京');
SELECT * FROM departments;
NULLの扱い
NOT NULL制約がない列には、NULLを明示的に入れることもできる。
INSERT INTO departments VALUES (4, '広報部', NULL);
INSERT INTO departments (dept_id, dept_name) VALUES (5, '経理部');
Step 3: 外部キーがあるテーブルへの追加
参照整合性エラー
外部キー制約があるテーブルにデータを追加する場合、 参照先のテーブルに該当するデータが存在しなければならない 。
-- 部門テーブル
CREATE TABLE departments (
dept_id INTEGER PRIMARY KEY,
dept_name VARCHAR(50) NOT NULL UNIQUE,
location VARCHAR(50)
);
-- 社員テーブル(dept_idが外部キー)
CREATE TABLE employees (
emp_id INTEGER PRIMARY KEY,
emp_name VARCHAR(50) NOT NULL,
dept_id INTEGER REFERENCES departments(dept_id),
salary INTEGER CHECK (salary > 0)
);
-- 先に部門を登録
INSERT INTO departments VALUES (1, '営業部', '東京');
INSERT INTO departments VALUES (2, '開発部', '大阪');
-- 存在する部門(dept_id = 1)で社員を登録 → 成功
INSERT INTO employees VALUES (1, '田中太郎', 1, 350000);
-- 存在しない部門(dept_id = 99)で社員を登録 → エラー!
INSERT INTO employees VALUES (2, '佐藤花子', 99, 400000);
正しい登録順序
| 順序 | 操作 | 理由 |
|---|
| 1. 親テーブル | departments にデータを追加 | 参照される側を先に作る |
| 2. 子テーブル | employees にデータを追加 | 参照先が存在していれば追加できる |
外部キーがあるテーブルへのINSERTでエラーが出たら、 参照先のテーブルにデータが存在するか を確認しよう。
これはよくあるミスの1つである。
Step 4: データの更新(UPDATE)
UPDATE文の基本構文
テーブルのデータを更新するには、 UPDATE 文を使う。
UPDATE テーブル名 SET 列名 = 新しい値 WHERE 条件;
-- テーブル作成とデータ登録
CREATE TABLE employees (
emp_id INTEGER PRIMARY KEY,
emp_name VARCHAR(50) NOT NULL,
salary INTEGER CHECK (salary > 0)
);
INSERT INTO employees VALUES (1, '田中太郎', 350000);
INSERT INTO employees VALUES (2, '佐藤花子', 400000);
INSERT INTO employees VALUES (3, '鈴木一郎', 320000);
-- 更新前を確認
SELECT * FROM employees;
-- 田中太郎の給与を380,000に更新
UPDATE employees SET salary = 380000 WHERE emp_id = 1;
-- 更新後を確認
SELECT * FROM employees;
複数列の同時更新
複数の列を同時に更新することもできる。
UPDATE employees SET emp_name = '田中太郎(部長)', salary = 500000 WHERE emp_id = 1;
WHERE句を忘れると全行が更新される
WHERE 句を書き忘れると、 テーブルの全行が更新されてしまう 。
これは非常に危険なミスである。
UPDATE employees SET salary = 380000;
UPDATE文を書くときは、必ずWHERE句を確認してから実行する こと。
Step 5: データの削除(DELETE)
DELETE文の基本構文
テーブルからデータを削除するには、 DELETE 文を使う。
DELETE FROM テーブル名 WHERE 条件;
-- テーブル作成とデータ登録
CREATE TABLE employees (
emp_id INTEGER PRIMARY KEY,
emp_name VARCHAR(50) NOT NULL,
salary INTEGER CHECK (salary > 0)
);
INSERT INTO employees VALUES (1, '田中太郎', 350000);
INSERT INTO employees VALUES (2, '佐藤花子', 400000);
INSERT INTO employees VALUES (3, '鈴木一郎', 320000);
-- 削除前を確認
SELECT * FROM employees;
-- 社員ID = 3のデータを削除
DELETE FROM employees WHERE emp_id = 3;
-- 削除後を確認
SELECT * FROM employees;
WHERE句を忘れると全行が削除される
UPDATEと同様に、 WHERE 句を書き忘れると テーブルの全データが削除されてしまう 。
DELETE文もWHERE句を必ず確認してから実行する こと。
Step 6: 外部キー制約と削除
参照されている行は削除できない
外部キーで参照されている行は、 削除できない 。
-- テーブル作成
CREATE TABLE departments (
dept_id INTEGER PRIMARY KEY,
dept_name VARCHAR(50) NOT NULL UNIQUE
);
CREATE TABLE employees (
emp_id INTEGER PRIMARY KEY,
emp_name VARCHAR(50) NOT NULL,
dept_id INTEGER REFERENCES departments(dept_id)
);
-- データ登録
INSERT INTO departments VALUES (1, '営業部');
INSERT INTO departments VALUES (2, '開発部');
INSERT INTO employees VALUES (1, '田中太郎', 1);
INSERT INTO employees VALUES (2, '佐藤花子', 2);
-- 社員が所属している部門を削除 → エラー!
DELETE FROM departments WHERE dept_id = 1;
正しい削除順序
| 順序 | 操作 | 理由 |
|---|
| 1. 子テーブル | employeesの該当データを削除 | 参照している側を先に削除する |
| 2. 親テーブル | departmentsの該当データを削除 | 参照されなくなったら削除できる |
DELETE FROM employees WHERE dept_id = 1;
DELETE FROM departments WHERE dept_id = 1;
削除の順序は追加の順序と 逆 になる。
追加は「親 → 子」、削除は「子 → 親」の順序で行う。
Step 7: トランザクション
トランザクションとは
トランザクション とは、複数のSQL操作を 1つのまとまり として扱う仕組みである。
トランザクション内の操作は、 全部成功するか、全部失敗するか のどちらかになる。
ACID特性
トランザクションには、4つの重要な特性がある。
| 特性 | 英語 | 意味 |
|---|
| 原子性 | Atomicity | 全部成功か全部失敗のどちらか |
| 一貫性 | Consistency | トランザクション前後でデータの整合性が保たれる |
| 独立性 | Isolation | 同時に実行されるトランザクション同士が干渉しない |
| 永続性 | Durability | 確定したデータは障害が起きても失われない |
なぜトランザクションが必要か
Step 0で見た銀行送金の例を思い出そう。
- ① Aさんの口座から1万円引き落とし
- ② Bさんの口座に1万円入金
この2つの操作は、 必ず両方とも成功するか、両方とも失敗する 必要がある。
途中で片方だけ成功すると、お金が消えたり増えたりしてしまう。
BEGIN、COMMIT、ROLLBACK
| コマンド | 意味 |
|---|
BEGIN | トランザクションを開始する |
COMMIT | トランザクション内の操作を 確定 する |
ROLLBACK | トランザクション内の操作を 取り消す (元に戻す) |
-- 口座テーブル
CREATE TABLE accounts (
account_id INTEGER PRIMARY KEY,
owner_name VARCHAR(50) NOT NULL,
balance INTEGER CHECK (balance >= 0)
);
INSERT INTO accounts VALUES (1, 'Aさん', 50000);
INSERT INTO accounts VALUES (2, 'Bさん', 30000);
-- 送金前の残高
SELECT * FROM accounts;
-- トランザクション開始
BEGIN;
-- ① 引き落とし
UPDATE accounts SET balance = balance - 10000 WHERE account_id = 1;
-- ② 入金
UPDATE accounts SET balance = balance + 10000 WHERE account_id = 2;
-- 全ての操作を確定
COMMIT;
-- 送金後の残高
SELECT * FROM accounts;
ROLLBACKの例
もし操作を取り消したい場合は、 COMMIT の代わりに ROLLBACK を使う。
BEGIN;
UPDATE accounts SET balance = balance - 10000 WHERE account_id = 1;
ROLLBACK;
Spring/MyBatisでのトランザクション管理
Spring Frameworkでは、 @Transactional アノテーションを付けるだけで、
トランザクション管理を自動的に行ってくれる。SQLレベルでBEGIN/COMMITを書く必要はないが、
「何が行われているか」を理解しておくことが大切である。
Step 8: 実践課題
課題1:新しい部門と社員を追加しよう
以下のテーブルに、新しい部門「マーケティング部(東京)」と、
その部門に所属する社員「山田次郎(給与300,000円)」を追加するSQLを書いてみよう。
CREATE TABLE departments (
dept_id INTEGER PRIMARY KEY,
dept_name VARCHAR(50) NOT NULL UNIQUE,
location VARCHAR(50)
);
CREATE TABLE employees (
emp_id INTEGER PRIMARY KEY,
emp_name VARCHAR(50) NOT NULL,
dept_id INTEGER REFERENCES departments(dept_id),
hire_date DATE NOT NULL,
salary INTEGER CHECK (salary > 0),
email VARCHAR(100) UNIQUE
);
INSERT INTO departments VALUES (1, '営業部', '東京');
INSERT INTO departments VALUES (2, '開発部', '大阪');
INSERT INTO departments VALUES (3, '人事部', '東京');
INSERT INTO employees VALUES (1, '田中太郎', 1, '2020-04-01', 350000, 'tanaka@example.com');
INSERT INTO employees VALUES (2, '佐藤花子', 2, '2021-04-01', 400000, 'sato@example.com');
INSERT INTO employees VALUES (3, '鈴木一郎', 2, '2022-04-01', 320000, 'suzuki@example.com');
INSERT INTO employees VALUES (4, '高橋美咲', 3, '2023-04-01', 280000, 'takahashi@example.com');
-- ここに新しい部門と社員を追加するSQLを書こう
-- 確認
SELECT * FROM departments;
SELECT * FROM employees;
課題2:全社員の給与を10%引き上げよう
全社員の給与を10%引き上げるUPDATE文を書いてみよう。
salary * 1.1 で10%増しの計算ができる。
WHERE句は不要(全社員が対象のため)。
課題3:トランザクションを使って安全に更新しよう
以下の操作をトランザクションで囲んで、安全に実行するSQLを書いてみよう。
- 営業部(dept_id = 1)の所在地を「横浜」に変更
- 田中太郎(emp_id = 1)の給与を400,000に変更
まとめ
この章では、 データの作成・更新・削除 を学んだ。
🎯 達成できたこと
- ✅ INSERT文でデータを追加できるようになった
- ✅ 外部キー制約を考慮してデータを追加できるようになった
- ✅ UPDATE文で条件を指定してデータを更新できるようになった
- ✅ DELETE文で条件を指定してデータを削除できるようになった
- ✅ トランザクションの必要性を説明できるようになった
- ✅ COMMITとROLLBACKの役割を説明できるようになった
📚 学んだ内容
| SQL文 | 構文 | 注意点 |
|---|
| INSERT | INSERT INTO テーブル名 VALUES (...) | 外部キーがある場合は親テーブルを先に登録 |
| UPDATE | UPDATE テーブル名 SET 列 = 値 WHERE 条件 | WHERE句を忘れると全行が更新される |
| DELETE | DELETE FROM テーブル名 WHERE 条件 | WHERE句を忘れると全行が削除される |
| BEGIN | BEGIN; | トランザクションを開始する |
| COMMIT | COMMIT; | トランザクション内の操作を確定する |
| ROLLBACK | ROLLBACK; | トランザクション内の操作を取り消す |
🚀 次のステップ
次の章では、 データの取得(SELECT文の基礎) を学ぶ。
テーブルから必要なデータを取り出す方法を、さまざまなパターンで練習しよう。
💡 よくある質問
Q1: INSERT文で列名を省略してよいのか?
A: 全列指定(VALUES (値1, 値2, ...))は短く書けるが、テーブルの列順に依存するため、テーブル構造が変更されると動かなくなるリスクがある。実務では列名を明示する方法(INSERT INTO テーブル (列1, 列2) VALUES (値1, 値2))が推奨される。
Q2: UPDATEでWHERE句を忘れた場合、元に戻せるか?
A: トランザクション内であれば ROLLBACK で元に戻せる。しかし、 COMMIT 済みの場合は 元に戻せない 。そのため、本番環境では必ずバックアップを取った上で操作すること。UPDATE/DELETEの実行前には、まず同じWHERE条件で SELECT を実行して、対象行を確認するのが安全な手順である。
Q3: DELETEとTRUNCATEの違いは何か?
A: DELETE はWHERE句で条件を指定でき、トランザクション内で ROLLBACK も可能である。 TRUNCATE はテーブルの全データを高速に削除するが、条件指定やROLLBACKができない場合が多い。通常は DELETE を使い、テーブルの全データを一括削除する特殊なケースでのみ TRUNCATE を検討する。
Q4: トランザクションはいつ使うのか?
A: 複数のSQL操作が「全部成功するか全部失敗するか」でなければならない場合に使う。例:銀行送金、在庫管理(出荷と在庫減少)、ユーザー登録(usersテーブルとprofilesテーブルへの同時追加)。Spring Frameworkでは @Transactional アノテーションで自動管理される。
Q5: 外部キー制約がある場合、データを全部削除するにはどうすればよいか?
A: 子テーブル(参照している側)から先に削除し、その後に親テーブル(参照されている側)を削除する。逆の順序で削除しようとすると、外部キー制約違反のエラーが発生する。
練習問題
この章の内容を理解できたか確認しよう。
以下のSQL文の空欄を埋めて、employees テーブルにデータを追加せよ。
employees (name, department)
('鈴木一郎', '開発部');
解答例
INSERT INTO employees (name, department) VALUES ('鈴木一郎', '開発部');
解説
テーブルにデータを追加するには INSERT INTO 文を使用する。
基本構文:
INSERT INTO テーブル名 (列名1, 列名2, ...) VALUES (値1, 値2, ...);
ポイント:
INSERT INTO でテーブル名と列名を指定する
VALUES の後に、対応する値をカッコで囲んで指定する
- 文字列は シングルクォート
'...' で囲む
- 数値はクォートなしで記述する
- 列の順番と値の順番を一致させること
よくある間違い:
- 文字列をダブルクォートで囲む(SQLではシングルクォートを使う)
VALUES を書き忘れる
- 列の数と値の数が一致しない
- テーブル名・カラム名は 小文字 で入力する(例:
employees, dept_id)
- SQLキーワードは 大文字 で入力する(例:
SELECT, FROM, WHERE)
正しい例: SELECT emp_name FROM employees WHERE dept_id = 1;
employees テーブルに以下のデータを追加するSQL文を完成させよ。
(dept_id=1 は departments テーブルに存在する値である)
emp_id=10, emp_name='新入社員', dept_id=1, hire_date='2024-04-01', salary=250000, email='new@example.com'
INSERT
employees (emp_id, emp_name, dept_id, hire_date, salary, email)
(10, '新入社員', 1, '2024-04-01', 250000, 'new@example.com');
解答例
INSERT INTO employees (emp_id, emp_name, dept_id, hire_date, salary, email) VALUES (10, '新入社員', 1, '2024-04-01', 250000, 'new@example.com');
解説
INSERT INTO テーブル名 (列名...) VALUES (値...) の形式でデータを追加する。
外部キー制約の注意点:
dept_id には departments テーブルに存在する値(1〜4)のみ指定できる
- 存在しない
dept_id(例: dept_id=99)を指定すると外部キー制約エラーが発生する
dept_id は NOT NULL ではないため NULL を指定することはできる(部門未所属)
挿入時のルール:
- 列の順番と値の順番を一致させる
- 文字列は シングルクォート
'...' で囲む
- 日付も
'2024-04-01' のようにシングルクォートで囲む
- 数値はクォートなしで記述する
- テーブル名・カラム名は 小文字 で入力する(例:
employees, dept_id)
- SQLキーワードは 大文字 で入力する(例:
SELECT, FROM, WHERE)
正しい例: SELECT emp_name FROM employees WHERE dept_id = 1;
emp_id が1の社員の salary を380000に更新するSQL文を完成させよ。
employees
salary = 380000
WHERE emp_id = 1;
解答例
UPDATE employees SET salary = 380000 WHERE emp_id = 1;
解説
データを更新するには UPDATE テーブル名 SET 列名 = 値 WHERE 条件 を使用する。
基本構文:
UPDATE テーブル名
SET 列名 = 新しい値
WHERE 条件;
注意点:
WHERE 句を省略すると 全行 が更新されるため、必ず対象を絞り込む条件を指定すること
- 複数列を同時に更新する場合:
SET 列1 = 値1, 列2 = 値2
- 更新前に
SELECT で対象行を確認するのがベストプラクティス
よくある間違い:
SET を書き忘れる
WHERE を省略して全データを更新してしまう
- テーブル名・カラム名は 小文字 で入力する(例:
employees, dept_id)
- SQLキーワードは 大文字 で入力する(例:
SELECT, FROM, WHERE)
正しい例: SELECT emp_name FROM employees WHERE dept_id = 1;
emp_id が10の社員データを削除するSQL文を完成させよ。
employees
WHERE emp_id = 10;
解答例
DELETE FROM employees WHERE emp_id = 10;
解説
データを削除するには DELETE FROM テーブル名 WHERE 条件 を使用する。
基本構文:
DELETE FROM テーブル名
WHERE 条件;
注意点:
WHERE 句を省略すると 全行 が削除されるため、必ず条件を指定すること
- 外部キーで参照されている行は削除できない(外部キー制約エラー)
- 削除前に
SELECT で対象行を確認するのがベストプラクティス
よくある間違い:
DELETE FROM ではなく DELETE テーブル名 と書いてしまう(FROM が必要)
WHERE を省略して全データを削除してしまう
以下のSQL文を実行したとき、何が起きるか選べ。
(employees テーブルに dept_id=1 の社員が存在する状態で実行する)
DELETE FROM departments WHERE dept_id = 1;
正解
B. 外部キー制約エラーが発生し、削除できない
解説
employees.dept_id は departments.dept_id を参照する外部キーが設定されている。
この外部キーを参照している行が employees テーブルに存在する場合、
参照先(departments)のデータは削除できず、外部キー制約エラーが発生する。
外部キー制約の削除オプション(明示的に設定した場合のみ動作):
ON DELETE CASCADE: 参照元の行も自動削除(選択肢C)
ON DELETE SET NULL: 参照元の外部キー列をNULLに更新(選択肢D)
ON DELETE RESTRICT(デフォルト): 参照元が存在する場合は削除を禁止(選択肢B)
設定しない場合のデフォルトは RESTRICT であるため、エラーが発生する。
トランザクションを使う最大の目的として正しいものを選べ。
正解
B. 複数の操作を「全部成功」か「全部失敗」に一括管理するため
解説
トランザクションは複数のSQL操作をひとまとまりとして扱う仕組みである。
なぜ必要か(具体例):
銀行振込を考えてみよう。
- 振込元の口座から10万円を引く:
UPDATE accounts SET balance = balance - 100000 WHERE id = 1;
- 振込先の口座に10万円を加える:
UPDATE accounts SET balance = balance + 100000 WHERE id = 2;
もし①の後、②の前にシステムがクラッシュしたら?振込元からお金が消えているのに、
振込先に届いていないという不整合が発生する。
トランザクションの役割:
- どちらも成功した場合のみ確定(
COMMIT)
- 一方が失敗したら両方取り消す(
ROLLBACK)
- これにより「全部成功」か「全部失敗」かのどちらかにしか結果がならない(原子性)
ROLLBACK を実行したとき、何が起きるか選べ。
COMMIT と ROLLBACK のどちらが「取り消し」か考えてみよう
正解
B. トランザクション中に行った変更を取り消して元の状態に戻す
解説
この問題は ROLLBACK の動作を問うている。
COMMIT と ROLLBACK の違い:
COMMIT: トランザクション中に行った変更を 確定 して保存する
ROLLBACK: トランザクション中に行った変更を 取り消し て BEGIN 前の状態に戻す
使い方:
BEGIN; -- トランザクション開始
UPDATE employees SET salary = salary + 10000; -- 操作
-- エラーが発生した場合 → ROLLBACK で取り消し
-- 正常に完了した場合 → COMMIT で確定
COMMIT; -- または ROLLBACK;
選択肢Aは COMMIT の説明であることに注意する。
- テーブル名・カラム名は 小文字 で入力する(例:
employees, dept_id)
- SQLキーワードは 大文字 で入力する(例:
SELECT, FROM, WHERE)
正しい例: SELECT emp_name FROM employees WHERE dept_id = 1;
以下のトランザクション処理の空欄を埋めよ。
dept_id=1 の全社員の salary を10000円増やし、正常に完了したら確定する。
;
UPDATE employees SET salary = salary + 10000 WHERE dept_id = 1;
;
トランザクションの開始コマンドと終了コマンドを思い出そう
解答例
BEGIN; UPDATE employees SET salary = salary + 10000 WHERE dept_id = 1; COMMIT;
解説
BEGIN(または START TRANSACTION)でトランザクションを開始し、
操作が正常に完了したら COMMIT で確定する。
トランザクション制御コマンド:
BEGIN: トランザクションを開始する
COMMIT: 変更を確定する(取り消し不可になる)
ROLLBACK: 変更を取り消して BEGIN 前の状態に戻す
実務でのパターン:
BEGIN;
-- 操作1
UPDATE employees SET salary = salary + 10000 WHERE dept_id = 1;
-- 操作2(追加の操作がある場合)
INSERT INTO salary_history VALUES (...);
COMMIT; -- 全操作が成功したら確定
-- エラーが起きたら ROLLBACK で全取り消し
複数テーブルにまたがる更新や、失敗が許されない重要なデータ操作には
必ずトランザクションを使うことが推奨される。