在SQL Server中实现基于同一表中另一个字段自动递增的字段有什么好方法?想象一下VersionNumber
字段自动递增1
到n
,为列DocumentID
的每个值。
要求:
ROW_NUMBER
。重点是创建审计跟踪。 详情
我正在设计一个需要版本化数据的数据库。这种事情的基本结构是使用包含不变字段的标头表和包含以下内容的版本详细信息表:
诸如SQL Strategies for 'Versioned' Data和Yet Another SQL Strategy for Versioned Data之类的文章讨论了有效查询此类结构的策略,但没有讨论VersionNumber
字段的实际实现(第二篇文章提供了部分示例,但是为“高级练习”提供更全面的实施。)
示例DDL:
CREATE TABLE Invoice (
InvoiceID bigint identity(1,1) not null primary key,
CreateDate date not null
);
CREATE TABLE InvoiceVersion (
InvoiceVersionID bigint identity(1,1) not null primary key,
InvoiceID bigint not null,
VersionNumber int not null
Other Fields...
CONSTRAINT FK_InvoiceVersion_Invoice
FOREIGN KEY ( InvoiceID )
REFERENCES Invoice ( InvoiceID )
);
CREATE UNIQUE INDEX UQ_InvoiceVersion
ON InvoiceVersion ( InvoiceID, VersionNumber )
;
我的部分解决方案解决了插入时增加版本号的问题,但不会阻止特定版本的选择性删除,或阻止VersionNumber
的直接更新。不过,如果所有这些都不需要三个50线触发器,我会喜欢它。
CREATE TRIGGER TRG_InvoiceVersion_Insert
ON InvoiceVersion
INSTEAD OF INSERT
AS
BEGIN
-- Prevent explicit VersionNumber insert
DECLARE @ExplicitVersionCount int;
SELECT @ExplicitVersionCount = COUNT(*)
FROM inserted
WHERE VersionNumber IS NOT NULL;
IF (@ExplicitVersionCount > 0) BEGIN
RAISERROR ('Explicit version number insert not allowed', 15, 1);
END
-- Get the current version number (implicit in row count)
DECLARE @InsertingInvoiceID TABLE (
InvoiceID bigint not null primary key
);
DECLARE @CurrentVersions TABLE (
InvoiceID bigint not null primary key,
VersionCount int
);
INSERT INTO @InsertingInvoiceID ( InvoiceID )
SELECT DISTINCT i.InvoiceID
FROM inserted i;
INSERT INTO @CurrentVersions ( InvoiceID, VersionCount )
SELECT I.InvoiceID, COUNT(V.InvoiceID)
FROM @InsertingInvoiceID I
LEFT JOIN migrate.InvoiceVersion V
ON V.InvoiceID = I.InvoiceID
GROUP BY I.InvoiceID
;
INSERT INTO InvoiceVersion (
InvoiceID
, VersionNumber
, [Other fields...]
)
SELECT
i.InvoiceID
, v.VersionCount + ROW_NUMBER() OVER (PARTITION BY i.InvoiceID ORDER BY i.InvoiceID)
, [Other fields...]
FROM inserted i
INNER JOIN @CurrentVersions v
ON i.InvoiceID = v.InvoiceID
;
END;
答案 0 :(得分:1)
我很想在视图中动态计算它。使用您可以使用的日期/ ID来自动识别版本信息。
忽略VersionNumber
中的InvoiceVersion
列,并使用默认值投入日期时间,并使用窗口函数为您计算版本(一旦感到舒适,请将其设为视图)。
InvoiceID
的分区,DateAdded
到InvoiceVersion
表的排序,以及{2}同时添加InvoiceVersionID
的分区。
应该在前端强制执行对已删除内容的控制,但可以通过删除后触发的触发器来完成,如果不是全部删除则恢复数据。您可以从触发器中已删除的表中获取InvoiceID
(s),如果InvoiceID
表中仍存在InvoiceVersion
(s),则回滚并引发错误。
另一种选择,而不是实际删除,只是添加"删除"位列到Invoice
表。将发票标记为"已删除",但从不将其从表中删除。然后在您的视图中,加入Invoice
表,然后添加where deleted=0
。
希望这有帮助。
查看示例:
IF OBJECT_ID('TEMPDB..#Invoice') IS NOT NULL DROP TABLE #Invoice
IF OBJECT_ID('TEMPDB..#InvoiceVersion') IS NOT NULL DROP TABLE #InvoiceVersion
CREATE TABLE #Invoice (
InvoiceID bigint identity(1,1) not null primary key,
CreateDate datetime not null
);
CREATE TABLE #InvoiceVersion (
InvoiceVersionID bigint identity(1,1) not null primary key,
InvoiceID bigint not null,
Col1 varchar(100),
Col2 int,
DateAdded datetime default CURRENT_TIMESTAMP
);
-- CREATE Invoice#1 & Invoice#2
INSERT INTO #Invoice (CreateDate) SELECT CURRENT_TIMESTAMP;
INSERT INTO #Invoice (CreateDate) SELECT CURRENT_TIMESTAMP;
-- CREATE 1 "version" for Invoice#1 & 4 for Invoice#2 & then 1 more for Invoice#1
INSERT INTO #InvoiceVersion (InvoiceID, Col1, Col2) SELECT 1, 'TEST 1', 543
INSERT INTO #InvoiceVersion (InvoiceID, Col1, Col2) SELECT 2, 'TEST 2', 34
INSERT INTO #InvoiceVersion (InvoiceID, Col1, Col2) SELECT 2, 'TEST 2', 242
INSERT INTO #InvoiceVersion (InvoiceID, Col1, Col2) SELECT 2, 'TEST 2', 965
INSERT INTO #InvoiceVersion (InvoiceID, Col1, Col2) SELECT 2, 'TEST 2', 184
INSERT INTO #InvoiceVersion (InvoiceID, Col1, Col2) SELECT 1, 'TEST 1', 684
--SELECT * FROM #Invoice
--SELECT * FROM #InvoiceVersion
--CREATE VIEW VersionedInvoice
--AS
SELECT *, ROW_NUMBER() OVER(PARTITION BY InvoiceID ORDER BY InvoiceID, DateAdded, InvoiceVersionID) as VersionNumber
FROM #InvoiceVersion
答案 1 :(得分:1)
这是我会尝试的选项。创建INSTEAD OF触发器以防止在表InvoiceVersion上直接插入,更新或删除。 然后创建一个“插入”存储过程,将新版本添加到InvoiceVersion表。 创建“删除”存储过程,按版本ID删除发票的所有版本。 每个proc都将禁用相关的触发器,执行所需的DML,然后启用触发器(所有这些都在一个事务中)。
这对你有用吗?
--Don't allow inserts to InvoiceVersion.
CREATE TRIGGER insInvoiceVersion
ON InvoiceVersion
INSTEAD OF INSERT
AS
BEGIN
RAISERROR ('Direct inserts to table InvoiceVersion are not permitted. Use stored proc InsertInvoiceVersion instead.', 16, 1);
END
GO
--Don't allow updates to InsertInvoiceVersion.
CREATE TRIGGER updInvoiceVersion
ON InvoiceVersion
INSTEAD OF UPDATE
AS
BEGIN
RAISERROR ('Direct updates of table InvoiceVersion are not permitted.', 16, 1);
END
GO
--Don't allow deletes from InsertInvoiceVersion.
CREATE TRIGGER delInvoiceVersion
ON InvoiceVersion
INSTEAD OF DELETE
AS
BEGIN
RAISERROR ('Direct deletes from table InvoiceVersion are not permitted. Use stored proc DeleteInvoiceVersions instead.', 16, 1);
END
GO
CREATE PROCEDURE InsertInvoiceVersion
@InvoiceID BIGINT
--Other fields
AS
BEGIN
--Get the next version number. (This could be replaced with a UDF for convenience, if desired.)
DECLARE @VersionNumber INT
SELECT @VersionNumber = MAX(VersionNumber) +1
FROM InvoiceVersion
WHERE InvoiceID = @InvoiceID
--In case there are no existing versions of the invoice yet...
SELECT @VersionNumber = COALESCE(@VersionNumber, 1)
--Disable the insert trigger, insert, enable the insert trigger.
ALTER TABLE InvoiceVersion DISABLE TRIGGER insInvoiceVersion
INSERT INTO InvoiceVersion (InvoiceID, VersionNumber /*, other fields*/)
VALUES (@InvoiceID, @VersionNumber /*, other fields*/)
ALTER TABLE InvoiceVersion ENABLE TRIGGER insInvoiceVersion
END
GO
CREATE PROCEDURE DeleteInvoiceVersions
@InvoiceID BIGINT
AS
--Disable the delete trigger, delete, enable the delete trigger.
ALTER TABLE InvoiceVersion DISABLE TRIGGER delInvoiceVersion
DELETE FROM InvoiceVersion
WHERE InvoiceID = @InvoiceID
ALTER TABLE InvoiceVersion ENABLE TRIGGER delInvoiceVersion
GO