Eu tenho uma tabela que armazena informações do cliente (seu material de CRM padrão do pântano):
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 ''
)
Esta tabela foi recentemente convertida em uma tabela temporal do 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 )
)
Desde que isso aconteceu, um problema surgiu: o formulário voltado para o usuário que os usuários editam os detalhes do cliente salva automaticamente o conteúdo do formulário 2 segundos após o último pressionamento de tecla, e cada salvamento resulta em uma UPDATE
instrução com quantidades de dados cada vez maiores - e assim SQL Server está constantemente adicionando novas linhas à Customers_History
tabela, com 2 segundos de intervalo, o que resulta em linhas com poucas informações e spam.
Então é isso que eu vejo quando faço um SELECT * FROM dbo.Customers_History WHERE CustomerId = 123
, por exemplo:
Eu estava pensando que o código do lado do servidor que lida com os envios de salvamento automático poderia verificar se o estado do formulário de entrada é uma alteração incremental e, em caso afirmativo, UPDATE
a dbo.Customers
tabela sem adicionar nenhuma linha à Customers_History
tabela.
Até agora, a única maneira que vejo para fazer isso é definindoSYSTEM_VERSIONING = OFF
dentro de uma transação, no entanto, tenho algumas dúvidas antes de realmente implementar isso ...
Embora a documentação diga (até recomende) a execução
ALTER TABLE dbo.Customers SET (SYSTEM_VERSIONING = OFF);
dentro de uma transação, ela não diz como isso afetaria outros usuários e conexões simultâneos . Afinal, esta é uma instrução DML, e as instruções DML têm comportamento inconsistente (hah!) com isolamento.Iniciar e concluir uma transação em um
PROCEDURE
lote armazenado ou SQL é uma coisa - mas determinar se umUPDATE
é realmente incremental ou não pode exigir alguma lógica de código de aplicativo personalizado, o que significa ter que iniciar e confirmar a transação a partir do código de aplicativo, o que parece uma má ideia por causa das milhares de coisas que agora podem dar errado , sem falar nos problemas de desempenho (neste caso, a latência da rede do servidor de aplicativos para o servidor de banco de dados deve ser inferior a 1 ms, mas isso ainda afetará a escalabilidade ).
Supondo que eu possa fazer isso em um PROCEDURE
, este código abaixo está correto e ele será dimensionado?
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
... mas isso não parece certo para mim - porque isso é basicamente a mesma coisa que hackear restrições adiáveis com ALTER TABLE dbo.Customers NOCHECK CONSTRAINT ALL
.
Isso soa como um caso em que o comportamento do aplicativo e o recurso de banco de dados simplesmente não combinam muito bem. Você pode alterar o comportamento do seu aplicativo para que os salvamentos incrementais não sejam feitos nessa tabela de forma tão agressiva (talvez o armazenamento em cache salve mais lentamente em outro lugar) ou para não usar tabelas temporais.
Se você não pode alterar o comportamento do aplicativo, parece que esta é uma oportunidade de usar um gatilho em vez de uma tabela temporal. O gatilho pode ter lógica para lidar com "salvamentos intermediários" para que eles obedeçam um período de carência para limitar os salvamentos frequentes.
O Stack Overflow e a Stack Exchange Network têm um período de carência de 5 minutos para edição .
Independentemente de você implementar o período de carência para o histórico de edições no nível do aplicativo ou no nível do banco de dados, definitivamente haverá um pouco de codificação de sua parte, pois não há recurso de banco de dados para fazer isso automaticamente para você.
ALTER TABLE sempre requer um bloqueio de esquema exclusivo (Sch-M). Portanto, nenhuma outra sessão pode ler, atualizar ou alterar a tabela até que a transação seja confirmada ou revertida.
Não vai escalar.
Então
mude isso ou viva com a tabela de histórico detalhada. Você sempre pode executar um trabalho em lote para excluir algumas das linhas do histórico (depois de desativar SYSTEM_VERSIONING em uma transação :)).