我们正在构建围绕数据库架构设计的应用,其中我们使用IDENTITY
列作为主键。已经确定客户需要更多用户友好的"标识符 - 表单YYYYDDDNNNN
的日期字符串,其中YYYY
是年份,DDD
是一年中的某一天,NNNN
是一天内的序列,从1开始
我们决定保留当前IDENTITY
作为父表的主键,并保留与子表的外键关系,而不是重新处理每个子表以使用这个新的复杂键。我们只是在父表中添加了一个新的标识符列。问题是 - 我们希望在插入新行时自动填充此字段。
一个适度的并发症 - 我们有多个客户,他们将有重叠的序列。 1月1日,每个人都会有一个标识为20140010001
的记录。
因此,我们创建了一个包含CustomerId
,LastUpdateDate
,LastSequenceNumber
的序列表。
传递CustomerId
并返回格式化标识符的存储过程,更新序列表以跟踪已经使用的内容(这需要更新序列表,这就是为什么它不是函数)
最后,我们在父记录上有一个AFTER INSERT
触发器:
ALTER TRIGGER [dbo].[tr_incidentnumber]
ON [dbo].[IMincident]
AFTER INSERT
AS
BEGIN
SET NOCOUNT ON;
DECLARE cur CURSOR FOR
SELECT customerid, incidentid
FROM inserted
FOR UPDATE OF incidentnumber
DECLARE @customerid NVARCHAR(32)
DECLARE @incidentid int
DECLARE @incidentnumber NVARCHAR(12)
OPEN cur
FETCH NEXT FROM cur
INTO @customerid, @incidentid
WHILE (@@FETCH_STATUS = 0)
BEGIN
EXECUTE sp_getIMID @customerid, @incidentnumber OUTPUT
UPDATE IMincident
SET incidentnumber = @incidentnumber
WHERE CURRENT OF cur
FETCH NEXT FROM cur
INTO @customerid, @incidentid
END
CLOSE cur
DEALLOCATE cur
END
对于需要游标的触发器来说,这看起来非常简单(我们需要一个游标,因为我们为每一行调用一个存储过程,并且它必须是一个存储过程,因为函数无法更新数据库)
但是,它不起作用。我收到一个错误:
Msg 16929,Level 16,State 1,Procedure tr_incidentnumber,Line 34
光标只读。
该语句已终止。
我不明白为什么。我可以在网上找到这个错误的最佳解释:http://support.microsoft.com/KB/158773
据我所知,如果基础表没有主键或唯一索引,我的光标将是READ ONLY。但我的表确实有一个主键。我甚至放弃了主键(以及依赖它的六个外键),然后重新创建它,我仍然得到错误。
表格本身很简单:
CREATE TABLE [dbo].[IMincident](
[customerid] [nvarchar](32) NOT NULL,
[incidentid] [int] IDENTITY(1,1) NOT NULL,
[createdbyoperatorid] [nvarchar](32) NOT NULL,
[createddt] [datetime] NOT NULL,
[currentrevisionnumber] [int] NOT NULL,
[incidentnumber] [nvarchar](32) NULL,
CONSTRAINT [PK_IMincident] PRIMARY KEY CLUSTERED
(
[incidentid] ASC
)
) ON [PRIMARY]
那么,为什么我应该收到16929错误?
答案 0 :(得分:0)
我找到一种比光标更好的方法,因为它们往往很慢。此外,您在触发器中插入和删除的表格都是虚拟的,也许您可以将SP调用推断为已加入插入和/或删除的选择,并立即执行所有更新。根据变更集的大小,您可以轻松创建超时/锁定问题