私は最近、Oracleデータベースでプログラミングするという奇妙な問題に遭遇しました.シリアライズ可能なトランザクション内で、一括挿入(INSERT ... SELECT)を実行し、その直後に、変更されたテーブルでSELECTを使用してカーソルを開きます。このカーソルには新しく挿入された行が含まれると思いましたが、驚いたことに、その内容は不安定で、新しく挿入されたすべての行を含むこともあれば、サブセットのみを含むこともあります。
カーソルを開く前にコミットすることでこの問題を解決しましたが、その動作に戸惑いました。同じトランザクション内での挿入後の選択は、介入コミットなしで実際に信頼できますか? それとも、この動作はトランザクションがシリアライズ可能であることに何らかの形で関連していますか?
フォローアップ: 再現可能なテスト ケースを作成しようとしたときに、インデックスを追加した後にのみこの動作を取得できました (この場合は主キー インデックスで、実際のコードでは通常のインデックスでした)。おそらく問題はインデックスの構築に費やされた時間にあるので、SELECT は実際には不完全なインデックスを使用して結果を取得しますか? とにかく、ここに再現可能なテストケースがあります:
-- Create empty source table
CREATE TABLE TEST_CASE_1 AS
(SELECT 'CONTENT' AS CONTENT
FROM DUAL
WHERE 1 = 2)
-- Add primary key
ALTER TABLE TEST_CASE_1
ADD CONSTRAINT TEST_CASE_1_PK PRIMARY KEY (CONTENT);
-- Create empty destination table
CREATE TABLE TEST_CASE_2 AS
(SELECT 'CONTENT' AS CONTENT
FROM DUAL
WHERE 1 = 2)
-- Example of faulty code
BEGIN
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
-- Populate with 100.000 rows (I used ALL_OBJECTS but any source of 100.000 rows is good)
INSERT INTO TEST_CASE_1
(SELECT ROWNUM
FROM ALL_OBJECTS
WHERE ROWNUM <= 100000);
INSERT INTO TEST_CASE_2
(SELECT *
FROM TEST_CASE_1
WHERE CONTENT > 0);
COMMIT;
END;
この例では、TEST_CASE_2 にも 100.000 行あると予想されます。このテスト ケースを (負荷のないデータベースで) 再現すると、約 400 ~ 500 行が挿入されました。トランザクションをシリアライズ可能に設定するステートメントを削除すると、正しい 100.000 行カウントが得られました。