Database
 sql >> Base de données >  >> RDS >> Database

Fonctionnalités obsolètes à retirer de votre boîte à outils – Partie 3

J'ai récemment discuté de quelques fonctionnalités que Microsoft déconseille d'utiliser, et que je pense que vous devriez également oublier d'exister. Il y a eu le cas où un collègue a constamment promu la vue de rétrocompatibilité obsolète sys.sysprocesses au lieu de nouvelles vues de gestion dynamique (DMV), et un autre cas où un autre collègue a arrêté un serveur de production à l'aide de SQL Server Profiler.

Mon dernier run-in avec des choses qu'il vaut mieux oublier est une nouvelle procédure stockée avec un ntext paramètre. J'ai vérifié et, bien sûr, le type de données correspond au schéma de la table sous-jacente. Mon esprit s'est mis à courir à propos de ces anciens types de données pour expliquer pourquoi nous ne devrions vraiment plus les utiliser :

  • image
  • texte
  • texte

Ces types sont sur la liste obsolète pour de nombreuses raisons, et ont occupé une place permanente sur cette liste depuis leur remplacement par le max types remontent à SQL Server 2005. Certains de ces problèmes incluent :

  • vous ne pouvez pas utiliser de nombreuses fonctions de chaîne, comme LEFT() , RTRIM() , UPPER() , et la plupart des opérateurs de comparaison ;
  • vous devez utiliser des fonctions telles que TEXTPTR , WRITETEXT , et UPDATETEXT pour modifications ;
  • vous ne pouvez pas utiliser les types comme variables locales ;
  • vous ne pouvez pas référencer les colonnes dans DISTINCT , GROUP BY , ORDER BY , ou en tant que colonne incluse (pas que vous vouliez faire l'un de ces éléments);
  • les valeurs plus petites qui pourraient tenir dans la ligne ne peuvent le faire qu'avec le text in row option.

Ce n'est pas une liste exhaustive; il existe d'autres différences que vous pourriez considérer comme plus ou moins importantes. La raison la plus pressante pour moi est que vous ne pouvez pas reconstruire l'index clusterisé en ligne si la table contient l'un de ces types de données.

Créons une base de données simple avec quelques tables :

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

Essayons maintenant d'effectuer des opérations en ligne sur les tables :

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

Alors que la première instruction réussit, la seconde génère un message d'erreur comme celui-ci :

Msg 2725, Niveau 16, État 2
Une opération en ligne ne peut pas être effectuée pour l'index 'PK__t2__3213E83FEEA1E0AD' car l'index contient la colonne 'msg' de type de données text, ntext, image ou FILESTREAM. Pour un index non clusterisé, la colonne peut être une colonne d'inclusion de l'index. Pour un index clusterisé, la colonne peut être n'importe quelle colonne de la table. Si DROP_EXISTING est utilisé, la colonne peut faire partie d'un nouvel ou ancien index. L'opération doit être effectuée hors ligne.

Dans un scénario ponctuel, le message d'erreur est assez facile à gérer :soit vous sautez la table, soit, si la table doit absolument être reconstruite, vous travaillez avec vos équipes pour planifier une panne. Lorsque vous automatisez une solution de maintenance d'index ou que vous déployez de nouveaux paramètres de compression dans votre environnement, il est un peu plus pénible de faire en sorte que votre solution gère l'état actuel ou futur.

Que faire ?

Vous pouvez commencer à remplacer ces colonnes par leurs homologues plus modernes. Voici une requête pour vous aider à les retrouver à l'aide de sys.columns vue catalogue, mais vous êtes seul pour toutes les références explicites qui pourraient exister dans votre code d'application :

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

Sortie :

Il peut être tentant d'aller dans SSMS et de modifier manuellement les types de données de ces colonnes, mais il peut également y avoir d'autres implications. Par exemple, les colonnes peuvent être associées à des contraintes par défaut. Et vous pouvez avoir des procédures stockées avec des paramètres qui doivent être mis à jour en tandem :

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

Pour trouver tous ces cas, vous pouvez adapter la requête ci-dessus pour effectuer une recherche sur sys.parameters vue catalogue à la place :

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

Sortie :

Si vous avez besoin de renvoyer ces données dans toutes les bases de données, vous pouvez saisir sp_ineachdb , une procédure que j'ai écrite (et documentée ici et ici) pour surmonter plusieurs des limitations du sp_MSforeachdb bogué, non documenté et non pris en charge . Ensuite, vous pouvez faire ceci :

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

Une note complémentaire intéressante ici :si vous l'exécutez sur toutes les bases de données, vous découvrirez que, même dans SQL Server 2019, Microsoft utilise toujours certains de ces anciens types.

Vous pouvez automatiser davantage cela en l'exécutant à partir de PowerShell ou de tout autre outil d'automatisation que vous utilisez pour gérer plusieurs instances de SQL Server.

Bien sûr, ce n'est que le début - cela ne produit qu'une liste. Vous pouvez l'étendre davantage pour générer une version brouillon de ALTER TABLE vous auriez besoin de mettre à jour toutes les tables, mais ces commandes devraient être revues avant de les exécuter, et vous auriez toujours besoin de modifier les procédures vous-même (en générant ALTER PROCEDURE commandes dont seuls ces noms de type de paramètre sont remplacés correctement n'est en aucun cas un exercice facile). Voici un exemple qui génère ALTER TABLE commandes, prenant en compte la nullité mais pas d'autres complications comme les contraintes par défaut :

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

Sortie :

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

Et au cas où vous vous poseriez la question, non, vous ne pouvez pas effectuer cette opération unique avec un WITH (ONLINE = ON) explicite choix :

Msg 11427, Niveau 16, État 1
L'opération ALTER COLUMN en ligne ne peut pas être effectuée pour la table 't2' car la colonne 'msg' a actuellement ou est en train d'être modifiée dans un type de données non pris en charge :texte, ntext, image, type CLR ou FLUX DE FICHIERS. L'opération doit être effectuée hors ligne.

Conclusion

J'espère que cela fournit de bonnes informations sur les raisons pour lesquelles vous souhaitez éliminer ces types obsolètes et un point de départ pour effectuer les modifications. Microsoft a appris à ses dépens qu'il n'y a pas beaucoup de fonctionnalités qu'ils peuvent simplement extraire du produit, donc je ne crains pas que celles-ci cesseront d'exister de mon vivant. Mais la peur du retrait ne devrait pas être votre seule motivation.