1

さて、私は 2 つのテーブル (ORDERS と ORDERLINES) を持っていますが、これらは基本的に同じ問題を抱えており、それぞれにトリガーを設定して問題に対処しています。問題は、テーブル レベルの一意性を持つ PK に加えて、RECID と呼ばれるフィールドに、別のフィールドに対して一意である必要がある別のフィールド RECNO があることです。

テーブルは次のように FK 関連です。

ORDERS.WAREHOUSEID > WAREHOUSES.CUSTOMERID > CUSTOMERS

ORDERSLINES.ORDERID > ORDERS

OnORDERSで、レルム固有の一意のRECNOORDERSLINESを割り当てるトリガーがあります。 では、RECNOはレコードの領域内で一意である必要があります。 では、RECNOはレコードの領域内で一意である必要があります。BEFORE INSERT
ORDERSCUSTOMERS
ORDERLINESORDERS

トリガーはORDERS完全に正常に機能します。新しい注文が挿入されると、その注文が属する顧客内で 次の固有のRECNOが割り当てられます。

一方、トリガーは、属するオーダー内で次の一意のRECNOORDERLINESを割り当てる必要がありますが、恐ろしい{ORA-04091: テーブル ORDERLINES が変化しています。トリガー/関数はそれを認識しない場合があります}例外をスローします。

動作するトリガーは次のとおりです。

CREATE OR REPLACE TRIGGER ORDERS_BI 
BEFORE INSERT ON ORDERS 
FOR EACH ROW
DECLARE
    CUSTID  WAREHOUSES.CUSTOMERID%TYPE;
BEGIN
    SELECT MIN(CUSTOMERID) INTO CUSTID FROM WAREHOUSES 
        WHERE NVL(WARE_ID, '-') = NVL(:NEW.WAREHOUSEID, '-');

    SELECT NVL(MAX(RECNO), 0) + 1
        INTO :NEW.RECNO
        FROM deploy.ORDERS O
        LEFT JOIN deploy.WAREHOUSES W
            ON NVL(W.REC, '-') = NVL(O.WAREHOUSEID, '-')
        WHERE NVL(W.CUSTOMERID, '-') = NVL(CUSTID, '-');
END;

そして、これが機能しないトリガーです:

CREATE OR REPLACE TRIGGER ORDERLINES_BI 
BEFORE INSERT ON ORDERLINES 
FOR EACH ROW
DECLARE
    nORDERID ORDERLINES.ORDERID%TYPE;
BEGIN
    SELECT MIN(ORDERID) INTO nORDERID FROM REVORDERS 
        WHERE ORDERID = :NEW.ORDERID;

    SELECT NVL(MAX(RECNO), 0) + 1
      INTO :NEW.RECNO
      FROM deploy.ORDERLINES L
      LEFT JOIN deploy.ORDERS O
        ON O.ORDERID = L.ORDERID
      WHERE O.ORDERID = nORDERID;
END;

誰かがなぜ最初のものは機能し、2番目のものは機能しないのか説明してもらえますか? そして、それを機能させるために2番目を書き直す方法はありますか?

4

2 に答える 2

4

あなたの説明ではなく、最初にあなたのコードを見ました。私が最初に思ったのは、「この人はシーケンスを偽造しようとしている」ということでした。これは明らかにあなたの質問に対する答えではありませんが、それがそもそもあなたがトラブルに巻き込まれている理由です.

シークエンスの偽造に問題がある場合の明らかな解決策は、実際のシークエンスを使用することです。

ニコラスがすでに指摘しているように、トリガーが起動されたテーブルから読み取ろうとすると、ORA-04091 が発生します。これを回避するにはさまざまな方法があります。ただし、エラーの根本原因には影響しません。それはあなたが何か間違ったことをしているということです。このエラーは通常、次の 2 つのいずれかまたは両方を示しています。

  1. トリガーにあまりにも多くのロジックを入れています
  2. データモデルに欠陥があります。

最初の解決策は、ロジックをパッケージに移動することです。これには、難読化のレイヤーを削除するという追加の利点があります。2 番目の解決策は、データベースを適切に正規化することです。

あなたの場合、あなたが提供した情報から、データモデルは問題ないように見えますが、私が言ったように、実装には同意しません。

これにより、問題を解決するための 4 つのオプションが残ります。

  1. トリガーを削除します。
  2. 現在のロジックをシーケンスに置き換えます。
  3. すべてのトリガー ロジックをプロシージャに削除します。
  4. エラーを回避します。

ポイント 3 については説明しません。自分でできるからです。Nicholas はポイント 4 を部分的にカバーしており、私が同意しないことを主張するつもりはありません。これでポイント 1 と 2 が残ります。

ORDERS では、RECNO は CUSTOMERS レコードの領域内で一意である必要があります。

これはあなたがそれを実装した方法ではありません。あなたのコードは、レコードの領域内でRECNO 連続します。CUSTOMERSの両方の主キーは、定義上、レコードの領域内で一意です。ORDERSORDERLINESCUSTOMERS

これ自体は、オプション 1 が最適であることを意味します。トリガーを完全に削除します。テーブルの主キーは、すでに必要なことをすべて実行しています。これもオプション 2 を無効にします。シーケンスを追加すると、基本的に別の主キーになります。

注文が各顧客内で連続して一意である必要があると考える理由はありません。なぜわざわざそうするのですか?

于 2012-09-09T10:05:59.393 に答える
2

変更中に 2 番目のトリガーがテーブルを読み取ろうとしているため、このエラーが発生しています。これは、親テーブルのトリガーによって、外部キーを参照する子テーブルに挿入が行われた場合にも発生する可能性があります。簡単な回避策として、ビューを作成し、トリガーの代わりに使用してみてください。また、ミューテーションの問題に対処する方法の Tom の例も見てください。また、2 番目のトリガーをそのままにしておくと、your_table select .. from table に挿入すると、変更エラーが発生します。例えば:

この挿入は機能します

insert into ORDERLINES(column1, column2... columnN) 
  values(val1, val2,..., valN)

しかし、これはそうではありません。

insert into ORDERLINES(column1, column2... columnN) 
  select val, val..val from table 
于 2012-09-09T06:11:18.597 に答える