INSERT 前の元の値や DELETE 後の新しい値を参照することには、意味がありません。それらを参照すると、コンパイル時に SQLCODE -48 が返されます。
SQL トリガ・コード
LANGUAGE SQL の場合、既定でトリガ文は SQL プロシージャ・ブロックです。SQL プロシージャ・ブロックは、1 つの SQL プロシージャ文とその後のセミコロン、またはキーワード BEGIN で始まりキーワード END で終わる 1 つ以上の SQL プロシージャ文と各文の後のセミコロンから成ります。
トリガ動作はアトミックで、完全に適用されるかまったく適用されないかのどちらかです。これに COMMIT 文または ROLLBACK 文を含めることはできません。キーワード BEGIN ATOMIC は、キーワード BEGIN と同義です。
LANGUAGE SQL の場合、CREATE TRIGGER 文はオプションで REFERENCING 節、WHEN 節、および/または UPDATE OF 節を含むことができます。UPDATE OF 節は、トリガに指定した 1 つ以上の列で UPDATE が実行されたときにのみ、トリガを実行することを指定します。LANGUAGE OBJECTSCRIPT の CREATE TRIGGER 文では、これらの節を含むことができません。
SQL トリガ・コードは埋め込み SQL として実行されます。これは、InterSystems IRIS で SQL トリガ・コードが ObjectScript に変換されることを意味します。したがって、SQL トリガ・コードに対応するクラス定義を表示すると、トリガ定義では Language=objectscript が表示されることになります。
SQL トリガ・コードの実行時に、システムは自動的に、トリガ・コード内で使用されているすべての変数をリセット (NEW) します。各 SQL 文の実行後に、InterSystems IRIS は SQLCODE をチェックします。エラーが発生すると、InterSystems IRIS は %ok 変数に 0 をセットし、トリガ・コードの処理および関連する INSERT、UPDATE、または DELETE を中止してロールバックします。
ObjectScript トリガ・コード
LANGUAGE OBJECTSCRIPT の場合、CREATE TRIGGER 文は REFERENCING 節、WHEN 節、または UPDATE OF 節を含むことができません。これらの SQL 限定である節を LANGUAGE OBJECTSCRIPT と共に指定すると、それぞれコンパイル時に SQLCODE エラーの -49、-57、または -50 が発生します。
LANGUAGE OBJECTSCRIPT の場合、トリガ文は、中括弧で囲まれた 1 つ以上の ObjectScript 文からなるブロックです。
トリガのコードはプロシージャとして生成されないため、トリガ内のすべてのローカル変数はパブリック変数となります。つまり、トリガ内のすべての変数は NEW 文で明示的に宣言される必要があります。これにより、トリガを呼び出すコード内の変数との競合を避けることができます。
トリガ・コードにマクロ・プリプロセッサ文 (# コマンド、## 関数、または $$$ マクロ参照) が含まれる場合、これらの文は CREATE TRIGGER DDL コード本体より前にコンパイルされます。
ObjectScript トリガ・コードには埋め込み SQL を含めることができます。
%ok 変数を 0 に設定して、トリガ・コードからエラーを発行できます。これにより、トリガの実行を中止してロール・バックする実行時エラーが生成されます。また、適切な SQLCODE エラー (例えば、SQLCODE -131 “After Insert トリガが失敗しました”) が生成され、トリガ・コード・エラーの原因を説明する文字列として %msg 変数のユーザ指定値が返されます。%ok を数値以外の値に設定すると、%ok=0 に設定されることに注意してください。
トリガ・コードは一度だけ生成されます。これは、マルチ・イベント・トリガの場合でも同様です。
フィールド参照および擬似フィールド参照
ObjectScript で記述するトリガ・コードには、{fieldname} で指定するフィールド参照を含めることができます。ここで、fieldname は現在のテーブルの既存フィールドを指定します。中括弧内に空白スペースは許可されません。
fieldname の後に *N (新)、*O (旧)、または *C (比較) を付けて、挿入、更新、または削除されたフィールドのデータ値の処理方法を以下のとおりに指定できます。
-
{fieldname*N}
-
{fieldname*O}
-
{fieldname*C}
-
UPDATE の場合は、新しい値が古い値と異なる場合に 1 (TRUE) を返します。そうでない場合は 0 (FALSE) を返します。
-
INSERT の場合は、挿入した値が NULL でない場合に 1 (TRUE) を返します。そうでない場合は 0 (FALSE) を返します。
-
DELETE の場合は、削除される値が NULL でない場合に 1 (TRUE) を返します。そうでない場合は 0 (FALSE) を返します。
UPDATE、INSERT、または DELETE では、{fieldname} は {fieldname*N} と同じ値を返します。
例えば、以下のトリガは、Sample.Employee に新しく挿入された行の Name フィールドの値を返します (SQL シェルから INSERT を実行して、この結果を表示できます)。
CREATE TRIGGER InsertNameTrig AFTER INSERT ON Sample.Employee
LANGUAGE OBJECTSCRIPT
{WRITE "The employee ",{Name*N}," was ",{%%OPERATION},"ed on ",{%%TABLENAME},!}
フィールド値を設定する文の中では改行ができません。詳細は、"クラス定義リファレンス" の "SqlComputeCode" プロパティ・キーワードを参照してください。
GetAllColumns()Opens in a new tab メソッドを使用すると、テーブルのために定義されているフィールド名をリストできます。詳細は、"InterSystems SQL の使用法" の “テーブルの定義” の章にある "列の名前と番号" を参照してください。
また、ObjectScript で記述するトリガ・コードには、擬似フィールド参照変数の {%%CLASSNAME}、{%%CLASSNAMEQ}、{%%OPERATION}、{%%TABLENAME}、および {%%ID} を含めることもできます。擬似フィールドは、クラスのコンパイル時に特定の値に変換されます。これらの擬似フィールド・キーワードは、すべて大文字と小文字が区別されません。
-
{%%CLASSNAME} と {%%CLASSNAMEQ} は、ともに SQL テーブル定義を投影するクラスの名前に変換されます。{%%CLASSNAME} は引用符なし文字列を返し、{%%CLASSNAMEQ} は引用符付き文字列を返します。
-
{%%OPERATION} は、トリガを呼び出した操作に応じて、文字列リテラル (INSERT、UPDATE、または DELETE のいずれか) に変換されます。
-
{%%TABLENAME} はテーブルの完全修飾名に変換されます。
-
{%%ID} は RowID 名に変換されます。この参照は、RowID フィールドの名前がわからないときに役立ちます。
ストリーム・プロパティの参照
{StreamField}、{StreamField*O}、または {StreamField*N} など、ストリーム・フィールド/プロパティがトリガ定義で参照される場合は、{StreamField} 参照の値がストリームの OID (オブジェクト ID) 値となります。
BEFORE INSERT または BEFORE UPDATE トリガについては、新しい値が INSERT/UPDATE/ObjectSave によって指定される場合、{StreamField*N} 値が一時的ストリーム・オブジェクトの OID、または新しいリテラル・ストリーム値のどちらかとなります。BEFORE UPDATE トリガについては、新しい値がストリーム・フィールド/プロパティに対して指定されていない場合、{StreamField*O} および {StreamField*N} は両方とも現在のフィールド/プロパティ・ストリーム・オブジェクトの OID になります。
SQLComputed プロパティの参照
一時 SqlComputed フィールド/プロパティ ("Calculated" または明示的な "Transient" のどちらか) がトリガ定義で参照される場合、そのトリガは Get()/Set() メソッドのオーバーライドを認識しません。プロパティの Get() または Set() メソッドをオーバーライドするのではなく、SQLCOMPUTED/SQLCOMPUTONCHANGE を使用してください。
Get()/Set() メソッドのオーバーライドを使用すると、{property*O} 値の決定に SQL が使用されて、オーバーライドされた Get()/Set() メソッドが使用されないという誤った結果がもたらされる可能性があります。プロパティがディスク上に格納されないため、{property*O} では SqlComputeCode が使用されて古い値が "再作成" されます。ただし、{property*N} は、オーバーライドされた Get()/Set() メソッドを使用してプロパティの値にアクセスします。その結果、プロパティが実際に変更されなかった場合でも、{property*O} と {property*N} は異なる場合があります (したがって、{property*C}=1)。
ラベル
トリガ・コードには、行ラベル (タグ) が含まれる場合があります。トリガ・コードでラベルを指定するには、ラベル行の先頭にコロンを付けて、その行が最初の列で始まることを示します。InterSystems IRIS はコロンを削除して、残りの行をラベルとして処理します。ただし、トリガ・コードはプロシージャ・ブロックの範囲外で生成されるため、すべてのラベルはクラス定義に対して一意である必要があります。クラスのルーチンにコンパイルされるその他すべてのコードには、同じラベルを定義することはできません。これには、その他のトリガ、プロシージャ・ブロックを使用しないメソッド、SqlComputeCode などが含まれます。
Note:
ラベル用のコロン接頭語の使用は、ホスト変数参照用のコロン接頭語の使用よりも優先されます。この競合を回避するために、埋め込み SQL トリガ・コード行の先頭にホスト変数参照を記述しないことをお勧めします。トリガ・コード行の先頭にホスト変数参照を記述する必要がある場合は、二重のコロン接頭語を使用することで、それがホスト変数でありラベルでないことを明示してください。
メソッドの呼び出し
クラス・メソッドは開いているオブジェクトの有無に依存しないため、クラス・メソッドをトリガ・コード内から使用できます。クラス・メソッドを呼び出すには、##class(classname).Method() 構文を使用する必要があります。..Method() 構文では、現在開いているオブジェクトが必要なので、この構文は使用できません。
クラス・メソッドの引数として現在の行のフィールドの値を渡すことができますが、クラス・メソッド自体はフィールド構文を使用できません。
トリガの実行時エラー
トリガおよびそのトリガが起動するイベントは、単一行ベースでアトミック処理として実行されます。つまり、以下のように処理されます。
-
失敗した BEFORE トリガはロールバックされ、関連する INSERT、UPDATE、または DELETE 操作は実行されません。さらに行のすべてのロックは解放されます。
-
失敗した AFTER トリガはロールバックされ、関連する INSERT、UPDATE、または DELETE 操作がロールバックされます。さらに行のすべてのロックは解放されます。
-
失敗した INSERT、UPDATE、または DELETE 操作はロールバックされ、関連する BEFORE トリガがロールバックされます。さらに行のすべてのロックは解放されます。
-
失敗した INSERT、UPDATE、または DELETE 操作はロールバックされ、関連する AFTER トリガは実行されません。さらに行のすべてのロックは解放されます。
整合性が維持されるのは、現在の行の操作のみであることに注意してください。アプリケーション・プログラムは、トランザクション処理文を使用して、複数行での操作も含めてデータの整合性の問題に対処する必要があります。
トリガはアトミック処理であるため、コミットやロールバックのようなトランザクション文は、トリガ・コード内ではコーディングできません。
INSERT、UPDATE、または DELETE 操作が複数のトリガを実行する場合、1 つのトリガが失敗すると残りすべてのトリガは実行されないままになります。
-
SQLCODE -415 : トリガ・コードにエラーがある場合 (例えば、存在しないテーブルや未定義の変数への参照)、トリガ・コードの実行は実行時に失敗し、InterSystems IRIS は SQLCODE -415 エラー “SQL ファイラ内で致命的なエラーが発生しました” を発行します。
-
SQLCODE -130 ~ -135 : トリガ処理が失敗すると、InterSystems IRIS は失敗したトリガのタイプにより SQLCODE エラー・コードの -130 から -135 のうちの 1 つを実行時に発行します。トリガ・コード内で %ok 変数を 0 に設定することで、トリガを失敗させることができます。これにより、適切な SQLCODE エラー (例えば、SQLCODE -131 “After Insert トリガが失敗しました”) が発行され、トリガ・コード・エラーの原因を説明する文字列として %msg 変数のユーザ指定値が返されます。
例
以下の例では、ObjectScript DELETE トリガとの CREATE TRIGGER の使用を実際に示します。ここでは、レコードが含まれるデータ・テーブル (TestDummy) が存在することを想定しています。これは、埋め込み SQL を使用してログ・テーブル (TestDummyLog) と DELETE トリガを作成します。DELETE トリガは、データ・テーブルで削除が実行されたときにログ・テーブルへの書き込みを行います。このトリガは、データ・テーブルの名前、削除された行の RowId、現在の日付、および実行された操作のタイプ (%oper 特殊変数) (この場合は “DELETE”) を挿入します。
&sql(CREATE TABLE TestDummyLog
(TableName VARCHAR(40),
IDVal INTEGER,
LogDate DATE,
Operation VARCHAR(40))
)
WRITE !,"SQL log table code is: ",SQLCODE
&sql(CREATE TRIGGER TrigTestDummy AFTER DELETE ON TestDummy
LANGUAGE OBJECTSCRIPT {
NEW id
SET id = {ID}
&sql(INSERT INTO TestDummyLog (TableName,IDVal,LogDate,Operation)
VALUES ('TestDummy',:id,+$HOROLOG,:%oper))
}
)
WRITE !,"SQL trigger code is: ",SQLCODE
以下の例では、SQL INSERT トリガとの CREATE TRIGGER の使用を実際に示します。1 つ目の埋め込み SQL プログラムでは、テーブル、そのテーブルの INSERT トリガ、およびそのトリガの使用を記録するログ・テーブルを作成します。2 つ目の埋め込み SQL プログラムでは、テーブルに対して INSERT を発行し、トリガを呼び出してログ・テーブルにエントリを記録します。ログ・エントリの表示後、プログラムは両方のテーブルを削除し、このプログラムを繰り返し実行できるようにします。
DO $SYSTEM.Security.Login("_SYSTEM","SYS")
&sql(CREATE TABLE TestDummy (
testnum INT NOT NULL,
firstword CHAR (30) NOT NULL,
lastword CHAR (30) NOT NULL,
CONSTRAINT TestDummyPK PRIMARY KEY (testnum))
)
WRITE !,"SQL table code is: ",SQLCODE
&sql(CREATE TABLE TestDummyLog (
entry CHAR (60) NOT NULL)
)
WRITE !,"SQL log table code is: ",SQLCODE
&sql(CREATE TRIGGER TrigTestDummy AFTER INSERT ON TestDummy
LANGUAGE SQL
BEGIN
INSERT INTO TestDummyLog (entry) VALUES
(CURRENT_TIMESTAMP||' INSERT to TestDummy');
END )
WRITE !,"SQL trigger code is: ",SQLCODE
NEW SQLCODE,%ROWCOUNT,%ROWID
&sql(INSERT INTO TestDummy (testnum,firstword,lastword) VALUES
(46639,'hello','goodbye'))
IF SQLCODE=0 {
WRITE !,"Insert succeeded"
WRITE !,"Row count=",%ROWCOUNT
WRITE !,"Row ID=",%ROWID }
ELSE {
WRITE !,"Insert failed, SQLCODE=",SQLCODE }
&sql(SELECT entry INTO :logitem FROM TestDummyLog)
IF SQLCODE<0 {WRITE "SQLCODE error ",SQLCODE," ",%msg QUIT}
ELSEIF SQLCODE=100 {WRITE "Query returns no results" QUIT}
WRITE !,"Log entry: ",logitem
&sql(DROP TABLE TestDummy)
&sql(DROP TABLE TestDummyLog)
WRITE !,"finished!"
以下に、括弧内の述語条件が満たされた場合にのみ action が実行されるように指定する WHEN 節の例を示します。
CREATE TRIGGER Trigger_2 AFTER INSERT ON Table_1
WHEN (f1 %STARTSWITH 'A')
BEGIN
INSERT INTO Log_Table VALUES (new_row.Category);
END
以下の例では、Sample.Employee での行の挿入、更新、または削除後に Name フィールドの古い値と新しい値を返すトリガを定義します (SQL シェルからこのトリガ・イベント操作を実行して、この結果を表示できます)。
CREATE TRIGGER EmployNameTrig AFTER INSERT,UPDATE,DELETE ON Sample.Employee
LANGUAGE OBJECTSCRIPT
{WRITE "Employee old name:",{Name*O}," new name:",{Name*N}," ",{%%OPERATION}," on ",{%%TABLENAME},!}