【ITニュース解説】Cursor & Trigger with Examples
2025年10月02日に「Dev.to」が公開したITニュース「Cursor & Trigger with Examples」について初心者にもわかりやすく解説しています。
ITニュース概要
データベースのCursorは、条件に合うデータを1行ずつ取り出し、加工するのに使う。Triggerは、データが追加・更新された際、自動で別の処理を実行する機能だ。例えば、新規登録で監査ログを自動記録するのに役立つ。
ITニュース解説
データベースは、システムが扱う情報を整理し、効率的に管理するための重要な基盤だ。システム開発の現場では、このデータベースに保存されたデータを柔軟に操作したり、特定の条件に基づいて自動的に処理したりする必要が頻繁に生じる。そのような高度なデータベース操作を実現するために、「カーソル」と「トリガー」という二つの強力な機能が存在する。これらはデータベース管理システム(DBMS)が提供する機能であり、開発者がより複雑で信頼性の高いシステムを構築する上で不可欠な要素となる。システムエンジニアを目指す者にとって、これらの機能の仕組みと適切な利用方法を理解することは、データベースを深く活用し、応用力を高めるために非常に役立つだろう。
まず、カーソルについて解説する。 通常、データベースから複数のデータを取得する際には、SELECT文を用いる。SELECT文は、指定された条件に合致する「複数のデータセット」を一括で取得する、いわば集合を扱う「セット指向」の処理が基本だ。これは非常に効率的だが、時には取得したデータの一つ一つに対して、特別な処理を順番に行いたい場合がある。例えば、ある条件を満たす従業員一人ひとりの給与を個別に確認し、特定のビジネスロジックを適用したり、外部システムへ個別のデータ連携を行ったりするようなケースだ。このように、クエリ結果の「行一つ一つ」に対して処理を行う「行指向」の操作が必要な場合に、カーソルがその能力を発揮する。
カーソルは、データベースのクエリ結果セットを一時的に保持し、その結果セット内の各行を一つずつ順次処理するための仕組みを提供する。これは、データベースから取得した大量のデータを、あたかもプログラムのループ処理のように一つずつ取り出して、必要な処理を実行することを可能にする。
具体的な例として、従業員テーブル(Employee)から、給与が50,000円を超える従業員の名前と給与を表示するという課題を見てみよう。 この課題を解決するために、まずデータベースに従業員テーブルが存在しない場合は作成する。このテーブルは、EmpID(従業員ID)、EmpName(従業員名)、Salary(給与)という三つのカラムで構成される。次に、テスト用のデータとして、Alice(給与60,000円)、Bob(48,000円)、Charlie(75,000円)、David(45,000円)、Eve(90,000円)といった従業員情報をテーブルに挿入する。
カーソルを定義し、使用する手順は以下の通りだ。
最初に、処理中に従業員名と給与を一時的に格納するための変数 @EmpName と @Salary を宣言する。これらは、取得した各行のデータを一時的に保持するための場所となる。
次に、DECLARE EmployeeCursor CURSOR FOR SELECT EmpName, Salary FROM Employee WHERE Salary > 50000; というSQL文でカーソルを宣言する。これは、EmployeeCursor という名前のカーソルを定義し、このカーソルが「従業員テーブルから給与が50,000円を超える従業員の名前と給与を選択する」というクエリの結果セットを対象とすることを指定している。この宣言の時点では、まだ実際のデータは取得されていない。
カーソルを実際に利用するためには、OPEN EmployeeCursor; という命令でカーソルを開く必要がある。この操作により、データベースシステムはカーソルが対象とするクエリの結果セットへのアクセスを準備する。
カーソルが開かれたら、FETCH NEXT FROM EmployeeCursor INTO @EmpName, @Salary; という命令を使って、結果セットの最初の行のデータを、先ほど宣言した変数 @EmpName と @Salary に取得する。FETCH NEXT は、カーソルが指す「次の行」のデータを取得するためのコマンドだ。
データが残っている間は処理を繰り返すために、WHILE @@FETCH_STATUS = 0 BEGIN ... END; というループ処理を開始する。@@FETCH_STATUS は、直前の FETCH 操作が成功したかどうかを示すシステム変数で、0 は成功を意味する。つまり、このループはデータが正常に取得できている間、繰り返し処理を実行する。
ループの中では、取得した従業員名と給与を表示する処理を行う。PRINT 'Employee: ' + @EmpName + ' | Salary: ' + CAST(@Salary AS VARCHAR); は、コンソールに情報を出力するコマンドで、取得した変数の値を文字列として整形して表示している。ここで CAST(@Salary AS VARCHAR) は、数値型の給与を文字列型に変換する役割を果たす。
各行の処理が完了したら、次の行のデータを取得するために再度 FETCH NEXT FROM EmployeeCursor INTO @EmpName, @Salary; を実行する。これにより、ループが次のデータを処理し、最終的にすべてのデータが処理されるか、条件を満たすデータがなくなると @@FETCH_STATUS が非ゼロになり、ループが終了する。
すべての処理が終わったら、CLOSE EmployeeCursor; でカーソルを閉じ、DEALLOCATE EmployeeCursor; でカーソルが使用していたシステムリソースを解放する。これらの手順を適切に踏むことで、給与が50,000円を超えるAlice、Charlie、Eveのデータが、指定された形式で順番に表示される。カーソルは行ごとの複雑な処理を可能にする一方で、セット指向の処理に比べてパフォーマンスが劣る場合があるため、その使用は慎重に検討し、必要な場合にのみ適用することが望ましい。
次に、トリガーについて解説する。 トリガーは、データベースに対する特定のイベント(データの挿入、更新、削除など)が発生したときに、データベースシステムが自動的に実行する特別なプログラムだ。これは、ユーザーやアプリケーションが明示的に呼び出すことなく、データベースシステム自身が内部で特定の処理を自動的に実行するように設定できるという点で非常に強力な機能だ。トリガーは、データの整合性を自動で維持したり、操作の監査ログを自動で記録したり、複雑なビジネスルールをデータ変更時に適用したりするのに活用される。
トリガーの具体的な例として、新しい学生が Students テーブルに追加された際に、その挿入操作の記録を自動的に Student_Audit という監査テーブルに残すという課題を見てみよう。
まず、Students テーブルを作成する。このテーブルは、StudentID(学生ID)、StudentName(学生名)、Department(学科)というカラムを持つ。
次に、監査情報を記録するための Student_Audit テーブルを作成する。このテーブルは、AuditID(監査ID)、StudentID(対象学生ID)、Action(操作内容)、ActionDate(操作日時)というカラムで構成される。ここで、AuditID には IDENTITY(1,1) が指定されており、新しいレコードが追加されるたびに自動的に連番が振られる設定になっている。
これらのテーブルが用意できたら、トリガーを作成する。
CREATE TRIGGER trg_AfterStudentInsert ON Students AFTER INSERT AS BEGIN INSERT INTO Student_Audit (StudentID, Action, ActionDate) SELECT StudentID, 'INSERT', GETDATE() FROM inserted; END; というSQL文でトリガーを定義する。
CREATE TRIGGER trg_AfterStudentInsert は、trg_AfterStudentInsert という名前の新しいトリガーを作成することを意味する。
ON Students は、このトリガーが Students テーブルに対して適用されることを示している。
AFTER INSERT は、Students テーブルにデータが「挿入された後」にこのトリガーが実行されるように指定している。トリガーは、INSERT だけでなく、UPDATE や DELETE といった他の操作、そして BEFORE や AFTER といった実行タイミングも指定できる。
AS BEGIN ... END; の中には、トリガーが実行されるときに実際に行いたい処理を記述する。この例では、Student_Audit テーブルにデータを挿入する処理が書かれている。
INSERT INTO Student_Audit (StudentID, Action, ActionDate) SELECT StudentID, 'INSERT', GETDATE() FROM inserted; の部分がトリガーの核となる処理だ。
ここで登場する inserted は、トリガー内で利用できる特別な仮想テーブルで、INSERT 操作の場合、新しく挿入された行のデータが一時的に格納される。つまり、Students テーブルに新たに追加された学生の StudentID を、この inserted テーブルから取得できるのだ。
'INSERT' は、固定の文字列として、この操作がデータの挿入であることを示す。
GETDATE() は、現在のシステム日時を取得する関数だ。
これにより、新しい学生が Students テーブルに挿入されるたびに、その学生のID、操作内容として'INSERT'、そして操作日時が自動的に Student_Audit テーブルに記録される。
トリガーが正しく動作するかをテストするために、実際に学生を Students テーブルに挿入してみよう。
INSERT INTO Students (StudentID, StudentName, Department) VALUES (101, 'Rahul', 'Computer Science'); というSQL文を実行すると、学生ID 101のRahulさんがStudentsテーブルに追加される。このINSERT操作が完了すると同時に、AFTER INSERT トリガーである trg_AfterStudentInsert が自動的に実行される。
そして、SELECT * FROM Student_Audit; を実行して Student_Audit テーブルの内容を確認すると、AuditID が自動採番され、StudentID に101、Action に'INSERT'、ActionDate に挿入時の日時が記録されていることが確認できる。このように、トリガーは特定のデータベース操作に対して、ユーザーやアプリケーションの介入なしに自動で追加処理を実行させることで、システムの自動化とデータ管理の信頼性を大幅に高める。
カーソルとトリガーは、それぞれ異なる目的でデータベースの柔軟な操作と自動化に貢献する。カーソルは、クエリ結果セットの各行に対して個別の、複雑な処理を行いたい場合に強力なツールとなる。一方、トリガーは、データの変更イベントに基づいて自動的に特定のタスクを実行し、データの整合性や監査証跡を維持するのに非常に役立つ。これらの機能を適切に理解し、使いこなすことで、より高度で効率的、かつ信頼性の高いデータベースシステムを構築することが可能となるだろう。システムエンジニアを目指す上で、これらの機能がどのような場面で、どのような仕組みで使われるのかをしっかりと理解することは、今後のキャリアにおいて大きな強みとなることは間違いない。