13

以下の記述:

INSERT INTO dbo.Changes([Content], [Date], [UserId], [CompanyId]) 
  VALUES (@1, @2, @3, @4);
SELECT @@identity;

このSQLエラー3960が表示されます:

更新の競合により、スナップショット分離トランザクションが中止されました。スナップショット分離を使用してデータベース 'myDatabase' のテーブル 'dbo.Companies' に直接または間接的にアクセスし、別のトランザクションによって変更または削除された行を更新、削除、または挿入することはできません。トランザクションを再試行するか、更新/削除ステートメントの分離レベルを変更してください。

私が理解している限り、エラーメッセージから、dbo.Companies別の接続が変更されている間は、テーブルを更新、削除、または挿入しないでくださいdbo.Companies

しかし、別のテーブルdbo.Changes( への外部キーを持つdbo.Companies) に新しい行を挿入していて、 で参照されている行を削除していなかったのに、主キーではなく でdbo.Companies行を更新しただけだったのはなぜですか? dbo.Companiesこれはうまくいくはずですよね?(SQL Server のバグですか?)

アップデート:

テーブルは次のようになります。

dbo.Changes([Id] int PK, [Content] nvarchar, 
  [Date] datetime, [UserId] int, [CompanyId] int -> dbo.Companies.[Id])
dbo.Companies([Id] int PK, [Name] nvarchar)

2番目の更新は行っています:

UPDATE dbo.Companies WHERE [Id] = @1 SET [Name] = @2;
4

2 に答える 2

8

SQL Serverは、レコードを変更しなくても、読み取らなければならないすべてのレコードに対して更新ロックを取得するようです。

このmicrosoft.public.sqlserver.server スレッドの詳細:

CustomerContactPerson のサポート インデックスがない場合、ステートメントは

ContactPerson WHERE ID = @ID; から削除します。

削除された ContactPerson 行を参照する CustomerContactPerson 行がないことを確認するために、CustomerContactPerson のすべての行の「現在の」読み取りが必要になります。インデックスを使用すると、DELETE は、他のトランザクションの影響を受ける行を読み取らずに、CustomerContactPerson に関連する行がないと判断できます。

さらに、スナップショット トランザクションでは、ターンアラウンドして更新しようとしているデータを読み取るためのパターンは、読み取り時に UPDLOCK を取ることです。これにより、「一貫性のある」(スナップショット) データではなく、「現在の」データに基づいて更新を行うことが保証され、DML を発行するときにデータがロックされず、無意識のうちに上書きされることがなくなります。別のセッションの変更。

私たちの修正は、外部キーにインデックスを追加することでした

あなたの例では、Changes.CompanyId にインデックスを追加すると役立つと思われます。これが本当の解決策であるかどうかはわかりません。SQL Server オプティマイザーは、インデックスを使用しないことを選択できますか?

于 2012-12-11T16:30:27.360 に答える
3

SQL Serverは、挿入の動作を変更できる依存テーブルへの更新を確認できます... SQLは[name]列に依存している可能性のある他のロジックを推測できないため、私には公平に思えます(トリガーなど)

アプリケーションがデッドロック再試行ロジックを実装している場合、エラー番号 3960 をエラー番号 1205 と同じように処理し、自動的に再試行するように変更できます ...

于 2012-07-14T09:29:09.430 に答える