1

新しいレコードが挿入されたときにテーブルの主キー列を自動インクリメントする Oracle シーケンスとトリガーを作成しました。これが私のコードです:

CREATE TABLE MOBILE_APP
(
  "MOBILE_APP_ID" NUMBER(9, 0) PRIMARY KEY,
  "NAME" VARCHAR2(60) NOT NULL,
  "DESCRIPTION" VARCHAR2(200),
  CONSTRAINT name_unique UNIQUE (name)
);

CREATE SEQUENCE MOBILE_APP_ID_SEQ
MINVALUE 1
MAXVALUE 999999999
INCREMENT BY 1
START WITH 1
NOCACHE
ORDER
NOCYCLE;

CREATE TRIGGER MOBILE_APP_BR_I
BEFORE INSERT ON MOBILE_APP
FOR EACH ROW
BEGIN
  SELECT MOBILE_APP_ID_SEQ.NEXTVAL INTO :NEW.MOBILE_APP_ID FROM dual;
END;

私のトリガーは「挿入前」であるため、レコードが実際にテーブルに挿入される前に実行されます。しかし、挿入中に一意の制約違反が発生した場合でも、トリガーが実行されるとは思っていませんでした。テーブル、シーケンス、およびトリガーがすべて新しく、次のステートメントを 2 回実行しようとするとします。

INSERT INTO MOBILE_APP (name, description) VALUES ('Name', 'Desc');

最初の実行は正常に完了し、挿入されたレコードの「mobile_app_id」フィールドに値 1 が自動入力されます。予想どおり、2 回目の実行では、「名前」フィールドに関連する一意の制約違反でエラーが発生します。しかし、一意の制約に違反せずに別のレコードを挿入すると、挿入されたレコードの「mobile_app_id」フィールドに値 3 が自動入力されます。つまり、一意の制約違反のために失敗した挿入の試行中に、シーケンスはまだ 1 から 2 にインクリメントされています。これを防ぐにはどうすればよいですか? この他の投稿を見つけましたが、残念ながら問題の解決策は含まれていません。どんな助けでも大歓迎です!

4

5 に答える 5

3

これはシーケンスの正常な動作です。ギャップのないシーケンスがあるとは限りません。複数のトランザクションがすべて実行されている可能性があり、一部はコミットされ、一部はロールバックされる可能性があるため、希望どおりにギャップのないシーケンスを提供するシーケンスをどのように作成できるかさえわかりません。

そのようなギャップのないシーケンスが本当に必要な場合は、同じトランザクションでインクリメントされることを確認する必要があります。

CREATE TABLE my_id ( col1 NUMBER );
INSERT INTO my_id ( col1 ) VALUES ( 0 );

次に、トランザクションを実行します。

DECLARE
    id NUMBER;
BEGIN
  UPDATE my_id SET col1 = col1 + 1
  RETURNING col1 INTO id;

  INSERT INTO mobile_app ... whatever ...

  COMMIT;
END;

これに関する大きな問題は、idテーブルがボトルネックになることです。異なるセッションから同時にmobile_appテーブルに挿入することはできません。

于 2012-10-04T23:56:20.173 に答える
1

提供されたすべてのコメントと回答に本当に感謝しています!提供された情報に基づいて、最善のアプローチはそのままにして、シーケンスがギャップレスのままではないことを期待することだと思います. 他の方法で進めるには、あまりにも多くの潜在的な不利益があるようです。

于 2012-10-05T16:28:01.243 に答える
0

すでに述べたように、シーケンスはシーケンスであり、ギャップレス シーケンスが必要な場合には適していません。最初に考えるのは、ギャップのないシーケンスが必要であると確信していますか? もしそうなら、提案された解決策のどちらも、ギャップに対して 100% 安全ではありません。最初のセッションが変更をコミットする前に、別のセッションが ID を取得すると、どちらも問題が発生します。2 番目のセッションは最初のセッションと同じ ID を持つことになり、もちろん問題が発生します。このリスクはそれほど大きくはないはずですが、アクティブなセッションが多数ある場合は特にそうです。

完全にギャップのないシーケンスを取得するのは簡単ではありません。1 つの方法は、たとえば、提案されたテーブル ソリューションのようなものを使用してから、重複キー エラーをトラップしてから、再試行して挿入することです。良くないし、効率的でもないが、それはタスクを成し遂げるはずだ。

于 2012-10-05T12:29:10.340 に答える
0
  1. insert ステートメント内にシーケンス番号を挿入するだけです。

    INSERT INTO MOBILE_APP (MOBILE_APP_ID,name, description) VALUES (MOBILE_APP_ID_SEQ.NEXTVAL ,'Name', 'Desc');

  2. 別のアプリケーションを使用してこの挿入を行う場合は、最初に ID を取得してから、insert ステートメントおよびその他のプロセスに渡します。

       --Create Id.
       CREATE OR REPLACE PROCEDURE MOBILE_APP_ID
       (MOBILE_APP_ID OUT SYS_REFCURSOR )
       IS
          BEGIN
              open MOBILE_APP_ID for 
           SELECT MOBILE_APP_ID_SEQ.NEXTVAL MOBILE_APP_ID
           FROM dual;
    
       END ;
    
      --Then get the id.
      int id = MOBILE_APP_ID.MOBILE_APP_ID;
    
     --Then insert the id.
     INSERT INTO MOBILE_APP (MOBILE_APP_ID,name, description) VALUES (id  ,'Name', 'Desc');
    
于 2012-10-04T22:53:34.587 に答える
-1

ここでシーケンスを作成したり、以下のコード以外のものを作成したりする必要はないと思います

CREATE OR REPLACE TRIGGER AUTO_APPID
BEFORE INSERT
ON MOBILE_APP
REFERENCING NEW AS NEW OLD AS OLD
FOR EACH ROW
DECLARE
begin

SELECT NVL(MAX(MOBILE_APP_ID),0)+1 INTO :NEW.MOBILE_APP_ID FROM MOBILE_APP;

END ;
于 2012-10-05T08:52:43.180 に答える