2012-05-26 32 views
4

我使用SQL Server 2008 R2。SQL Server依赖的标识 - 是否有这样的事情?

我正在寻找一种功能,我将其描述为从属身份。

我会通过一个例子来解释。

考虑这样一个表:

脚本

CREATE TABLE [dbo].[Rooms](
    [RoomID] [int] NOT NULL, 
    [ItemID] [int] NOT NULL, 
    [ItemDescription] [nvarchar] (250)) 
GO 

数据:

RoomID ItemID ItemDescription 
------ ------ --------------- 
7  1  Door 
7  2  Window (West) 
7  3  Window (North) 
8  1  Door 
8  2  Table #1 
8  3  Table #2 
7  4  Table #1 
8  4  Chair #1 
7  5  Table #2 
7  6  Table #3 
8  5  Chair #2 

(?谁能告诉秘密这里怎么形式的例表)

我希望能够像这样声明一个从属标识列:

ItemID [int] Identity(RoomID,1,1) NOT NULL

[Rooms]一个新行应触发测试的ItemID最高值,其中RoomID = @roomID再加1

而是更新与RoomID利用变化删除和插入所需数据。

现在我做到这一点编程这样的:

DECLARE @roomID INT 
SET @roomID = 7 
INSERT INTO [Allocation].[dbo].[Rooms] 
    ([RoomID], [ItemID], [ItemDescription]) VALUES (@roomID, 
    (SELECT max([ItemID])+1 FROM [Allocation].[dbo].[Rooms] WHERE [RoomID][email protected]) 
    ,'Chair #1') 
GO 

那么,有没有这样的功能?

在可能的情况下没有,我可以编程服务器为我自动设置下一个依赖身份,给定一个特定的表,父列和从属标识列?

+2

目前,在SQL Server 2008 R2 - 不,不幸的是。使用SQL Server ** 2012 **,我们将获得[sequences](http://sqlblog.com/blogs/aaron_bertrand/archive/2010/11/11/sql-server-11-denali-using-sequence.aspx ),这可能是一种帮助 - 它就像是一个“独立”的“IDENTITY”,而不是绑定到表格的列。 –

+0

从表格中选择“MAX(ID)+ 1”并不容易,“这样的方法可以保证在繁忙的系统中生成重复数据。 得到这个正确的,正确的锁定和事务处理是一门科学 - 一个Remus Rusanu已经完善和[显示在以前的SO问题的答案中](http://stackoverflow.com/questions/5083846/sql- server-2005-using-generated-sequences-instead-of-identity-columns) –

回答

1

您可以使用触发器和索引来提高性能并确保没有重复项。

改变你的表有一个主键,允许null作为项目ID

CREATE TABLE [dbo].[Rooms](
    [RoomID] [int] NOT NULL, 
    [ItemID] [int] NULL, 
    [ItemDescription] [nvarchar](250) NULL, 
    [Id] [int] IDENTITY(1,1) NOT NULL, 
    CONSTRAINT [PK_Rooms] PRIMARY KEY CLUSTERED 
    (
     [Id] ASC 
    ) 
) 

,然后添加一个触发器

CREATE TRIGGER RoomTrigger 
    ON Rooms 
    AFTER INSERT 
AS 
BEGIN 
    SET NOCOUNT ON; 

    update Rooms 
    set 
     ItemID = (select coalesce(MAX(itemid), 0) + 1 
         from Rooms r where r.RoomID = inserted.RoomID) 
    from 
     inserted where Rooms.Id = inserted.Id 

END 

然后你就可以做到这一点

insert into Rooms (RoomID, ItemDescription) values (1, 'Test') 
insert into Rooms (RoomID, ItemDescription) values (1, 'Test') 

这结果

RoomID ItemID ItemDescription Id 
2  0  Test   1 
2  1  Test   2 

正如marc_s所示,我使用了10个线程的SQL查询压力来查看加载时该触发器会发生什么。我根本没有得到任何重复(使用默认的隔离级别),但我确实得到了大量的死锁,正如我所预料的那样。

使用从问题中的原始查询,我得到了很多重复。

使用触发的方法,我得到死锁和结果是这样的:

RoomID ItemID ItemDescription Id 
1  6  Test   6 
1  7  Test   9 
1  8  Test   902 
1  9  Test   903 

这里的ItemID是连续的,但约900出1000行未能插入留下编号较大的差距。

如果再加上以下指标:

CREATE UNIQUE NONCLUSTERED INDEX [IX_Rooms] ON [dbo].[Rooms] 
(
    [RoomID] ASC, 
    [ItemID] ASC 
) 

为了保证没有重复,提高计算最大(项目Id)特定RoomID,那么现在的表现:

  • 的来自问题的原始查询会导致重复,并且只能管理插入500行。
  • 使用默认隔离级别的触发器版本成功没有任何死锁或错误并且运行速度非常快。

使用具有隔离级别= serializable的触发器会导致死锁,因此只有40%的插入成功(但由于重复而没有例外)。

作为使用触发器+ 50个线程+隔离级别=默认值尝试的最终测试。 没有错误

+1

这绝对是**不安全**在负载下....如果你的负载相当轻,你会得到**副本* *为新的'ItemID' - 不要这样做。看到我对原始问题的评论,并附有指向以前SO问题答案的链接,其中显示了如何正确安全地完成此操作,并且没有重复。 –

+0

@marc_s:我可以理解这会导致锁定问题。但是如果隔离级别是可序列化的,你确定它会导致重复吗? – Phil

+0

marc_s-我不明白可能会发生重复,但我确实看到触发器如何更新最后一次插入两次。 @菲尔 - 我已经改变了ItemID从1开始,它的工作原理。我怎样才能确定,如果两个用户几乎同时插入一行,我不会错过插入语句?比方说,user1插入一行,在触发器触发之前,另一个用户插入另一行。我错了,触发器最后一次插入不会触发两次吗?换句话说,插入和触发器之间是否可以一起运行?如何隔离插入和触发器? – Different111222

相关问题