我有一个存储客户信息的表格(您的标准 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 ''
)
该表最近被转换为 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 )
)
由于这样做,出现了一个问题:用户编辑客户详细信息的面向用户的表单会在最后一次击键后 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
是否实际上是增量的可能需要一些自定义应用程序代码逻辑,这意味着必须从应用程序代码启动和提交事务,这看起来像一个坏主意,因为现在有成千上万的事情可能出错,更不用说性能问题了(在这种情况下,从应用程序服务器到数据库服务器的网络延迟应该小于 1ms,但这仍然会影响可伸缩性)。
假设我可以在 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
...但这对我来说感觉不对- 因为这与使用ALTER TABLE dbo.Customers NOCHECK CONSTRAINT ALL
.
这听起来像是应用程序的行为和数据库功能不太合适的情况。您可以更改应用程序的行为,以便不会如此积极地对该表进行增量保存(也许缓存在其他地方更懒惰地保存),或者根本不使用临时表。
如果您无法更改应用程序行为,那么这似乎是一个使用触发器而不是临时表的机会。触发器可以具有处理“中间保存”的逻辑,以便它们遵守宽限期以限制频繁保存。
Stack Overflow 和 Stack Exchange Network 有 5 分钟的编辑宽限期。
无论您是在应用程序级别还是在数据库级别实现编辑历史记录的宽限期,您都肯定需要进行一些编码,因为没有数据库功能可以自动为您执行此操作。
ALTER TABLE 始终需要独占模式锁 (Sch-M)。因此,在事务提交或回滚之前,没有其他会话可以读取、更新或更改表。
不会缩放。
所以
改变它,或者使用详细的历史记录表。您始终可以运行批处理作业来删除一些历史记录行(在事务中关闭 SYSTEM_VERSIONING 之后:))。