【发布时间】:2016-04-10 00:18:27
【问题描述】:
我正在本地运行 SQL Server 2014,以获取将部署到 Azure SQL V12 数据库的数据库。
我有一个存储业务实体对象的可扩展属性值的表,在本例中,三个表如下所示:
CREATE TABLE Widgets (
WidgetId bigint IDENTITY(1,1),
...
)
CREATE TABLE WidgetProperties (
PropertyId int IDENTITY(1,1),
Name nvarchar(50)
Type int -- 0 = int, 1 = string, 2 = date, etc
)
CREATE TABLE WidgetPropertyValues (
WidgetId bigint,
PropertyId int,
Revision int,
DateTime datetimeoffset(7),
Value varbinary(255)
CONSTRAINT [PK_WidgetPropertyValues] PRIMARY KEY CLUSTERED (
[WidgetId] ASC,
[PropertyIdId] ASC,
[Revision] ASC
)
)
ALTER TABLE dbo.WidgetPropertyValues WITH CHECK ADD CONSTRAINT FK_WidgetPropertyValues_WidgetProperties FOREIGN KEY( PropertyId )
REFERENCES dbo.WidgetProperties ( PropertyId )
ALTER TABLE dbo.WidgetPropertyValues WITH CHECK ADD CONSTRAINT FK_WidgetPropertyValues_Widgets FOREIGN KEY( WidgetId )
REFERENCES dbo.Widgets ( WidgetId )
所以你看到WidgetId, PropertyId, Revision是一个复合键,并且表存储了Values的整个历史(当前值是通过为每个WidgetId + PropertyId获取具有最大Revision数字的行来获得的。
我想知道如何将Revision 列设置为每个WidgetId + PropertyId 增加1。我想要这样的数据:
WidgetId, PropertyId, Revision, DateTime, Value
------------------------------------------------
1 1 1 123
1 1 2 456
1 1 3 789
1 2 1 012
IDENTITY 不起作用,因为它对表是全局的,同样适用于 SEQUENCE 对象。
更新我能想到一个使用INSTEAD OF INSERT触发器的可能解决方案:
CREATE TRIGGER WidgetPropertyValueInsertTrigger ON WidgetPropertyValues
INSTEAD OF INSERT
AS
BEGIN
DECLARE @maxRevision int
SELECT @maxRevision = ISNULL( MAX( Revision ), 0 ) FROM WidgetPropertyValues WHERE WidgetId = INSERTED.WidgetId AND PropertyId = INSERTED.PropertyId
INSERT INTO WidgetPropertyValues VALUES (
INSERTED.WidgetId,
INSERTED.PropertyId,
@maxRevision + 1,
INSERTED.DateTime,
INSERTED.Value,
)
END
(与在INSERT 操作之前或之后运行的普通INSERT-trigger 相比,INSTEAD OF INSERT 触发器在表上运行而不是任何 INSERT 操作)
我认为这将是并发安全的,因为所有INSERT 操作都有一个隐式事务,并且任何关联的触发器都在同一个事务上下文中执行,这应该意味着它是安全的。除非任何人都可以提出其他要求?
【问题讨论】:
-
如果您有
IDENTITY,您可以使用row_number()窗口函数根据插入顺序枚举修订。 -
或者使用序列来执行@ConsiderMe 建议,而不向表本身添加字段
-
@Dai 如果您的表中已经有数据,您需要某种列才能分配正确的修订号。将此类信息存储在多用户环境中会因为并发性而导致问题。不过,有一些解决方法,包括将正在应用修订的相关部分存储在另一个表中并获取行级锁。
-
@ConsiderMe 实际上,这没有实际意义。我决定使用非连续的
Revision值并改用IDENTITY。 -
@Dai 就触发解决方案而言,选择和插入之间存在时间间隔 - 这就是它可能中断的原因。
标签: sql-server database-design auto-increment