Acredito que essa seja realmente uma falha de design, embora não seja específica do SQL Server 2016, pois todas as outras implementações existentes de tabelas temporais (tanto quanto eu sei) têm a mesma falha. Os problemas que podem surgir com as tabelas temporais por causa disso são bastante graves; o cenário no seu exemplo é moderado em comparação com o que pode dar errado em geral:
Referências de chave estrangeira quebradas : suponha que tenhamos duas tabelas temporais, com a tabela A tendo uma referência de chave estrangeira à tabela B. Agora, digamos que temos duas transações, ambas em execução no nível de isolamento READ COMMITTED: a transação 1 começa antes da transação 2, transação 2 insere uma linha na tabela B e confirma, a transação 1 insere uma linha na tabela A com uma referência à linha recém-adicionada de B. Como a adição da nova linha a B já foi confirmada, a restrição de chave estrangeira é satisfeita e a transação 1 é capaz de confirmar com sucesso. No entanto, se visualizarmos o banco de dados "AS OF" em algum momento entre o início da transação 1 e o início da transação 2, veremos a tabela A com uma referência a uma linha de B que não existe. Então, neste caso,a tabela temporal fornece uma visão inconsistente do banco de dados . Obviamente, essa não era a intenção do padrão SQL: 2011, que afirma,
As linhas históricas do sistema em uma tabela com versão do sistema formam instantâneos imutáveis do passado. Quaisquer restrições que estavam em vigor quando uma linha histórica do sistema foi criada já teriam sido verificadas quando essa linha era uma linha atual do sistema, portanto, nunca há necessidade de impor restrições nas linhas históricas do sistema.
Chaves primárias não exclusivas : digamos que temos uma tabela com uma chave primária e duas transações, ambas no nível de isolamento READ COMMITTED, no qual ocorre o seguinte: Após o início da transação 1, mas antes de tocar nessa tabela, a transação 2 exclui um determinado linha da tabela e confirma. Em seguida, a transação 1 insere uma nova linha com a mesma chave primária que a que foi excluída. Isso ocorre muito bem, mas quando você olha para a tabela A PARTIR DE ENTREGAMENTE no momento em que a transação 1 começou e quando a transação 2 começou, veremos duas linhas com a mesma chave primária.
Erros em atualizações simultâneas : digamos que temos uma tabela e duas transações que atualizam a mesma linha nela, novamente em um nível de isolamento READ COMMITTED. A transação 1 começa primeiro, mas a transação 2 é a primeira a atualizar a linha. A transação 2 é confirmada e a transação 1 faz uma atualização diferente na linha e confirmada. Tudo bem, exceto que, se for uma tabela temporal, após a execução da atualização na transação 1, quando o sistema inserir a linha necessária na tabela de histórico, o SysStartTime gerado será o horário de início da transação 2, enquanto o SysEndTime será o horário de início da transação 1, que não é um intervalo de tempo válido, pois o SysEndTime seria anterior ao SysStartTime. Nesse caso, o SQL Server gera um erro e reverte a transação (por exemplo, consulteesta discussão ). Isso é muito desagradável, pois no nível de isolamento READ COMMITTED, não seria de esperar que os problemas de concorrência levassem a falhas definitivas, o que significa que os aplicativos não necessariamente serão preparados para fazer novas tentativas. Em particular, isso é contrário a uma "garantia" na documentação da Microsoft:
Esse comportamento garante que seus aplicativos herdados continuem funcionando quando você habilitar a versão do sistema em tabelas que se beneficiarão da versão. ( link )
Outras implementações de tabelas temporais lidaram com esse cenário (duas transações simultâneas atualizando a mesma linha) oferecendo uma opção para "ajustar" automaticamente os carimbos de data e hora, se eles forem inválidos (veja aqui e aqui ). Essa é uma solução feia, pois tem a conseqüência infeliz de interromper a atomicidade das transações, pois outras instruções nas mesmas transações geralmente não terão seus timestamps ajustados da mesma maneira; isto é, com esta solução alternativa, se visualizarmos o banco de dados "AS OF" em determinados momentos, poderemos ver transações parcialmente executadas.
Solução: Você já sugeriu a solução óbvia, que é a implementação de usar o horário de término da transação (ou seja, o horário de confirmação) em vez do horário de início. Sim, é verdade que, quando estamos executando uma instrução no meio de uma transação, é impossível saber qual será o tempo de consolidação (como é no futuro, ou talvez nem exista se a transação for rolada). de volta). Mas isso não significa que a solução não é implementável; só tem que ser feito de uma maneira diferente. Por exemplo, ao executar uma instrução UPDATE ou DELETE, ao criar a linha do histórico, o sistema pode simplesmente inserir o ID da transação atual em vez de uma hora de início e, em seguida, o ID pode ser convertido em um carimbo de data / hora posteriormente pelo sistema após a transação ser confirmada .
No contexto desse tipo de implementação, eu sugeriria que, antes da transação ser confirmada, todas as linhas adicionadas à tabela de histórico não deveriam ser visíveis ao usuário. Da perspectiva do usuário, deve parecer simplesmente que essas linhas foram adicionadas (com o carimbo de data / hora de confirmação) no momento da confirmação. Em particular, se a transação nunca for confirmada com êxito, nunca deverá aparecer no histórico. Obviamente, isso é inconsistente com o padrão SQL: 2011, que descreve as inserções no histórico (incluindo registros de data e hora) como ocorrendo no momento das instruções UPDATE e DELETE (em oposição ao horário da confirmação). Mas não acho que isso realmente importe, considerando que o padrão nunca foi implementado adequadamente (e possivelmente nunca pode ser) devido aos problemas descritos acima,
Do ponto de vista do desempenho, pode parecer indesejável que o sistema precise voltar e revisitar as linhas do histórico para preencher o carimbo de data / hora de confirmação. Mas, dependendo de como isso é feito, o custo pode ser bastante baixo. Não estou muito familiarizado com o funcionamento interno do SQL Server, mas o PostgreSQL, por exemplo, usa um write-ahead-log, o que faz com que, se várias atualizações forem executadas nas mesmas partes de uma tabela, essas atualizações sejam consolidadas para que o os dados precisam ser gravados apenas uma vez nas páginas da tabela física - e isso normalmente se aplica a esse cenário. Em qualquer caso,
É claro que, desde que (até onde eu sei) esse tipo de sistema nunca foi implementado, não posso dizer com certeza que funcionaria - talvez haja algo que esteja faltando - mas não vejo nenhuma razão por que não funcionou.
20160707 11:04:58
e agora você atualiza todas as linhas com esse carimbo de data / hora. Mas essa atualização também é executada por alguns segundos e termina às20160707 11:05:02
, agora, qual carimbo de data e hora é o final correto da transação? Ou suponha que você usouRead Uncommited
at20160707 11:05:00
e tenha retornado linhas, mas depoisAS OF
não as mostra.