Dai*_*Dai 0 sql-server azure-sql-database temporal-tables
我有一张表存储客户信息(您的沼泽标准 CRM 资料):
CREATE TABLE dbo.Customers (
TenantId int NOT NULL,
CustomerId int NOT NULL,
FirstName nvarchar(50) NOT NULL DEFAULT '',
LastName nvarchar(50) NOT NULL DEFAULT '',
CompanyName nvarchar(50) NOT NULL DEFAULT '',
Notes nvarchar(4000) NOT NULL DEFAULT ''
)
Run Code Online (Sandbox Code Playgroud)
此表最近已转换为 SQL Server 时态表:
ALTER TABLE dbo.Customers
ADD COLUMN
SysStart datetime2(7) GENERATED ALWAYS AS ROW START NOT NULL,
SysEnd datetime2(7) GENERATED ALWAYS AS ROW END NOT NULL;
GO
ALTER TABLE dbo.Customers WITH (
PERIOD FOR SYSTEM_TIME ( SysStart, SysEnd ),
SYSTEM_VERSIONING = ON ( HISTORY_TABLE = dbo.Customers_History )
)
Run Code Online (Sandbox Code Playgroud)
这样做之后,出现了一个问题:用户编辑客户详细信息的面向用户的表单会在最后一次击键后 2 秒自动保存表单内容,每次保存都会导致一条UPDATE
数据量逐渐增加的语句——SQL Server 也是如此不断地向Customers_History
表中添加新行,间隔 2 秒,这会导致垃圾邮件、低信息行。
所以这就是我在做 a 时看到的SELECT * FROM dbo.Customers_History WHERE CustomerId = 123
,例如:
我在想,处理自动保存提交的服务器端代码可以检查传入的表单状态是否是增量更改,如果是,UPDATE
则dbo.Customers
表不添加任何行到Customers_History
表中。
到目前为止,我能看到SYSTEM_VERSIONING = OFF
的唯一方法是在事务中设置,但是在我实际实施之前我有一些问题......
虽然文档说(甚至推荐)ALTER TABLE dbo.Customers SET (SYSTEM_VERSIONING = OFF);
在事务内运行,但它没有说明这将如何影响其他并发用户和连接。毕竟这是一个 DML 语句,并且 DML 语句具有不一致的(哈!)行为 wrt 隔离。
在存储PROCEDURE
或 SQL 批处理中启动和完成事务是一回事 - 但确定一个事务UPDATE
是否实际上是增量的可能需要一些自定义的应用程序代码逻辑,这意味着必须从应用程序代码启动和提交事务,这看起来像一个坏主意,因为现在有成千上万的事情可能会出错,更不用说性能问题(在这种情况下,从应用程序服务器到数据库服务器的网络延迟应该小于 1 毫秒,但这仍然会影响可扩展性)。
假设我可以在 a 中完成PROCEDURE
,下面的代码是否正确并且可以扩展?
CREATE PROCEDURE dbo.UpdateSingleCustomerRow(
@tenantId int,
@customerId int,
@firstName nvarchar(50),
@lastName nvarchar(50),
@companyName nvarchar(50),
@notes nvarchar(4000)
)
BEGIN TRY
BEGIN TRANSACTION editCustomerTxn;
SET XACT_ABORT ON;
-- Is it an incremental change?
DECLARE @isIncremental bit = 0;
IF EXISTS( SELECT 1 FROM dbo.Customers WHERE
CustomerId = @customerId
AND
TenantId = @tenantId
AND
CHARINDEX( FirstName, @firstName ) > 0
AND
CHARINDEX( LastName, @lastName ) > 0
AND
CHARINDEX( CompanyName, @companyName ) > 0
AND
CHARINDEX( Notes, @notes ) > 0
)
BEGIN
SET @isIncremental = 1;
END
-----------------------
IF @isIncremental = 1
BEGIN
ALTER TABLE dbo.Customers SET ( SYSTEM_VERSIONING = OFF );
END
UPDATE
dbo.Customers
SET
FirstName = @firstName,
LastName = @lastName,
CompanyName = @companyName,
Notes = @notes
WHERE
CustomerId = @customerId
AND
TenantId = @tenantId;
IF @isIncremental = 1
BEGIN
ALTER TABLE dbo.Customers SET (
SYSTEM_VERSIONING = ON(
HISTORY_TABLE = dbo.Customers_History
)
);
END
COMMIT TRANSACTION editCustomerTxn;
RETURN 0;
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0
BEGIN
ROLLBACK TRANSACTION editCustomerTxn;
END
RETURN 1;
END CATCH
Run Code Online (Sandbox Code Playgroud)
...但这对我来说感觉不对- 因为这与使用ALTER TABLE dbo.Customers NOCHECK CONSTRAINT ALL
.
这听起来像是应用程序的行为和数据库功能不太适合的情况。您可以更改应用程序的行为,以便不会如此积极地对该表进行增量保存(也许缓存在其他地方更懒惰地保存),或者根本不使用时态表。
如果您无法更改应用程序行为,那么这似乎是使用触发器而不是时态表的机会。触发器可以具有处理“中间保存”的逻辑,以便它们遵守宽限期以限制频繁保存。
Stack Overflow 和 Stack Exchange Network 有 5 分钟的编辑宽限期。
为了防止一系列微小的编辑出现在修订历史中,编辑帖子的单个用户有 5 分钟的宽限期......在此期间,他们所做的任何其他编辑都将折叠到同一个编辑中在修订历史记录中,仅显示 5 分钟内所有编辑的最终结果。
无论您是在应用程序级别还是在数据库级别实现编辑历史的宽限期,您都肯定需要进行一些编码,因为没有数据库功能可以自动为您执行此操作。