Générer un script pour automatiser le changement de nom des contraintes par défaut

8

Contexte: Certaines de nos contraintes de colonne par défaut ont été générées sans noms explicites, nous obtenons donc des noms amusants qui varient d'un serveur à l'autre comme: DF__User__TimeZoneIn__5C4D869D

Je préférerais les avoir tous gérables avec un nom cohérent comme DF_Users_TimeZoneInfopour que nous puissions nous assurer que les contraintes appropriées existent sur les futures tables cibles (comme dans RedGate compare, ou même simplement visuellement)

J'ai un script qui fonctionne principalement pour ce que je veux:

select 'sp_rename N''[' + s.name + '].[' + d.name + ']'', 
   N''[DF_' + t.name + '_' + c.name + ']'', ''OBJECT'';'
from sys.tables t
    join
    sys.default_constraints d
        on d.parent_object_id = t.object_id
    join
    sys.columns c
        on c.object_id = t.object_id
        and c.column_id = d.parent_column_id
    join sys.schemas s
        on t.schema_id = s.schema_id
WHERE d.NAME like 'DF[_][_]%'

Mais cela me donne juste un jeu de résultats, et non quelque chose que je peux réellement passer à un exécuteur ou autre.

Comment puis-je faire cela pour que je puisse simplement exécuter ces sp_renamescripts sans avoir à recourir à la copie de tous les éléments retournés et à les coller dans une nouvelle fenêtre de requête et à les réexécuter? Essayer d'enregistrer autant de frappes que possible pour que je puisse corriger cela dans de nombreux environnements.

entrez la description de l'image ici

jcolebrand
la source
<facepalm> Vous auriez dû me poser des questions à ce sujet l'autre jour - j'ai tiré ma réponse du script que j'ai qui fait exactement cela. :)
Jon Seigel
@ JonSeigel ok, que diriez-vous d'un niveau d'indrection autour de cela pour s'exécuter une fois sur chacun de plusieurs dbs? : D
jcolebrand
Ne mettez pas de crochets autour du nouveau nom - je me suis retrouvé avec un monde de douleur après avoir exécuté ce script :)
samjudson

Réponses:

16

Ok, deux ou trois choses.

  1. toujours utiliser EXEClors de l'exécution de procédures stockées; la sténographie sans EXECfonctionne uniquement lorsqu'il s'agit de la première instruction du lot (et ce ne sera pas le cas ici).
  2. utilisez toujours des terminateurs point-virgule - dans ce cas, ils sont utiles au lieu de jolis retours chariot et indentation, mais ils sont toujours judicieux.
  3. utilisez toujours QUOTENAME()au lieu d'appliquer manuellement des crochets. Dans ce cas, vous êtes probablement en sécurité, mais il y a des cas où l'approche manuelle se cassera.
  4. vous pouvez tester la PRINTsortie mais elle ne sera pas nécessairement complète si votre commande totale est> 8k (voir cette astuce pour quelques approches alternatives ).

    DECLARE @sql nvarchar(max) = N'';
    
    SELECT @sql += N'EXEC sys.sp_rename N''' 
        + QUOTENAME(s.name) + '.' + QUOTENAME(d.name) 
        + ''', N''DF_' + t.name + '_' + c.name + ''', ''OBJECT'';'
      FROM sys.tables AS t
      INNER JOIN sys.default_constraints AS d
         ON d.parent_object_id = t.object_id
      INNER JOIN sys.columns AS c
         ON c.object_id = t.object_id
        AND c.column_id = d.parent_column_id
      INNER JOIN sys.schemas AS s
         ON t.schema_id = s.schema_id
      WHERE d.NAME LIKE N'DF[_][_]%';
    
    PRINT @sql;
    -- EXEC sys.sp_executesql @sql;
    
Aaron Bertrand
la source
Bon à savoir sur l'Exec pour chaque déclaration, je ne savais pas à ce sujet.
jcolebrand
3
Ouais essayer d'exécuter ceci: sp_help; sp_help;.
Aaron Bertrand
3

Sur la base de votre question .. que vous avez supprimée "automatiser le même script sur certaines des bases de données de l'instance"

Voici le code qui vous aidera

set nocount on
DECLARE @table TABLE 
  ( 
     dbname VARCHAR(30) 
  ) 

INSERT INTO @table 
            (dbname) 
VALUES      ( 'dev_construct1' ), 
            ('dev_construct2'), 
            ('dev_construct3' ); 

DECLARE @sql NVARCHAR(max) = N''; 
DECLARE @dbname VARCHAR(30) 

/*  
Added by Kin : While loop and an extra @dbname variable 
*/ 
SELECT @dbname = Min(dbname) 
FROM   @table 

WHILE @dbname IS NOT NULL 
  BEGIN 
      SELECT @sql = N'USE ' + tt.dbname + Char(10) + N' GO;'
      FROM   @table tt 
      WHERE  @dbname = dbname 

      SELECT @sql += Char(10) + N'EXEC sp_rename N''' 
                     + Quotename(s.name) + '.' + Quotename(d.name) 
                     + ''', N''DF_' + t.name + '_' + c.name 
                     + ''', ''OBJECT'';' 
      FROM   sys.tables AS t 
             JOIN sys.default_constraints d 
               ON d.parent_object_id = t.object_id 
             JOIN sys.columns c 
               ON c.object_id = t.object_id 
                  AND c.column_id = d.parent_column_id 
             JOIN sys.schemas s 
               ON t.schema_id = s.schema_id 
             JOIN @table tt 
               ON tt.dbname = tt.dbname 
      WHERE  d.name LIKE 'DF[_][_]%'; 

      PRINT @sql 

      SELECT @dbname = Min(dbname) 
      FROM   @table 
      WHERE  dbname > @dbname 
  END 
-- EXEC sp_executesql @sql; 
Kin Shah
la source
Je l'ai supprimé car la question précédente manquait un élément clé de détail, car les noms de colonne changent dans chaque base de données, j'ai donc besoin d'exécuter la requête interne par contexte de la base de données individuelle.
jcolebrand
@jcolebrand merci pour la mise à jour. Dans tous les cas, le code avec mes modifications vous aidera dans ce que vous cherchez.
Kin Shah