Database
 sql >> Database >  >> RDS >> Database

Funzionalità obsolete da eliminare dalla cassetta degli attrezzi - Parte 3

Di recente ho discusso di alcune funzionalità che Microsoft sconsiglia di utilizzare e che penso che dovresti anche dimenticare che esistono. C'è stato il caso in cui un collega ha costantemente promosso la vista di compatibilità con le versioni precedenti sys.sysprocesses invece delle più recenti viste a gestione dinamica (DMV) e un altro caso in cui un altro collega ha interrotto un server di produzione utilizzando SQL Server Profiler.

Il mio ultimo incontro con le cose che è meglio dimenticare è una nuova stored procedure con un ntext parametro. Ho controllato e, abbastanza sicuro, il tipo di dati corrisponde allo schema per la tabella sottostante. La mia mente ha iniziato a correre su questi vecchi tipi di dati per spiegare perché non dovremmo più usarli:

  • immagine
  • ntext
  • testo

Questi tipi sono nell'elenco obsoleto per molte ragioni e hanno occupato un posto permanente in tale elenco da quando sono stati sostituiti dal max tipi risalenti a SQL Server 2005. Alcuni di questi punti deboli includono:

  • Non puoi usare molte funzioni di stringa, come LEFT() , RTRIM() , UPPER() e la maggior parte degli operatori di confronto;
  • devi usare funzioni come TEXTPTR , WRITETEXT e UPDATETEXT per modifiche;
  • non puoi usare i tipi come variabili locali;
  • non puoi fare riferimento alle colonne in DISTINCT , GROUP BY , ORDER BY , o come una colonna inclusa (non che tu debba voler fare nessuna di queste);
  • valori più piccoli che potrebbero rientrare nella riga possono farlo solo con il text in row opzione.

Questo non è un elenco esaustivo; ci sono altre differenze che potresti considerare più o meno importanti. Il motivo più urgente per me è che non è possibile ricostruire l'indice cluster online se la tabella contiene uno di questi tipi di dati.

Creiamo un semplice database con poche tabelle:

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);

Ora, proviamo ad eseguire operazioni online sui tavoli:

ALTER TABLE dbo.t1 REBUILD WITH (ONLINE = ON);
GO
ALTER TABLE dbo.t2 REBUILD WITH (ONLINE = ON);

Mentre la prima istruzione ha esito positivo, la seconda restituisce un messaggio di errore come questo:

Msg 2725, livello 16, stato 2
Non è possibile eseguire un'operazione online per l'indice 'PK__t2__3213E83FEEA1E0AD' perché l'indice contiene la colonna 'msg' di tipo di dati text, ntext, image o FILESTREAM. Per un indice non cluster, la colonna potrebbe essere una colonna di inclusione dell'indice. Per un indice cluster, la colonna potrebbe essere qualsiasi colonna della tabella. Se viene utilizzato DROP_EXISTING, la colonna potrebbe far parte di un indice nuovo o precedente. L'operazione deve essere eseguita offline.

In uno scenario unico, il messaggio di errore è abbastanza facile da gestire:salti il ​​tavolo o, se il tavolo deve essere assolutamente ricostruito, collabori con i tuoi team per pianificare un'interruzione. Quando si automatizza una soluzione di manutenzione dell'indice o si distribuiscono nuove impostazioni di compressione nell'ambiente, è un po' più una seccatura fare in modo che la soluzione gestisca lo stato presente o futuro.

Cosa fare?

Puoi iniziare a sostituire queste colonne con le loro controparti più moderne. Ecco una query per aiutarti a rintracciarli utilizzando sys.columns vista catalogo, ma sei solo per eventuali riferimenti espliciti che potrebbero esistere nel codice dell'applicazione:

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];

Uscita:

Potrebbe essere allettante entrare in SSMS e modificare manualmente i tipi di dati di queste colonne, ma potrebbero esserci anche altre implicazioni. Ad esempio, alle colonne potrebbero essere associati vincoli predefiniti. E potresti avere stored procedure con parametri che dovrebbero essere aggiornati in tandem:

CREATE PROCEDURE dbo.sp1 @p1 ntext AS PRINT 1;
GO

Per trovare tutti questi casi, puoi adattare la query precedente per eseguire una ricerca in base a sys.parameters vista catalogo invece:

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];

Uscita:

Se hai bisogno di restituire questi dati su tutti i database, puoi prendere sp_ineachdb , una procedura che ho scritto (e documentato qui e qui) per superare molte delle limitazioni nel buggy, non documentato e non supportato sp_MSforeachdb . Quindi puoi farlo:

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];';

Una nota a margine interessante qui:se lo esegui su tutti i database, scoprirai che, anche in SQL Server 2019, Microsoft utilizza ancora alcuni di questi vecchi tipi.

Puoi automatizzarlo ulteriormente eseguendolo da PowerShell o da qualsiasi strumento di automazione che utilizzi per gestire più istanze di SQL Server.

Naturalmente, questo è solo l'inizio:produce solo un elenco. Puoi estenderlo ulteriormente per generare una bozza di ALTER TABLE comandi dovresti aggiornare tutte le tabelle, ma quei comandi dovrebbero essere rivisti prima di eseguirli e dovresti comunque modificare le procedure da solo (generando ALTER PROCEDURE comandi che hanno solo quei nomi dei tipi di parametri sostituiti correttamente non è un esercizio facile in alcun modo). Ecco un esempio che genera ALTER TABLE comandi, tenendo conto della capacità di nulla ma senza altre complicazioni come i vincoli predefiniti:

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);

Uscita:

ALTER TABLE [dbo].[t2] ALTER COLUMN [msg] nvarchar(max) NULL;

E nel caso te lo stia chiedendo, no, non puoi eseguire questa operazione una tantum con un esplicito WITH (ONLINE = ON) opzione:

Msg 11427, livello 16, stato 1
L'operazione ALTER COLUMN online non può essere eseguita per la tabella 't2' perché la colonna 'msg' è attualmente o viene modificata in un tipo di dati non supportato:testo, ntext, immagine, tipo CLR o FILESTREAM. L'operazione deve essere eseguita offline.

Conclusione

Si spera che questo fornisca un buon background sul motivo per cui si desidera eliminare questi tipi deprecati e un punto di partenza per apportare effettivamente le modifiche. Microsoft ha imparato a proprie spese che non ci sono molte funzionalità che possono semplicemente strappare via dal prodotto, quindi non sono preoccupato che queste cesseranno mai di esistere nella mia vita. Ma la paura della rimozione non dovrebbe essere la tua unica motivazione.