例えば、君が大きなデータベースの管理者だと想像してみて。突然誰かが大事な情報をテーブルから消しちゃって、みんなが「誰がやったの!?」ってパニックになる。こういう事態を防ぐために、データベースには変更履歴を記録して、何が起きたか追跡できる仕組みがあるんだ。それがログ記録と監査(audit)だよ。
- 変更ログは「何が、どう、いつ変わったか」の履歴を残すためのもの。
- データ監査はもっと深くて、変更内容だけじゃなくて「誰がやったか」みたいな情報も記録するために使うよ。
「なぜ必要か」がわかったところで、今度は「どうやるか」を見ていこう!
ログ用テーブルを作ろう
トリガーを設定する前に、まずは変更ログを保存するテーブルが必要だよ。例えばこんな感じ:
-- 変更ログ用のテーブルを作成
CREATE TABLE change_logs (
log_id SERIAL PRIMARY KEY, -- ログレコードのユニークID
table_name TEXT NOT NULL, -- 変更があったテーブル名
operation TEXT NOT NULL, -- 操作タイプ: INSERT, UPDATE, DELETE
change_time TIMESTAMP DEFAULT NOW(), -- 変更時刻
old_data JSONB, -- 変更前データ(UPDATE/DELETE用)
new_data JSONB -- 変更後データ(INSERT/UPDATE用)
);
これってどういう意味?
log_id— 各ログレコードのユニークIDだよ。table_name— どのテーブルが変更されたか記録する。operation— 操作タイプ:INSERT、UPDATE、DELETE。change_time— 変更があった正確な時刻を記録。old_dataとnew_data— 変更前と変更後のデータをJSON形式で保存する。
トリガーで変更をログに記録しよう
ログ用テーブルができたら、今度は例えばstudentsテーブルにトリガーを作ってみよう。これで新しい学生の追加、更新、削除の全部を記録できるよ。 やることはこんな感じ:
- PL/pgSQLでログ用テーブルにレコードを追加する関数を書く。
studentsテーブルにトリガーを作る。
関数は操作タイプ(INSERT、UPDATE、DELETE)と、変更されたデータ(OLDとNEW)を受け取るよ。
-- 変更をログテーブルに記録する関数
CREATE OR REPLACE FUNCTION log_student_changes()
RETURNS TRIGGER AS $$
BEGIN
-- INSERT操作のログ
IF TG_OP = 'INSERT' THEN
INSERT INTO change_logs (table_name, operation, new_data)
VALUES ('students', 'INSERT', row_to_json(NEW));
-- DELETE操作のログ
ELSIF TG_OP = 'DELETE' THEN
INSERT INTO change_logs (table_name, operation, old_data)
VALUES ('students', 'DELETE', row_to_json(OLD));
-- UPDATE操作のログ
ELSIF TG_OP = 'UPDATE' THEN
INSERT INTO change_logs (table_name, operation, old_data, new_data)
VALUES ('students', 'UPDATE', row_to_json(OLD), row_to_json(NEW));
END IF;
RETURN NULL; -- AFTERトリガーなのでNULLを返す
END;
$$ LANGUAGE plpgsql;
ここで:
TG_OP— 今の操作タイプ(INSERT、UPDATE、DELETE)が入ってる特別な変数。row_to_json(OLD)とrow_to_json(NEW)— レコードをJSON形式に変換して保存しやすくする。RETURN NULL—AFTERトリガーだから、変更データは返さない。
じゃあ、この関数をstudentsテーブルに紐付けよう。
-- 変更ログ用のトリガーを作成
CREATE TRIGGER students_log_trigger
AFTER INSERT OR UPDATE OR DELETE ON students
FOR EACH ROW
EXECUTE FUNCTION log_student_changes();
これってどういうこと?
AFTER INSERT OR UPDATE OR DELETE—studentsテーブルで指定した操作が終わった後にトリガーが動く。FOR EACH ROW— 変更された各行ごとにトリガーが実行される。EXECUTE FUNCTION log_student_changes()— ログ記録用の関数を呼び出す。
トリガーをテストしよう
さあ、トリガーがちゃんと動くか試してみよう。
- 新しいレコードの挿入
INSERT INTO students (name, age, grade)
VALUES ('オットー・リン', 20, 'A');
ログテーブルに何が記録されたか見てみよう:
SELECT * FROM change_logs;
結果例:
| log_id | table_name | operation | change_time | old_data | new_data |
|---|---|---|---|---|---|
| 1 | students | INSERT | 2023-10-10 12:00:00 | NULL | {"name": "オットー・リン", "age": 20, ...} |
- レコードの更新
UPDATE students
SET grade = 'B'
WHERE name = 'オットー・リン';
またログテーブルをチェック:
SELECT * FROM change_logs ORDER BY change_time DESC;
結果:
| log_id | table_name | operation | change_time | old_data | new_data |
|---|---|---|---|---|---|
| 2 | students | UPDATE | 2023-10-10 12:05:00 | {"name": "オットー・リン", "age": ...} | {"name": "オットー・リン", "age": ..., ...} |
- レコードの削除
DELETE FROM students
WHERE name = 'オットー・リン';
またまたログを確認:
| log_id | table_name | operation | change_time | old_data | new_data |
|---|---|---|---|---|---|
| 3 | students | DELETE | 2023-10-10 12:10:00 | {"name": "オットー・リン", "age": ...} | NULL |
実際の使い方の例
- 重要なテーブルの操作ログ:例えば銀行口座のテーブルは、全部の変更を記録して不正を防ぐ必要があるよ。
- システム監査:法令順守やユーザー行動の分析のために記録を残すこともできる。
- データ復元のための保険:誰かがうっかりデータを消しても、ログテーブルから復元できる。
注意点と落とし穴
トリガーでログ記録を実装するときは、パフォーマンスに注意しよう。トリガーが頻繁に発動すると、データベースに負荷がかかることもある。だから:
- 本当に重要なテーブルだけログ記録を使おう。
- ログが増えすぎたら、アーカイブ戦略を考えよう。
トリガーはギターの弦みたいなもの。ちゃんと調整すれば最高の音が出るし、面倒な作業を自動化してデータ管理もバッチリできるよ!
GO TO FULL VERSION