By specification each table uses a guid as the primary key. Another requirement is that each resource requires a 6 number identifier (100001, 100002, ...) as a public id that is unique within the customer that owns the resource (related via Resource -> Location -> Customer
).
I have tried to set this 6 number using an after insert trigger. The logic works fine but causes deadlocks when I simulate many inserts coming in. I can eliminate this deadlock by adding a tablockx
hint to the insert. However since Linq2SQL will be used to insert data I would like to avoid having to add a locking hint to the insert. I was hoping some combination of locking hints or index hints in the trigger might work instead.
The schema:
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]
)
Currently there are no indexes on the Resource
table except the clustered primary key.
The trigger:
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
An insert generating a bunch of data:
;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
Deadlock graph from SQL profiler when running inserts without tablockx
hint:
maxNumbers
CTE.