sql >> データベース >  >> RDS >> Sqlserver

tsqlでは、同時実行の観点から安全なSelectステートメントを含むInsertはありますか?

    パウロが書いているように:いいえ、それは安全ではありません 、経験的証拠を追加したい:テーブルを作成するTable_1 1つのフィールドID 値が0の1つのレコード 。次に、2つのManagementStudioクエリウィンドウで同時に次のコードを実行します。 :

    declare @counter int
    set @counter = 0
    while @counter < 1000
    begin
      set @counter = @counter + 1
    
      INSERT INTO Table_1
        SELECT MAX(ID) + 1 FROM Table_1 
    
    end
    

    次に実行

    SELECT ID, COUNT(*) FROM Table_1 GROUP BY ID HAVING COUNT(*) > 1
    

    私のSQLServer2008では、1つのID(662 )は2回作成されました。したがって、単一のステートメントに適用されるデフォルトの分離レベルは not 十分です。

    編集:明らかに、INSERTをラップします BEGIN TRANSACTIONを使用 およびCOMMIT トランザクションのデフォルトの分離レベルはまだREAD COMMITTEDであるため、修正されません 、これでは不十分です。トランザクション分離レベルをREPEATABLE READに設定することに注意してください 十分ではありません。上記のコードを安全にする唯一の方法 追加することです

    SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
    

    頂点で。ただし、これにより、テストでデッドロックが発生することがありました。

    編集:私が見つけた唯一の安全な解決策は (少なくとも私のテストでは)デッドロックを生成しないのは、テーブルを明示的に排他的にロックすることです(ここではデフォルトのトランザクション分離レベルで十分です)。ただし、注意してください。このソリューションは殺すかもしれません パフォーマンス:

    ...loop stuff...
        BEGIN TRANSACTION
    
        SELECT * FROM Table_1 WITH (TABLOCKX, HOLDLOCK) WHERE 1=0
    
        INSERT INTO Table_1
          SELECT MAX(ID) + 1 FROM Table_1 
    
        COMMIT
    ...loop end...
    


    1. PostgreSQLで既存のテーブルに自動インクリメントの主キーを追加するにはどうすればよいですか?

    2. Oracleの関数とプロシージャ

    3. Powershellを使用してリモートPostgreSqlデータベースに接続します

    4. SQL Serverネットワークインターフェイス:接続文字列が無効です[87]