仕様により、各テーブルは主キーとしてGUIDを使用します。別の要件は、各リソースが6つの番号識別子(100001、100002、...)を、リソースを所有する顧客(Resource -> Location -> Customer
)。
挿入後トリガーを使用して、この6つの数値を設定しようとしました。ロジックは正常に機能しますが、着信する多くの挿入をシミュレートするとデッドロックが発生します。tablockx
ヒントを挿入に追加することで、このデッドロックを解消できます。ただし、Linq2SQLを使用してデータを挿入するので、挿入にロックヒントを追加する必要はありません。代わりに、トリガーのロックヒントまたはインデックスヒントのいくつかの組み合わせが機能することを望んでいました。
スキーマ:
CREATE TABLE [Resource] (
[pkGUID] UNIQUEIDENTIFIER NOT NULL,
[nIdentifier] INT NOT NULL DEFAULT 0,
[fkLocation] UNIQUEIDENTIFIER NOT NULL,
CONSTRAINT [PK_Resource] PRIMARY KEY CLUSTERED ([pkGUID] ASC)
)
CREATE TABLE [Location] (
[pkGUID] UNIQUEIDENTIFIER NOT NULL,
[fkCustomer] UNIQUEIDENTIFIER NOT NULL,
CONSTRAINT [PK_Location] PRIMARY KEY CLUSTERED ([pkGUID]
)
CREATE TABLE [Customer] (
[pkGUID] UNIQUEIDENTIFIER NOT NULL,
CONSTRAINT [PK_Customer] PRIMARY KEY CLUSTERED ([pkGUID]
)
現在、クラスター化された主キーを除いて、Resource
テーブルにはインデックスがありません。
引き金:
CREATE TRIGGER [dbo].[Trigger_Resource_Insert]
ON [dbo].[Resource]
FOR INSERT
AS
BEGIN
SET NoCount ON
;with relevantCustomers as (
select c.pkGUID
from inserted i
join Location l on i.fkLocation = l.pkGUID
join Customer c on l.fkCustomer = c.pkGUID
group by c.pkGUID
), maxNumbers as (
select rc.pkGUID, max = case when max(x.nIdentifier) < 100000 then 100000 else max(x.nIdentifier) end
from relevantCustomers rc
join Location l on l.fkCustomer = rc.pkGUID
join Resource x on x.fkLocation = l.pkGUID
group by rc.pkGUID
), numbers as (
select i.pkGUID, number = row_number() over (partition by n.pkGUID order by i.pkGUID) + n.max
from inserted i
join Location l on i.fkLocation = l.pkGUID
join maxNumbers n on n.pkGUID = l.fkCustomer
)
update x
set nIdentifier = n.number
from Resource x
join numbers n on x.pkGUID = n.pkGUID
END
一連のデータを生成する挿入:
;with x as (
select num = 1
union all
select x.num + 1
from x
where x.num < {0}
), l as (
select top {1} fkLocation = pkGUID, sLocationName from Location order by sLocationName
)
insert into Resource with (tablockx) (pkGUID, fkLocation, ...)
select newid(), l.fkLocation, ...
from x
cross join l
tablockx
ヒントなしで挿入を実行すると、SQLプロファイラからのデッドロックグラフ:
私は、あなたが見ているものは必ずしもフレームワークの問題であると述べているコメントの一部にはまったく同意しないと言わざるを得ません。 Linq to SQLフレームワーク ほとんどの場合そうです では、ストアドプロシージャを行の入力手段として指定できます。
そうは言っても、可能であれば、トリガーの代わりにそのメカニズムを使用することを強くお勧めします。ストアドプロシージャ内にロックヒントを追加でき、挿入は正常に機能します。
しかし、あなたの仕様は私には非常に疑わしいようです。IMOで現在発生していると思われる問題は、このような設計を追求したくない理由の良い反例です。だから私の純粋な修辞的な質問はデザイナーのためのものです。問題のある連番は何を表しているのですか?それが時間のシーケンスである場合は、時間を使用し、ウィンドウ関数を使用してビューのようなもので行を並べ替えないのはなぜですか(LinqからSQLをビューにポイントすることもできます)?なんらかのギャップのない連番を付けるつもりですか?その数を維持するためだけに対処する必要がある複雑さとロックのすべてにサインオンしていますか?
残念ながら、本当の答えはありません-フレームワークとデザイン全体で機能するヒントの魔法の組み合わせはありません-あなたの問題を解決します。 IMO私の答えは一歩下がって、あなたのデザインを批判的に見ることです。
トリガーにはコードが多すぎるようです。
一歩下がって、達成しようとしていることを見てみましょう。
挿入したレコードにnIdentifierを設定したい。
私の解決策は、トリガーをまったく使用しないことです。