Recentemente, discuti alguns recursos que a Microsoft desaconselha o uso e que acho que você deve esquecer que também existem. Houve o caso em que um colega promovia constantemente a visão obsoleta de compatibilidade com versões anteriores
sys.sysprocesses
em vez de exibições de gerenciamento dinâmico (DMVs) mais recentes e outro caso em que um colega diferente derrubou um servidor de produção usando o SQL Server Profiler. Meu último encontro com coisas melhor esquecidas é um novo procedimento armazenado com um
ntext
parâmetro. Verifiquei e, com certeza, o tipo de dados corresponde ao esquema da tabela subjacente. Minha mente começou a correr sobre esses tipos de dados mais antigos para explicar por que realmente não deveríamos mais usá-los:- imagem
- ntexto
- texto
Esses tipos estão na lista obsoleta por vários motivos e ocupam um lugar permanente nessa lista desde que foram substituídos pelo
max
tipos no SQL Server 2005. Alguns desses pontos problemáticos incluem:- você não pode usar muitas funções de string, como
LEFT()
,RTRIM()
,UPPER()
, e a maioria dos operadores de comparação; - você precisa usar funções como
TEXTPTR
,WRITETEXT
eUPDATETEXT
para modificações; - você não pode usar os tipos como variáveis locais;
- você não pode referenciar as colunas em
DISTINCT
,GROUP BY
,ORDER BY
, ou como uma coluna incluída (não que você queira fazer qualquer uma dessas); - valores menores que cabem na linha só podem fazer isso com o
text in row
opção.
Essa não é uma lista exaustiva; existem outras diferenças que você pode considerar mais ou menos importantes. A razão mais urgente para mim é que você não pode reconstruir o índice clusterizado online se a tabela contiver um desses tipos de dados.
Vamos criar um banco de dados simples com algumas tabelas:
CREATE DATABASE BadIdeas; GO USE BadIdeas; GO CREATE TABLE dbo.t1(id bigint IDENTITY PRIMARY KEY, msg nvarchar(max)); CREATE TABLE dbo.t2(id bigint IDENTITY PRIMARY KEY, msg ntext);
Agora, vamos tentar realizar operações online nas tabelas:
ALTER TABLE dbo.t1 REBUILD WITH (ONLINE = ON); GO ALTER TABLE dbo.t2 REBUILD WITH (ONLINE = ON);
Enquanto a primeira instrução é bem-sucedida, a segunda gera uma mensagem de erro como esta:
Msg 2725, Level 16, State 2
Uma operação online não pode ser executada para o índice 'PK__t2__3213E83FEEA1E0AD' porque o índice contém a coluna 'msg' do tipo de dados text, ntext, image ou FILESTREAM. Para um índice não clusterizado, a coluna pode ser uma coluna de inclusão do índice. Para um índice clusterizado, a coluna pode ser qualquer coluna da tabela. Se DROP_EXISTING for usado, a coluna poderá fazer parte de um índice novo ou antigo. A operação deve ser realizada offline.
Em um cenário único, a mensagem de erro é fácil de lidar:você pula a tabela ou, se a tabela absolutamente precisar ser reconstruída, você trabalha com suas equipes para agendar uma interrupção. Quando você está automatizando uma solução de manutenção de índice ou implantando novas configurações de compactação em seu ambiente, é um pouco mais trabalhoso fazer com que sua solução lide com o estado presente ou futuro.
O que fazer?
Você pode começar a substituir essas colunas por suas contrapartes mais modernas. Aqui está uma consulta para ajudá-lo a rastreá-los usando o
sys.columns
exibição de catálogo, mas você está por conta própria para quaisquer referências explícitas que possam existir no código do seu aplicativo:SELECT [Schema] = s.name, [Object] = o.name, [Column] = c.name, [Data Type] = TYPE_NAME(c.user_type_id) + CASE WHEN c.system_type_id <> c.user_type_id THEN N' (' + TYPE_NAME(c.system_type_id) + N')' ELSE N'' END FROM sys.columns AS c INNER JOIN sys.objects AS o ON c.[object_id] = o.[object_id] INNER JOIN sys.schemas AS s ON o.[schema_id] = s.[schema_id] WHERE c.system_type_id IN (34, 35, 99) ORDER BY [Schema], [Object], [Column];
Saída:
Pode ser tentador entrar no SSMS e alterar manualmente os tipos de dados dessas colunas, mas também pode haver outras implicações. Por exemplo, as colunas podem ter restrições padrão associadas a elas. E você pode ter procedimentos armazenados com parâmetros que devem ser atualizados em conjunto:
CREATE PROCEDURE dbo.sp1 @p1 ntext AS PRINT 1; GO
Para encontrar todos esses casos, você pode adaptar a consulta acima para pesquisar em
sys.parameters
visualização de catálogo em vez disso:SELECT [Schema] = s.name, [Object] = o.name, [Parameter] = p.name, [Data Type] = TYPE_NAME(p.user_type_id) + CASE WHEN p.system_type_id <> p.user_type_id THEN N' (' + TYPE_NAME(p.system_type_id) + N')' ELSE N'' END FROM sys.objects AS o INNER JOIN sys.schemas AS s ON o.[schema_id] = s.[schema_id] INNER JOIN sys.parameters AS p ON p.[object_id] = o.[object_id] WHERE p.system_type_id IN (34, 35, 99) ORDER BY [Schema], [Object], [Parameter];
Saída:
Se você precisar retornar esses dados em todos os bancos de dados, você pode pegar
sp_ineachdb
, um procedimento que escrevi (e documentei aqui e aqui) para superar várias das limitações no sp_MSforeachdb
com erros, não documentados e não suportados . Então você pode fazer isso:EXEC master.dbo.sp_ineachdb @command = N'SELECT [Database] = DB_NAME(), [Schema] = s.name, [Object] = o.name, [Column] = c.name, [Data Type] = TYPE_NAME(c.user_type_id) + CASE WHEN c.system_type_id <> c.user_type_id THEN N'' ('' + TYPE_NAME(c.system_type_id) + N'')'' ELSE N'''' END FROM sys.columns AS c INNER JOIN sys.objects AS o ON c.[object_id] = o.[object_id] INNER JOIN sys.schemas AS s ON o.[schema_id] = s.[schema_id] WHERE c.system_type_id IN (34, 35, 99) ORDER BY [Schema], [Object], [Column]; SELECT [Database] = DB_NAME(), [Schema] = s.name, [Object] = o.name, [Parameter] = p.name, [Data Type] = TYPE_NAME(p.user_type_id) + CASE WHEN p.system_type_id <> p.user_type_id THEN N'' ('' + TYPE_NAME(p.system_type_id) + N'')'' ELSE N'''' END FROM sys.objects AS o INNER JOIN sys.schemas AS s ON o.[schema_id] = s.[schema_id] INNER JOIN sys.parameters AS p ON p.[object_id] = o.[object_id] WHERE p.system_type_id IN (34, 35, 99) ORDER BY [Schema], [Object], [Parameter];';
Uma observação interessante aqui:se você executar isso em todos os bancos de dados, descobrirá que, mesmo no SQL Server 2019, a Microsoft ainda está usando alguns desses tipos antigos.
Você pode automatizar ainda mais executando-o no PowerShell ou em qualquer ferramenta de automação que você use para gerenciar várias instâncias do SQL Server.
Claro, isso é apenas o começo – só produz uma lista. Você pode estendê-lo ainda mais para gerar uma versão de rascunho do
ALTER TABLE
comandos você precisaria atualizar todas as tabelas, mas esses comandos precisariam ser revisados antes de executá-los, e você ainda precisaria modificar os procedimentos você mesmo (gerando ALTER PROCEDURE
comandos que apenas têm esses nomes de tipo de parâmetro substituídos corretamente não é um exercício fácil de forma alguma). Aqui está um exemplo que gera ALTER TABLE
comandos, levando em conta a nulidade, mas sem outras complicações, como restrições padrão:SELECT N'ALTER TABLE ' + QUOTENAME(s.name) + N'.' + QUOTENAME(o.name) + N' ALTER COLUMN ' + QUOTENAME(c.name) + N' ' + CASE c.system_type_id WHEN 34 THEN N'varbinary' WHEN 35 THEN N'varchar' WHEN 99 THEN N'nvarchar' END + N'(max)' + CASE c.is_nullable WHEN 0 THEN N' NOT' ELSE N'' END + N' NULL;' FROM sys.columns AS c INNER JOIN sys.objects AS o ON c.[object_id] = o.[object_id] INNER JOIN sys.schemas AS s ON o.[schema_id] = s.[schema_id] WHERE c.system_type_id IN (34, 35, 99);
Saída:
ALTER TABLE [dbo].[t2] ALTER COLUMN [msg] nvarchar(max) NULL;
E caso você esteja se perguntando, não, você não pode fazer essa operação única com um
WITH (ONLINE = ON)
explícito opção:Msg 11427, Level 16, State 1
A operação ALTER COLUMN online não pode ser executada para a tabela 't2' porque a coluna 'msg' atualmente tem ou está sendo alterada para um tipo de dados não suportado:text, ntext, image, CLR type ou FILESTREAM. A operação deve ser realizada offline.
Conclusão
Espero que isso forneça uma boa base sobre por que você deseja eliminar esses tipos obsoletos e um ponto de partida para realmente fazer as alterações. A Microsoft aprendeu da maneira mais difícil que não há muita funcionalidade que eles possam simplesmente extrair do produto, então não estou preocupado que eles deixem de existir na minha vida. Mas o medo da remoção não deve ser seu único motivador.