A
ALTER TABLE ... ALTER COLUMN
comando é muito poderoso. Você pode usá-lo para alterar o tipo de dados de uma coluna, comprimento, precisão, escala, nulidade, agrupamento… e muitas outras coisas além. Certamente é mais conveniente que a alternativa:criar uma nova tabela e migrar os dados sempre que uma alteração for necessária. No entanto, há muito que pode ser feito para esconder a complexidade subjacente. Junto com um grande número de restrições sobre o que é possível com este comando, há sempre a questão do desempenho.
Em última análise, as tabelas são armazenadas como uma sequência de bytes com alguns metadados em outras partes do sistema para descrever o que cada um desses bytes significa e como eles se relacionam com cada uma das várias colunas da tabela. Quando pedimos ao SQL Server para alterar algum aspecto da definição de uma coluna, ele precisa verificar se os dados existentes são compatíveis com a nova definição. Ele também precisa determinar se o layout físico atual precisa ser alterado.
Dependendo do tipo de alteração e da configuração do banco de dados, um
ALTER COLUMN
comando precisará executar uma das seguintes ações:- Altere os metadados apenas nas tabelas do sistema.
- Verifique a compatibilidade de todos os dados existentes e altere os metadados.
- Reescreva alguns ou todos os dados armazenados para corresponder à nova definição.
A opção 1 representa o caso ideal do ponto de vista do desempenho. Requer apenas algumas alterações nas tabelas do sistema e uma quantidade mínima de log. A operação ainda exigirá uma modificação de esquema restritiva
Sch-M
lock, mas as próprias alterações de metadados serão concluídas muito rapidamente, independentemente do tamanho da tabela. Alterações somente de metadados
Há vários casos especiais a serem observados, mas como resumo geral, as ações a seguir exigem apenas alterações nos metadados:
- Indo de
NOT NULL
paraNULL
para o mesmo tipo de dados. - Aumentar o tamanho máximo de um
varchar
,nvarchar
, ouvarbinary
coluna (exceto paramax
).
Melhorias no SQL Server 2016
O assunto desta postagem são as alterações adicionais habilitadas apenas para metadados do SQL Server 2016 em diante . Nenhuma alteração na sintaxe é necessária e nenhuma configuração precisa ser modificada. Você obtém essas melhorias não documentadas gratuitamente.
Os novos recursos visam um subconjunto de comprimento fixo tipos de dados. As novas habilidades se aplicam a tabelas de armazenamento de linha nas seguintes circunstâncias:
- A compactação deve estar ativada:
- Em todos os índices e partições , incluindo o heap base ou o índice clusterizado.
- Qualquer
ROW
ouPAGE
compressão. - Índices e partições podem usar uma mistura desses níveis de compressão. O importante é que não haja partições ou índices descompactados.
- Mudando de
NULL
paraNOT NULL
é não permitido . - As seguintes alterações de tipo inteiro são suportados:
smallint
parainteger
oubigint
.integer
parabigint
.smallmoney
paramoney
(usa representação inteira internamente).
- As seguintes alterações de tipo de string e binário são suportados:
char(n)
parachar(m)
ouvarchar(m)
nchar(n)
paranchar(m)
ounvarchar(m)
binary(n)
parabinary(m)
ouvarbinary(m)
- Todos os itens acima apenas para
n < m
em != max
- As alterações de agrupamento não são permitidas
Essas alterações podem ser apenas de metadados porque o layout de dados binários subjacente não muda quando o Descritor de coluna formato de linha é usado (daí a necessidade de compactação). Sem compactação, o armazenamento de linha usa a FixedVar original representação, que não pode acomodar essas alterações de tipo de dados de comprimento fixo sem reescrever o layout físico.
Você pode notar que
tinyint
é omitido da lista de tipos inteiros. Isso ocorre porque ele não é assinado, enquanto os outros tipos inteiros são todos assinados, portanto, uma alteração somente de metadados não é possível. Por exemplo, um valor de 255 pode caber em um byte para tinyint
, mas requer dois bytes em qualquer um dos formatos assinados. Os formatos assinados podem conter -128 a +127 em um byte quando compactados. Exemplo de inteiro
Uma aplicação muito útil dessa melhoria é alterar o tipo de dados de uma coluna com o
IDENTITY
propriedade. Digamos que temos a seguinte tabela de heap usando compactação de linha (a compactação de página também funcionaria):
DROP TABLE IF EXISTS dbo.Test; GO CREATE TABLE dbo.Test ( id integer IDENTITY NOT NULL, some_value integer NOT NULL ) WITH (DATA_COMPRESSION = ROW);
Vamos adicionar 5 milhões de linhas de dados. Isso será suficiente para tornar óbvio (do ponto de vista do desempenho) se a alteração do tipo de dados da coluna é uma operação somente de metadados ou não:
WITH Numbers AS ( SELECT n = ROW_NUMBER() OVER (ORDER BY @@SPID) FROM sys.all_columns AS AC1 CROSS JOIN sys.all_columns AS AC2 ORDER BY n OFFSET 0 ROWS FETCH FIRST 5 * 1000 * 1000 ROWS ONLY ) INSERT dbo.Test WITH (TABLOCKX) ( some_value ) SELECT N.n FROM Numbers AS N;
Em seguida, vamos propagar novamente a
IDENTITY
para fazer parecer que estamos quase no ponto de esgotar os valores que cabem em um integer
:DBCC CHECKIDENT ( N'dbo.Test', RESEED, 2147483646 );
Podemos adicionar mais uma linha com sucesso:
INSERT dbo.Test (some_value) VALUES (123456);
Mas tentando adicionar outra linha:
INSERT dbo.Test (some_value) VALUES (7890);
Resulta em uma mensagem de erro:
Msg 8115, Level 16, State 1, Line 1
Erro de estouro aritmético convertendo IDENTITY para tipo de dados int.
Podemos corrigir isso convertendo a coluna para
bigint
:ALTER TABLE dbo.Test ALTER COLUMN id bigint NOT NULL;
Graças às melhorias no SQL Server 2016, este comando altera somente metadados , e conclui imediatamente. O
INSERT
anterior instrução (a que gerou o erro de estouro aritmético) agora é concluída com êxito. Essa nova capacidade não resolve todos os problemas relacionados à alteração do tipo de uma coluna com a
IDENTITY
propriedade. Ainda precisaremos descartar e recriar quaisquer índices na coluna, recriar quaisquer chaves estrangeiras de referência e assim por diante. Isso está um pouco fora do escopo deste post (embora Aaron Bertrand tenha escrito sobre isso antes). Ser capaz de alterar o tipo como uma operação somente de metadados certamente não faz mal. Com um planejamento cuidadoso, as outras etapas necessárias podem ser feitas da forma mais eficiente possível, por exemplo, usando registro mínimo ou ONLINE
operações. Tenha cuidado com a sintaxe
Certifique-se de sempre especifique
NULL
ou NOT NULL
ao alterar os tipos de dados com ALTER COLUMN
. Digamos, por exemplo, que queremos também alterar o tipo de dados do some_value
coluna em nossa tabela de teste de integer NOT NULL
para bigint NOT NULL
. Quando escrevemos o comando, omitimos o
NULL
ou NOT NULL
qualificador:ALTER TABLE dbo.Test ALTER COLUMN some_value bigint;
Este comando é concluído com sucesso como uma alteração somente de metadados, mas também remove o
NOT NULL
limitação. A coluna agora é bigint NULL
, que não é o que pretendíamos. Esse comportamento é documentado, mas é fácil ignorar. Podemos tentar corrigir nosso erro com:
ALTER TABLE dbo.Test ALTER COLUMN some_value bigint NOT NULL;
Isso não uma alteração somente de metadados. Não temos permissão para mudar de
NULL
para NOT NULL
(consulte a tabela anterior se precisar de uma atualização sobre as condições). O SQL Server precisará verificar todos os valores existentes para garantir que nenhum nulo esteja presente. Ele então reescreverá fisicamente cada linha da mesa. Além de serem lentas em si, essas ações geram uma grande quantidade de log de transações, que podem ter efeitos indiretos. Como observação lateral, esse mesmo erro não é possível para colunas com a
IDENTITY
propriedade. Se escrevermos uma ALTER COLUMN
declaração sem NULL
ou NOT NULL
nesse caso, o mecanismo presume que queremos dizer NOT NULL
porque a propriedade identity não é permitida em colunas anuláveis. Ainda é uma ótima ideia não confiar nesse comportamento. Sempre especifique
NULL
ou NOT NULL
com ALTER COLUMN
. Agrupamento
É necessário um cuidado especial ao alterar uma coluna de string que tenha um agrupamento que não corresponda ao padrão do banco de dados.
Por exemplo, digamos que temos uma tabela com um agrupamento que diferencia maiúsculas de minúsculas e acentos (suponha que o padrão do banco de dados seja diferente):
DROP TABLE IF EXISTS dbo.Test2; GO CREATE TABLE dbo.Test2 ( id integer IDENTITY NOT NULL, some_string char(8) COLLATE Latin1_General_100_CS_AS NOT NULL ) WITH (DATA_COMPRESSION = ROW);
Adicione 5 milhões de linhas de dados:
WITH Numbers AS ( SELECT n = ROW_NUMBER() OVER (ORDER BY @@SPID) FROM sys.all_columns AS AC1 CROSS JOIN sys.all_columns AS AC2 ORDER BY n OFFSET 0 ROWS FETCH FIRST 5 * 1000 * 1000 ROWS ONLY ) INSERT dbo.Test2 WITH (TABLOCKX) ( some_string ) SELECT CONVERT(char(8), N.n) COLLATE Latin1_General_100_CS_AS FROM Numbers AS N;
Dobre o comprimento da coluna de string usando o seguinte comando:
ALTER TABLE dbo.Test2 ALTER COLUMN some_string char(16) NOT NULL;
Lembramos de especificar
NOT NULL
, mas esqueceu o agrupamento não padrão. O SQL Server assume que pretendemos alterar o agrupamento para o padrão do banco de dados (Latin1_General_CI_AS
para meu banco de dados de teste). Alterar o agrupamento impede que a operação seja somente de metadados e, portanto, a operação é executada por vários minutos, gerando pilhas de log. Recrie a tabela e os dados usando o script anterior e tente
ALTER COLUMN
command novamente, mas especificando o agrupamento não padrão existente como parte do comando:ALTER TABLE dbo.Test2 ALTER COLUMN some_string char(16) COLLATE Latin1_General_100_CS_AS NOT NULL;
A alteração agora é concluída imediatamente, como uma operação somente de metadados. Assim como o
NULL
e NOT NULL
sintaxe, vale a pena ser explícito para evitar acidentes. Este é um bom conselho em geral, não apenas para ALTER COLUMN
. Compressão
Esteja ciente de que a compactação precisa ser especificada explicitamente para cada índice e separadamente para a tabela base, se for um heap. Este é outro exemplo em que o uso de sintaxe ou atalhos abreviados pode impedir o resultado desejado.
Por exemplo, a tabela a seguir não especifica a compactação explícita para a chave primária ou a definição de índice in-line:
CREATE TABLE dbo.Test ( id integer IDENTITY NOT NULL PRIMARY KEY, some_value integer NOT NULL INDEX [IX dbo.Test some_value] ) WITH (DATA_COMPRESSION = PAGE);
A
PRIMARY KEY
terá um nome atribuído, padrão para CLUSTERED
, e seja PAGE
comprimido. O índice in-line será NONCLUSTERED
e não compactado. Esta tabela não será habilitada para nenhuma das novas otimizações porque nem todos os índices e partições são compactados. Uma definição de tabela muito melhor e mais explícita seria:
CREATE TABLE dbo.Test ( id integer IDENTITY NOT NULL CONSTRAINT [PK dbo.Test id] PRIMARY KEY CLUSTERED WITH (DATA_COMPRESSION = PAGE), some_value integer NOT NULL INDEX [IX dbo.Test some_value] NONCLUSTERED WITH (DATA_COMPRESSION = ROW) );
Essa tabela se qualificará para as novas otimizações porque todos os índices e partições são compactados. Como observado anteriormente, misturar tipos de compressão é bom.
Existem várias maneiras de escrever este
CREATE TABLE
declaração de forma explícita, então há um elemento de preferência pessoal. O ponto importante é sempre ser explícito sobre o que você quer. Isso se aplica a separar CREATE INDEX
declarações também. Eventos estendidos e sinalizador de rastreamento
Há um evento estendido especificamente para os novos metadados
ALTER COLUMN
operações com suporte no SQL Server 2016 em diante. O evento estendido é
compressed_alter_column_is_md_only
em Depurar canal. Seus campos de evento são object_id
, column_id
e is_md_only
(verdadeiro falso). Este evento indica apenas se uma operação é somente metadados devido aos novos recursos do SQL Server 2016. As alterações de coluna que eram somente metadados antes de 2016 mostrarão
is_md_only = false
apesar de ainda ser apenas metadados. Outros eventos estendidos úteis para rastrear
ALTER COLUMN
as operações incluem metadata_ddl_alter_column
e alter_column_event
, ambos no Analytic canal. Se você precisar desativar os novos recursos do SQL Server 2016 por qualquer motivo, o sinalizador de rastreamento global (ou de inicialização) não documentado 3618 pode ser usado. Esse sinalizador de rastreamento não é eficaz quando usado no nível da sessão. Não há como especificar um sinalizador de rastreamento em nível de consulta com um
ALTER COLUMN
comando. Considerações finais
Ser capaz de alterar alguns tipos de dados inteiros de comprimento fixo com uma alteração somente de metadados é uma melhoria de produto muito bem-vinda. Requer que a tabela já esteja totalmente compactada, mas isso está se tornando mais comum de qualquer maneira. Isso é especialmente verdadeiro porque a compactação foi habilitada em todas as edições a partir do SQL Server 2016 Service Pack 1.
Colunas do tipo string de comprimento fixo são provavelmente muito menos comuns. Parte disso pode ser devido a considerações um tanto desatualizadas, como o uso do espaço. Quando compactadas, as colunas de string de comprimento fixo não armazenam espaços em branco à direita, tornando-as tão eficientes quanto as colunas de string de comprimento variável do ponto de vista de armazenamento. Pode ser irritante cortar espaços para manipulação ou exibição, mas se os dados geralmente ocupam a maior parte do comprimento máximo, os tipos de comprimento fixo podem ter vantagens importantes, principalmente em relação às concessões de memória para coisas como classificação e hash.
Nem tudo são boas notícias com a compactação ativada. Mencionei anteriormente que o SQL Server às vezes pode realizar uma alteração somente de metadados após verificar se todos os valores existentes serão convertidos com êxito para o novo tipo. Este é o caso ao usar
ALTER COLUMN
para mudar de integer
para smallint
por exemplo. Infelizmente, no momento, essas operações não são apenas metadados para objetos compactados. Agradecimentos
Agradecimentos especiais a Panagiotis Antonopoulos (Engenheiro de software principal) e Mirek Sztajno (Sênior Program Manager) da equipe de produto do SQL Server por sua assistência e orientação durante a pesquisa e redação deste artigo.
Nenhum dos detalhes fornecidos neste trabalho deve ser considerado como documentação oficial da Microsoft ou declarações de produtos.