この問題の適切な解決策を見つけるのに数時間を費やしたので、Qでこの問題を作成しました。PL/SQL:列を更新するときに再帰的なトリガーを避ける
私はこのようなテーブルを持っている:
CREATE TABLE SoftwareVersion
(
ID NUMBER NOT NULL,
DeviceID NUMBER NOT NULL,
ReadoutDate DATE NOT NULL,
Version VARCHAR2(20 CHAR) NOT NULL,
NextReadoutDate DATE NULL
);
このテーブルには、デバイスのソフトウェアバージョンのコードが含まれています。各デバイスは、1つ以上のソフトウェアバージョンを持つことができます。このテーブルのINSERT文を実行するインポートプロセスでは、ID、DeviceID、ReadoutDate、およびVersionのみが入力されます。 ReadoutDateは、インポートプロセスの現在のタイムスタンプです。 ソフトウェアバージョンの最初の発生は、「ソフトウェアバージョンは読み取り日以降有効です」とみなされます。
私の問題は、ソフトウェアバージョンの範囲が必要になったことです。どのタイムスタンプから各ソフトウェアバージョンがタイムスタンプに有効か?
パフォーマンスを向上させるため(およびそのテーブルのトリガーが既にあるので)、トリガーによって維持されるNextReadoutDate列を追加しました。そのDeviceIDの次の有効なReadoutDate値を受け取ります。したがって、各ソフトウェアのバージョンは範囲になります(〜から有効)。
変更表の問題(ORA-04091)を回避するために、私は私はこのようなAFTER行トリガーを使用してい文のすべての更新情報の収集:その後
CREATE OR REPLACE TRIGGER TRG_SoftwareVersion1
AFTER INSERT OR UPDATE OR DELETE ON SoftwareVersion
FOR EACH ROW
BEGIN
IF UPDATING THEN
IF :OLD.DeviceID = :NEW.DeviceID AND :OLD.ReadoutDate = :NEW.ReadoutDate OR
(:OLD.DeviceID IS NULL OR :OLD.ReadoutDate IS NULL) AND
(:NEW.DeviceID IS NULL OR :NEW.ReadoutDate IS NULL) THEN
-- Nothing to do
RETURN;
END IF;
END IF;
-- Evaluate later
INSERT INTO
SoftwareVersion_TrgHelper
(
OldDeviceID,
OldReadoutDate,
NewDeviceID,
NewReadoutDate
)
VALUES
(
:OLD.DeviceID,
:OLD.ReadoutDate,
:NEW.DeviceID,
:NEW.ReadoutDate
);
END;
を私はAFTER文の中でテーブルを更新
CREATE OR REPLACE TRIGGER TRG_SoftwareVersion2
AFTER INSERT OR UPDATE OR DELETE ON SoftwareVersion
DECLARE
CURSOR cCursorMain IS SELECT * FROM SoftwareVersion_TrgHelper FOR UPDATE;
vOldDeviceID NUMBER;
vOldReadoutDate DATE;
vNewDeviceID NUMBER;
vNewReadoutDate DATE;
BEGIN
OPEN cCursorMain;
LOOP
FETCH cCursorMain INTO vOldDeviceID, vOldReadoutDate, vNewDeviceID, vNewReadoutDate;
EXIT WHEN cCursorMain%NOTFOUND;
IF UPDATING OR DELETING THEN
UPDATE
SoftwareVersion SV
SET
SV.NextReadoutDate = (SELECT MIN(SV2.ReadoutDate) KEEP (DENSE_RANK FIRST ORDER BY SV2.ReadoutDate ASC, SV2.ID ASC) FROM SoftwareVersion SV2 WHERE SV.DeviceID = SV2.DeviceID AND SV.ReadoutDate < SV2.ReadoutDate)
WHERE
SV.DeviceID = vOldDeviceID AND
SV.ReadoutDate <= vOldReadoutDate;
END IF;
IF UPDATING OR INSERTING THEN
UPDATE
SoftwareVersion SV
SET
SV.NextReadoutDate = (SELECT MIN(SV2.ReadoutDate) KEEP (DENSE_RANK FIRST ORDER BY SV2.ReadoutDate ASC, SV2.ID ASC) FROM SoftwareVersion SV2 WHERE SV.DeviceID = SV2.DeviceID AND SV.ReadoutDate < SV2.ReadoutDate)
WHERE
SV.DeviceID = vNewDeviceID AND
SV.ReadoutDate <= vNewReadoutDate;
END IF;
DELETE FROM SoftwareVersion_TrgHelper WHERE CURRENT OF cCursorMain;
END LOOP;
CLOSE cCursorMain;
END;
/
残念ながら、私はインポートプロセスを再度実行した直後にORA-00036を取得しました。 トリガーの再帰を避ける方法が必要でした。