O SQL Server sempre usa a combinação de operadores Dividir, Classificar e Recolher ao manter um índice exclusivo como parte de uma atualização que afeta (ou pode afetar) mais de uma linha.
Trabalhando com o exemplo da pergunta, podemos escrever a atualização como uma atualização de linha única separada para cada uma das quatro linhas presentes:
-- Per row updates
UPDATE dbo.Banana SET pk = 2 WHERE pk = 1;
UPDATE dbo.Banana SET pk = 3 WHERE pk = 2;
UPDATE dbo.Banana SET pk = 4 WHERE pk = 3;
UPDATE dbo.Banana SET pk = 5 WHERE pk = 4;
O problema é que a primeira instrução falhará, pois muda pk
de 1 para 2, e já existe uma linha em que pk
= 2. O mecanismo de armazenamento do SQL Server exige que índices exclusivos permaneçam exclusivos em todas as etapas do processamento, mesmo em uma única instrução . Esse é o problema resolvido por Dividir, Classificar e Recolher.
Dividido
A primeira etapa é dividir cada instrução de atualização em uma exclusão seguida por uma inserção:
DELETE dbo.Banana WHERE pk = 1;
INSERT dbo.Banana (pk, c1, c2) VALUES (2, 'A', 'W');
DELETE dbo.Banana WHERE pk = 2;
INSERT dbo.Banana (pk, c1, c2) VALUES (3, 'B', 'X');
DELETE dbo.Banana WHERE pk = 3;
INSERT dbo.Banana (pk, c1, c2) VALUES (4, 'C', 'Y');
DELETE dbo.Banana WHERE pk = 4;
INSERT dbo.Banana (pk, c1, c2) VALUES (5, 'D', 'Z');
O operador Split adiciona uma coluna de código de ação ao fluxo (aqui chamado Act1007):
O código de ação é 1 para uma atualização, 3 para uma exclusão e 4 para uma inserção.
Ordenar
As instruções de divisão acima ainda produziriam uma violação de chave exclusiva transitória falsa; portanto, a próxima etapa é classificar as instruções pelas chaves do índice exclusivo que está sendo atualizado ( pk
nesse caso) e depois pelo código de ação. Neste exemplo, isso significa simplesmente que as exclusões (3) na mesma chave são ordenadas antes das inserções (4). A ordem resultante é:
-- Sort (pk, action)
DELETE dbo.Banana WHERE pk = 1;
DELETE dbo.Banana WHERE pk = 2;
INSERT dbo.Banana (pk, c1, c2) VALUES (2, 'A', 'W');
DELETE dbo.Banana WHERE pk = 3;
INSERT dbo.Banana (pk, c1, c2) VALUES (3, 'B', 'X');
DELETE dbo.Banana WHERE pk = 4;
INSERT dbo.Banana (pk, c1, c2) VALUES (4, 'C', 'Y');
INSERT dbo.Banana (pk, c1, c2) VALUES (5, 'D', 'Z');
Colapso
O estágio anterior é suficiente para garantir a prevenção de falsas violações de exclusividade em todos os casos. Como uma otimização, o Collapse combina exclusões adjacentes e insere o mesmo valor-chave em uma atualização:
-- Collapse (pk)
DELETE dbo.Banana WHERE pk = 1;
UPDATE dbo.Banana SET c1 = 'A', c2 = 'W' WHERE pk = 2;
UPDATE dbo.Banana SET c1 = 'B', c2 = 'X' WHERE pk = 3;
UPDATE dbo.Banana SET c1 = 'C', c2 = 'Y' WHERE pk = 4;
INSERT dbo.Banana (pk, c1, c2) VALUES (5, 'D', 'Z');
Os pares de exclusão / inserção para os pk
valores 2, 3 e 4 foram combinados em uma atualização, deixando uma única exclusão em pk
= 1 e uma inserção em pk
= 5.
O operador Recolher agrupa linhas pelas colunas principais e atualiza o código de ação para refletir o resultado do recolhimento:
Atualização de índice em cluster
Este operador é rotulado como uma atualização, mas é capaz de inserções, atualizações e exclusões. A ação que é executada pela Atualização de Índice em Cluster por linha é determinada pelo valor do código de ação nessa linha. O operador possui uma propriedade Action para refletir este modo de operação:
Contadores de modificação de linha
Observe que as três atualizações acima não modificam as chaves do índice exclusivo que está sendo mantido. Com efeito, transformamos as atualizações nas colunas- chave do índice em atualizações das colunas não-chave ( c1
e c2
), além de uma exclusão e uma inserção. Nem uma exclusão nem uma inserção podem causar uma violação falsa de chave exclusiva.
Uma inserção ou exclusão afeta todas as colunas da linha; portanto, as estatísticas associadas a cada coluna terão seus contadores de modificação incrementados. Para atualizações, apenas as estatísticas com qualquer uma das colunas atualizadas como coluna principal têm seus contadores de modificação incrementados (mesmo que o valor seja inalterado).
Os contadores de modificação de linha de estatísticas mostram, portanto, 2 alterações em pk
e 5 para c1
e c2
:
-- Collapse (pk)
DELETE dbo.Banana WHERE pk = 1; -- All columns modified
UPDATE dbo.Banana SET c1 = 'A', c2 = 'W' WHERE pk = 2; -- c1 and c2 modified
UPDATE dbo.Banana SET c1 = 'B', c2 = 'X' WHERE pk = 3; -- c1 and c2 modified
UPDATE dbo.Banana SET c1 = 'C', c2 = 'Y' WHERE pk = 4; -- c1 and c2 modified
INSERT dbo.Banana (pk, c1, c2) VALUES (5, 'D', 'Z'); -- All columns modified
Nota: Somente as alterações aplicadas ao objeto base (heap ou índice clusterizado) afetam os contadores de modificação de linha de estatísticas. Índices não agrupados em cluster são estruturas secundárias, refletindo as alterações já feitas no objeto base. Eles não afetam os contadores de modificação de linha de estatísticas.
Se um objeto tiver vários índices exclusivos, uma combinação separada de Dividir, Classificar e Recolher será usada para organizar as atualizações para cada um. O SQL Server otimiza esse caso para índices não clusterizados, salvando o resultado do Split em um spool de tabela ansioso e reproduzindo o conjunto para cada índice exclusivo (que terá sua própria classificação por chaves de índice + código de ação e recolhimento).
Efeito nas atualizações de estatísticas
As atualizações automáticas de estatísticas (se ativadas) ocorrem quando o otimizador de consultas precisa de informações estatísticas e percebe que as estatísticas existentes são desatualizadas (ou inválidas devido a uma alteração no esquema). As estatísticas são consideradas desatualizadas quando o número de modificações registradas excede um limite.
O arranjo Dividir / Classificar / Recolher resulta em diferentes modificações de linha sendo gravadas do que seria esperado. Por sua vez, isso significa que uma atualização de estatísticas pode ser acionada mais cedo ou mais tarde do que seria o caso.
No exemplo acima, as modificações de linha da coluna chave aumentam em 2 (a alteração líquida) em vez de 4 (uma para cada linha da tabela afetada) ou 5 (uma para cada exclusão / atualização / inserção produzida pelo recolhimento).
Além disso, colunas não chave que não foram logicamente alteradas pela consulta original acumulam modificações de linha, que podem numerar o dobro do dobro das linhas da tabela atualizadas (uma para cada exclusão e uma para cada inserção).
O número de alterações registradas depende do grau de sobreposição entre os valores antigos e novos da coluna-chave (e, portanto, o grau em que as exclusões e exclusões separadas podem ser reduzidas). Redefinindo a tabela entre cada execução, as seguintes consultas demonstram o efeito nos contadores de modificação de linha com diferentes sobreposições:
UPDATE dbo.Banana SET pk = pk + 0; -- Full overlap
UPDATE dbo.Banana SET pk = pk + 1;
UPDATE dbo.Banana SET pk = pk + 2;
UPDATE dbo.Banana SET pk = pk + 3;
UPDATE dbo.Banana SET pk = pk + 4; -- No overlap