Existe alguma maneira de alterar o tipo de dados da coluna como uma operação somente de metadados?
Acho que não, é assim que o produto funciona agora. Existem algumas soluções realmente ótimas para essa limitação proposta na resposta de Joe .
... resulta no SQL Server reescrevendo a tabela inteira (e usando o tamanho da tabela 2x no espaço de log)
Vou responder às duas partes dessa declaração separadamente.
Reescrevendo a Tabela
Como mencionei antes, não há realmente nenhuma maneira de evitar isso. Essa parece ser a realidade da situação, mesmo que não faça todo o sentido da nossa perspectiva como clientes.
Observar DBCC PAGE
antes e depois de alterar a coluna de 4000 para 260 mostra que todos os dados são duplicados na página de dados (minha tabela de teste teve 'A'
260 vezes na linha):
Neste ponto, há duas cópias exatamente dos mesmos dados na página. A coluna "antiga" é essencialmente excluída (a identificação é alterada de id = 2 para id = 67108865) e a versão "nova" da coluna é atualizada para apontar para o novo deslocamento dos dados na página:
Usando 2x o tamanho da tabela no espaço de log
A adição WITH (ONLINE = ON)
ao final da ALTER
instrução reduz a atividade de registro em cerca de metade . Portanto, esse é um aprimoramento que você pode fazer para reduzir a quantidade de gravações necessárias no espaço em disco / disco.
Eu usei este equipamento de teste para testá-lo:
USE [master];
GO
DROP DATABASE IF EXISTS [248749];
GO
CREATE DATABASE [248749]
ON PRIMARY
(
NAME = N'248749',
FILENAME = N'C:\Program Files\Microsoft SQL Server\MSSQL14.SQL2017\MSSQL\DATA\248749.mdf',
SIZE = 2048000KB,
FILEGROWTH = 65536KB
)
LOG ON
(
NAME = N'248749_log',
FILENAME = N'C:\Program Files\Microsoft SQL Server\MSSQL14.SQL2017\MSSQL\DATA\248749_log.ldf',
SIZE = 2048000KB,
FILEGROWTH = 65536KB
);
GO
USE [248749];
GO
CREATE TABLE dbo.[table]
(
id int IDENTITY(1,1) NOT NULL,
[col] nvarchar (4000) NULL,
CONSTRAINT [PK_test] PRIMARY KEY CLUSTERED (id ASC)
);
INSERT INTO dbo.[table]
SELECT TOP (1000000)
REPLICATE(N'A', 260)
FROM master.dbo.spt_values v1
CROSS JOIN master.dbo.spt_values v2
CROSS JOIN master.dbo.spt_values v3;
GO
Eu verifiquei sys.dm_io_virtual_file_stats(DB_ID(N'248749'), DEFAULT)
antes e depois de executar a ALTER
instrução, e aqui estão as diferenças:
Padrão (offline) ALTER
- Gravações no arquivo de dados / bytes gravados: 34.809 / 2.193.801.216
- Gravações no arquivo de log / bytes gravados: 40.953 / 1.484.910.080
Conectados ALTER
- Gravações no arquivo de dados / bytes gravados: 36.874 / 1.693.745.152 (queda de 22,8%)
- Gravações do arquivo de log / bytes gravados: 24.680 / 866.166.272 (queda de 41%)
Como você pode ver, houve uma pequena queda nas gravações do arquivo de dados e uma grande queda nas gravações do arquivo de log.
update table set new_col = old_col where new_col <> old_col;
antes de largarold_col
.where new_col <> old_col
não resultará nenhuma outra cláusula de filtragem, você pode adicionar um gatilho para transportar essas alterações à medida que elas acontecem e removê-las no final do processo. Ainda é um sucesso em potencial, mas muitas pequenas quantidades ao longo do processo, em vez de um grande sucesso no final, provavelmente (dependendo do padrão de atualização do seu aplicativo para a tabela) resultando em muito menos menos no total do que aquele grande sucesso .Bem, há uma alternativa dependendo do espaço disponível no seu banco de dados.
Crie uma cópia exata da sua tabela (por exemplo
new_table
), exceto a coluna na qual você reduzirá deNVARCHAR(4000)
paraNVARCHAR(260)
:Em uma janela de manutenção, copie os dados da tabela "quebrada" (
table
) para a tabela "fixa" (new_table
) com um simplesINSERT ... INTO ... SELECT ....
:Renomeie a tabela "quebrada"
table
para outra coisa:Renomeie a tabela "fixa"
new_table
paratable
:Se tudo estiver bem, descarte a tabela renomeada "quebrada":
Ai está.
Respondendo as suas perguntas
Não. Atualmente não é possível
Não.
( Veja minha solução e outras. )
fonte