如何禁止或禁用單個語句或事務的臨時表歷史記錄插入?
我有一個儲存客戶資訊的表格(您的標準 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
是否實際上是增量的可能需要一些自定義應用程式碼邏輯,這意味著必須從應用程式碼啟動和送出事務,這看起來像一個壞主意,因為現在有成千上萬的事情可能出錯,更不用說性能問題了(在這種情況下,從應用程序伺服器到數據庫伺服器的網路延遲應該小於 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
…但這對我來說感覺不對- 因為這與使用
ALTER TABLE dbo.Customers NOCHECK CONSTRAINT ALL
.
這聽起來像是應用程序的行為和數據庫功能不太合適的情況。您可以更改應用程序的行為,以便不會如此積極地對該表進行增量保存(也許記憶體在其他地方更懶惰地保存),或者根本不使用臨時表。
如果您無法更改應用程序行為,那麼這似乎是一個使用觸發器而不是臨時表的機會。觸發器可以具有處理“中間保存”的邏輯,以便它們遵守寬限期以限制頻繁保存。
Stack Overflow 和 Stack Exchange Network 有 5 分鐘的編輯寬限期。
為了防止一系列微小的編輯出現在修訂歷史記錄中,編輯文章的單個使用者有 5 分鐘的寬限期……在此期間,他們所做的任何其他編輯都會折疊到同一個編輯中在修訂歷史中,僅顯示他們在 5 分鐘內所有編輯的最終結果。
無論您是在應用程序級別還是在數據庫級別實現編輯歷史記錄的寬限期,您都肯定需要進行一些編碼,因為沒有數據庫功能可以自動為您執行此操作。
執行 ALTER TABLE dbo.Customers SET (SYSTEM_VERSIONING = OFF);在事務內部,它沒有說明這將如何影響其他並髮使用者和連接。
ALTER TABLE 始終需要獨占模式鎖 (Sch-M)。因此,在事務送出或回滾之前,沒有其他會話可以讀取、更新或更改表。
下面的程式碼是否正確,它會擴展嗎?
不會縮放。
所以
使用者編輯客戶詳細資訊的面向使用者的表單在最後一次擊鍵後 2 秒自動保存表單內容,每次保存都會導致 UPDATE 語句
改變它,或者使用詳細的歷史記錄表。您始終可以執行批處理作業來刪除一些歷史記錄行(在事務中關閉 SYSTEM_VERSIONING 之後:))。