Criar uma tabela temporal com controle de versão pelo sistema
Aplica-se a: SQL Server 2016 (13.x) e versões posteriores Banco de Dados SQL do Azure Banco de dados SQL da Instância Gerenciada de SQL do Azure no Microsoft Fabric
Há três maneiras de criar uma tabela temporal com controle da versão do sistema ao considerar o modo como a tabela de histórico é especificada:
Tabela temporal com uma tabela de histórico anônimo: especifique o esquema da tabela atual e deixe o sistema criar a tabela de histórico correspondente com o nome gerado automaticamente.
Tabela temporal com uma tabela de histórico padrão: especifique o nome do esquema de tabela de histórico e o nome da tabela e deixe o sistema criar tabela de histórico nesse esquema.
Tabela temporal com uma tabela de histórico definida pelo usuário criada antecipadamente: crie a tabela de histórico que melhor atenda às suas necessidades e faça referência a essa tabela durante a criação da tabela temporal.
Crie uma tabela temporal com uma tabela de histórico anônimo
Criar uma tabela temporal com uma tabela de histórico anônimo é uma opção conveniente para a criação rápida de objeto, especialmente em ambientes de teste e de protótipos. Também é a maneira mais simples de criar uma tabela temporal, já que não exige qualquer parâmetro na cláusula SYSTEM_VERSIONING
. No exemplo a seguir, uma nova tabela é criada com o controle de versão do sistema habilitado, sem definir o nome da tabela de histórico.
CREATE TABLE Department
(
DeptID INT NOT NULL PRIMARY KEY CLUSTERED,
DeptName VARCHAR(50) NOT NULL,
ManagerID INT NULL,
ParentDeptID INT NULL,
ValidFrom DATETIME2 GENERATED ALWAYS AS ROW START NOT NULL,
ValidTo DATETIME2 GENERATED ALWAYS AS ROW END NOT NULL,
PERIOD FOR SYSTEM_TIME (ValidFrom, ValidTo)
)
WITH (SYSTEM_VERSIONING = ON);
Comentários
Uma tabela temporal com controle da versão do sistema deve ter uma chave primária definida e ter exatamente um PERIOD FOR SYSTEM_TIME
definido com duas colunas datetime2, declaradas como GENERATED ALWAYS AS ROW START
ou GENERATED ALWAYS AS ROW END
.
Supõe-se que as colunas PERIOD
sempre são não anuláveis, mesmo se a nulidade não é especificada. Quando as colunas PERIOD
são definidas explicitamente como anuláveis, a instrução CREATE TABLE
falha.
A tabela de histórico deve sempre ser alinhada ao esquema com a tabela atual ou temporal, em relação ao número de colunas, nomes de coluna, ordenação e tipos de dados.
Uma tabela de histórico anônimo é criada automaticamente no mesmo esquema que a tabela temporal ou atual.
O nome da tabela de histórico anônimo tem o seguinte formato: MSSQL_TemporalHistoryFor_<current_temporal_table_object_id>_<suffix>
. O sufixo é opcional e adicionado somente se a primeira parte do nome da tabela não for exclusiva.
A tabela de histórico é criada como uma tabela rowstore. Se possível, a compactação PAGE
é aplicada, caso contrário, a tabela de histórico é descompactada. Por exemplo, algumas configurações de tabela, como as colunas SPARSE
, não permitem compactação.
Um índice clusterizado padrão é criado para a tabela de histórico com um nome gerado automaticamente no formato IX_<history_table_name>
. O índice clusterizado contém as colunas PERIOD
(início, fim).
No banco de dados SQL do Fabric, a tabela de histórico criada não é espelhada no Fabric OneLake.
Para criar a tabela atual como uma tabela com otimização de memória, consulte Tabelas temporais com controle de versão do sistema com tabelas com otimização de memória.
Criar uma tabela temporal com uma tabela de histórico padrão
A criação de uma tabela temporal com uma tabela de histórico padrão é uma opção conveniente quando você deseja controlar a nomenclatura e ainda depende do sistema para criar a tabela de histórico com a configuração padrão. No exemplo a seguir, uma nova tabela é criada com o controle de versão do sistema habilitado, com o nome da tabela de histórico definido explicitamente.
CREATE TABLE Department
(
DeptID INT NOT NULL PRIMARY KEY CLUSTERED,
DeptName VARCHAR(50) NOT NULL,
ManagerID INT NULL,
ParentDeptID INT NULL,
ValidFrom DATETIME2 GENERATED ALWAYS AS ROW START NOT NULL,
ValidTo DATETIME2 GENERATED ALWAYS AS ROW END NOT NULL,
PERIOD FOR SYSTEM_TIME (ValidFrom, ValidTo)
)
WITH (SYSTEM_VERSIONING = ON (HISTORY_TABLE = dbo.DepartmentHistory));
Comentários
A tabela de histórico é criada usando as mesmas regras que se aplicam à criação de uma tabela de histórico "anônimo", com as seguintes regras se aplicam especificamente à tabela de histórico nomeada.
O nome do esquema é obrigatório para o parâmetro
HISTORY_TABLE
.Quando o esquema especificado não existe, a instrução
CREATE TABLE
falha.Quando a tabela especificada pelo parâmetro
HISTORY_TABLE
já existe, ela é validada em relação à tabela temporal recém-criada em termos de consistência do esquema e consistência dos dados temporais. Quando você especifica uma tabela de histórico inválido, a instruçãoCREATE TABLE
falha.
Criar uma tabela temporal com uma tabela de histórico definida pelo usuário
A criação de uma tabela temporal com uma tabela de histórico definida pelo usuário é uma opção conveniente quando o usuário deseja especificar a tabela de histórico com opções de armazenamento específicas e índices diferentes ajustados às consultas históricas. No exemplo a seguir, uma tabela de histórico definido pelo usuário é criada com um esquema alinhado à tabela temporal criada. Para esta tabela de histórico definida pelo usuário, um índice columnstore clusterizado e um índice rowstore não clusterizado extra (árvore B) são criados para pesquisas de ponto. Após a criação dessa tabela de histórico definido pelo usuário, a tabela temporal é criada especificando a tabela de histórico definido pelo usuário como a tabela de histórico padrão.
Observação
A documentação usa o termo árvore B geralmente em referência a índices. Em índices de rowstore, o Database Engine implementa uma árvore B+. Isso não se aplica a índices columnstore ou índice em tabelas com otimização de memória. Para obter mais informações, confira o Guia de arquitetura e design do índice do SQL Server e SQL do Azure.
CREATE TABLE DepartmentHistory
(
DeptID INT NOT NULL,
DeptName VARCHAR(50) NOT NULL,
ManagerID INT NULL,
ParentDeptID INT NULL,
ValidFrom DATETIME2 NOT NULL,
ValidTo DATETIME2 NOT NULL
);
GO
CREATE CLUSTERED COLUMNSTORE INDEX IX_DepartmentHistory ON DepartmentHistory;
CREATE NONCLUSTERED INDEX IX_DepartmentHistory_ID_Period_Columns
ON DepartmentHistory (ValidTo, ValidFrom, DeptID);
GO
CREATE TABLE Department
(
DeptID int NOT NULL PRIMARY KEY CLUSTERED,
DeptName VARCHAR(50) NOT NULL,
ManagerID INT NULL,
ParentDeptID INT NULL,
ValidFrom DATETIME2 GENERATED ALWAYS AS ROW START NOT NULL,
ValidTo DATETIME2 GENERATED ALWAYS AS ROW END NOT NULL,
PERIOD FOR SYSTEM_TIME (ValidFrom, ValidTo)
)
WITH (SYSTEM_VERSIONING = ON (HISTORY_TABLE = dbo.DepartmentHistory));
Comentários
Se você planeja executar consultas analíticas em dados históricos que empregam agregações ou funções de janelamento, a criação de uma columnstore clusterizada como um índice primário é altamente recomendável para o desempenho da consulta e compactação.
Se você planeja usar tabelas temporais para auditoria de dados (ou seja, pesquisar por alterações históricas de uma única linha da tabela atual), é interessante criar uma tabela de histórico de rowstore com um índice clusterizado.
A tabela de histórico não pode ter chave primária, chaves estrangeiras, índices exclusivos, restrições de tabela ou gatilhos. Ela não pode ser configurada para captura de dados de alterações, controle de alterações, replicação transacional ou replicação de mesclagem.
No banco de dados SQL do Fabric e no Banco de Dados SQL do Azure com o espelhamento do Fabric configurado, quando você usa uma tabela existente como a tabela de histórico durante a criação da tabela temporal, a tabela existente deixa de ser espelhada.
Alteração de tabela não temporal para tabela temporal com controle da versão do sistema
É possível habilitar o controle da versão do sistema usando uma tabela não temporal existente, por exemplo, quando você deseja migrar uma solução personalizada temporal para o suporte interno.
Por exemplo, você pode ter um conjunto de tabelas nas quais o controle de versão é implementado com gatilhos. O uso do controle de versão do sistema temporal é menos complexo e oferece outros benefícios que incluem:
- Histórico imutável
- Nova sintaxe para consultas entre períodos
- Melhor desempenho de DML
- Custos mínimos de manutenção
Ao converter uma tabela existente, considere usar a cláusula HIDDEN
para ocultar as novas colunas PERIOD
(as colunas datetime2 ValidFrom
e ValidTo
), a fim de evitar o impacto em aplicativos existentes que não especificam explicitamente os nomes das colunas (por exemplo, SELECT *
ou INSERT
sem lista de colunas) e não foram projetados para lidar com novas colunas.
Adição do controle de versão a tabelas não temporais
Para controlar as alterações de uma tabela não temporal que contém os dados, é necessário adicionar a definição PERIOD
e, opcionalmente, fornecer um nome para a tabela de histórico vazio que o SQL Server cria para você:
CREATE SCHEMA History;
GO
ALTER TABLE InsurancePolicy ADD
ValidFrom DATETIME2 GENERATED ALWAYS AS ROW START HIDDEN
CONSTRAINT DF_InsurancePolicy_ValidFrom DEFAULT SYSUTCDATETIME(),
ValidTo DATETIME2 GENERATED ALWAYS AS ROW END HIDDEN
CONSTRAINT DF_InsurancePolicy_ValidTo DEFAULT CONVERT(DATETIME2, '9999-12-31 23:59:59.9999999'),
PERIOD FOR SYSTEM_TIME(ValidFrom, ValidTo);
GO
ALTER TABLE InsurancePolicy
SET (SYSTEM_VERSIONING = ON (HISTORY_TABLE = History.InsurancePolicy));
GO
Importante
A precisão de DATETIME2
deve alinhar-se à precisão da tabela subjacente.
Comentários
A adição de colunas não nulas com padrões à tabela com dados existente é uma operação de tamanho de dados (size of data operation) em todas as edições com exceção do SQL Server Enterprise (em que é uma operação de metadados). Com uma tabela de histórico grande e com dados existente no SQL Server Standard, a adição de uma coluna não nula pode ser uma operação dispendiosa.
As restrições para colunas com período de início e período de término devem ser escolhidas com cuidado:
O padrão para a coluna inicial especifica a partir de qual ponto no tempo você considera as linhas existentes válidas. Isso não pode ser especificado como um ponto de datetime no futuro.
A hora de término precisa ser especificada como o valor máximo para determinada precisão de datetime2, por exemplo,
9999-12-31 23:59:59
ou9999-12-31 23:59:59.9999999
.
A adição de PERIOD
executará a verificação de consistência dos dados na tabela atual a fim de se certificar de que os valores existentes para colunas do período sejam válidos.
Quando uma tabela de histórico existente é especificada ao habilitar SYSTEM_VERSIONING
, uma verificação de consistência de dados temporais é executada na tabela atual e de histórico. Ela pode ser ignorada se você especificar DATA_CONSISTENCY_CHECK = OFF
como um parâmetro extra.
Migrar as tabelas existentes para o suporte interno
Este exemplo mostra como migrar de uma solução existente com base em gatilhos para o suporte temporal interno. Para este exemplo, vamos supor que a solução personalizada atual divide os dados atuais e históricos em duas tabelas de usuário separadas (ProjectTaskCurrent
e ProjectTaskHistory
).
Se a solução existente usa uma só tabela para armazenar as linhas reais e históricas, divida os dados entre duas tabelas antes das etapas de migração mostradas no exemplo a seguir. Primeiro, solte o gatilho na tabela temporal futura. Em seguida, verifique se as colunas PERIOD
não são anuláveis.
/* Drop trigger on future temporal table */
DROP TRIGGER ProjectCurrent_OnUpdateDelete;
/* Make sure that future period columns are non-nullable */
ALTER TABLE ProjectTaskCurrent
ALTER COLUMN [ValidFrom] DATETIME2 NOT NULL;
ALTER TABLE ProjectTaskCurrent
ALTER COLUMN [ValidTo] DATETIME2 NOT NULL;
ALTER TABLE ProjectTaskHistory
ALTER COLUMN [ValidFrom] DATETIME2 NOT NULL;
ALTER TABLE ProjectTaskHistory
ALTER COLUMN [ValidTo] DATETIME2 NOT NULL;
ALTER TABLE ProjectTaskCurrent ADD PERIOD
FOR SYSTEM_TIME([ValidFrom], [ValidTo]);
ALTER TABLE ProjectTaskCurrent SET (
SYSTEM_VERSIONING = ON (
HISTORY_TABLE = dbo.ProjectTaskHistory,
DATA_CONSISTENCY_CHECK = ON
)
);
Comentários
A referência às colunas existentes na definição de PERIOD
altera implicitamente generated_always_type
para AS_ROW_START
e para AS_ROW_END
nessas colunas.
A adição de PERIOD
executará a verificação de consistência dos dados na tabela atual a fim de se certificar de que os valores existentes para colunas do período sejam válidos.
É altamente recomendável definir SYSTEM_VERSIONING
com DATA_CONSISTENCY_CHECK = ON
para impor as verificações de consistência de dados nos dados existentes.
Se as colunas ocultas forem preferenciais, use o comando ALTER TABLE [tableName] ALTER COLUMN [columnName] ADD HIDDEN;
.
Conteúdo relacionado
- Tabelas temporais
- Introdução a tabelas temporais com controle de versão do sistema
- Gerenciar a retenção de dados históricos em tabelas temporárias com versão do sistema
- Tabelas temporais com controle de versão do sistema e tabelas com otimização de memória
- CREATE TABLE (Transact-SQL)
- Modificar dados em uma tabela temporal com controle de versão de sistema
- Consultar dados em uma tabela temporal com controle da versão do sistema
- Alterar o esquema de uma tabela temporal com controle de versão de sistema
- Parar o controle de versão de sistema em uma tabela temporal com controle de versão de sistema