Database
 sql >> Base de Dados >  >> RDS >> Database

Recursos obsoletos para tirar da sua caixa de ferramentas – Parte 3


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 e UPDATETEXT 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.