Sql server 依赖SQL Server的标识—是否存在这样的情况?

Sql server 依赖SQL Server的标识—是否存在这样的情况?,sql-server,sql-server-2008,Sql Server,Sql Server 2008,我使用SQLServer2008R2 我正在寻找一个我描述为依赖身份的特性 我会举例说明 考虑这样一个表: 剧本 CREATE TABLE [dbo].[Rooms]( [RoomID] [int] NOT NULL, [ItemID] [int] NOT NULL, [ItemDescription] [nvarchar] (250)) GO 数据: (有人能告诉我们如何在这里设置示例表的格式吗?) 我希望能够声明如下所示的从属标识列: ItemID[int]标识(R

我使用SQLServer2008R2

我正在寻找一个我描述为依赖身份的特性

我会举例说明

考虑这样一个表:

剧本

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

(有人能告诉我们如何在这里设置示例表的格式吗?)

我希望能够声明如下所示的从属标识列:

ItemID[int]标识(RoomID,1,1)不为空

[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]=@roomID)
    ,'Chair #1')
GO
RoomID ItemID ItemDescription Id
1      6      Test            6
1      7      Test            9
1      8      Test            902
1      9      Test            903
那么,有这样一个特点吗


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

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

将表更改为具有主键,并允许ItemID为null

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_建议的那样,我已经使用了10个线程的SQL查询压力来查看这个触发器在加载时会发生什么。我根本没有得到任何副本(使用默认的隔离级别),但我确实得到了我预期的死锁负载

使用问题的原始查询,我得到了许多重复的问题

使用触发器方法,我得到死锁和如下结果:

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]=@roomID)
    ,'Chair #1')
GO
RoomID ItemID ItemDescription Id
1      6      Test            6
1      7      Test            9
1      8      Test            902
1      9      Test            903
这里ItemID是连续的,但1000行中有900行未能插入,在Id中留下了很大的间隙

如果我们添加以下索引:

CREATE UNIQUE NONCLUSTERED INDEX [IX_Rooms] ON [dbo].[Rooms] 
(
    [RoomID] ASC,
    [ItemID] ASC
)
为了保证不存在重复项,并提高计算特定RoomID的Max(ItemId)的性能,现在:

  • 问题的原始查询会导致重复,并且只插入500行
  • 使用默认隔离级别的触发器版本成功,没有任何死锁或错误,运行速度非常快
使用隔离级别为serializable的触发器会返回死锁,因此只有40%的插入成功(但不会因重复而出现异常)


作为最后一次测试,使用触发器+50个线程+隔离级别=默认值没有错误

目前,在SQL Server 2008 R2中-很遗憾,没有。使用SQL Server 2012,我们可以得到一些帮助—它就像一个“独立的”
IDENTITY
,不绑定到表的列。正确获取“手动”序列并不像从表中选择
MAX(ID)+1
那么容易—这种方法保证在即使是中等繁忙的系统中也能生成重复的序列。通过正确的锁定和事务处理来实现这一点是一门科学——Remus Rusanu已经完善了这一点,而这在负载下是绝对不安全的。。。。如果您的负载很轻,那么新的
ItemID
也会有重复项-不要这样做。请参阅我对原始问题的评论,其中有一个链接,指向前一个SO问题的答案,该问题显示了如何正确地、安全地、无风险地执行此操作duplicates@marc_s:我可以理解这会导致锁定问题。但是你确定如果隔离级别是可序列化的,它会导致重复吗?marc_s-我不知道重复是如何发生的,但我知道触发器如何更新相同的最后一次插入两次@Phil-我已将ItemID更改为1开头,并且它可以正常工作。如果两个用户几乎同时插入一行,我如何确保不会错过insert语句?假设user1插入一行,在触发器触发之前,另一个用户插入另一行。我错了吗?最后一次插入时触发器不会触发两次?换句话说,插入和触发器之间的链接是否可以一起运行?如何将insert和触发器一起隔离?如果您使用的是SET TRANSACTION ISOLATION LEVEL SERIALIZABLE,那么重复项应该不会有问题,但我很高兴被证明是错误的。@phil-您是对的。将隔离度提高一个级别没有帮助,甚至会导致更多键/从属键切换。提高两个级别会导致死锁。降低一个级别会减少开关,但它们仍然会被创建。在插入之前启动提升的隔离级别,并在触发器末尾移回默认隔离级别会导致死锁(所有插入都没有提交!)。我看不到出路,不过还是要谢谢你。