Référencement de base de données par programmation via T-SQL

11

J'écris une procédure stockée qui prend un nom de base de données comme argument et renvoie une table des index de cette base de données et leur niveau de fragmentation. Cette procédure stockée vivra dans notre base de données DBA (la base de données qui contient les tables que les DBA utilisent pour surveiller et optimiser les choses). Les systèmes en question sont tous SQL Server 2008 R2 si cela fait une différence.

J'ai la requête de base élaborée, mais je suis coincé à essayer de fournir les noms réels des index. À ma connaissance, cette information est contenue dans la vue sys.indexes de chaque individu. Mon problème spécifique essaie de référencer cette vue par programme à partir d'une procédure stockée d'une autre base de données.

Pour illustrer, voici la partie de la requête en cause:

FROM sys.dm_db_index_physical_stats(@db_id,NULL,NULL,NULL,NULL) p
INNER JOIN sys.indexes b ON p.[object_id] = b.[object_id] 
    AND p.index_id = b.index_id 
    AND b.index_id != 0

La requête fonctionne correctement lorsqu'elle est exécutée à partir de la base de données identifiée par @db_id, car elle utilise la vue sys.indexes appropriée. Si j'essaie d'appeler cela à partir de la base de données DBA, cependant, tout est nul, car la vue sys.indexes est pour la mauvaise base de données.

De manière plus générale, je dois pouvoir faire quelque chose comme ceci:

DECLARE @db_name NVARCHAR(255) = 'my_database';
SELECT * FROM @db_name + '.sys.indexes';

ou

USE @db_name;

J'ai essayé de changer de base de données ou de référencer d'autres bases de données en utilisant des combinaisons de concaténation de chaînes et de fonctions OBJECT_NAME / OBJECT_ID / DB_ID et rien ne semble fonctionner. J'apprécierais toutes les idées que la communauté pourrait avoir, mais je soupçonne que je devrai réorganiser cette procédure stockée pour résider dans chaque base de données individuelle.

Merci d'avance pour toute suggestion.

mdoyle
la source
1
Vous devrez utiliser du SQL dynamique pour cela, je suppose ...
JNK

Réponses:

10

Dynamic SQL est pratique pour ces types de tâches administratives. Voici un extrait d'une procédure stockée que j'ai écrite qui non seulement obtient les niveaux de défragmentation, mais génère également le code pour effectuer la défragmentation:

select @SQL = 
'
select getdate(),
       ''' + @@ServerName + ''',
       ''' + @DatabaseName + ''',
       so.Name,
       si.Name,
       db_id(''' + @DatabaseName + '''),
       ips.object_id,
       ips.index_id,
       ips.index_type_desc,
       ips.alloc_unit_type_desc,
       ips.index_depth,
       ips.avg_fragmentation_in_percent,
       ips.fragment_count,
       avg_fragment_size_in_pages,
       ips.page_count,
       ips.record_count,
       case
         when ips.index_id = 0 then ''alter table [' + @DatabaseName + '].'' + ss.name + ''.['' + so.name + ''] rebuild with (online = on)''
         else ''alter index '' + si.name + '' on [' + @DatabaseName + '].'' + ss.name + ''.['' + so.name + ''] rebuild with (online = on)''
       end
  from sys.dm_db_index_physical_stats(db_id(''' + @DatabaseName + '''),null,null,null, ''' + @SampleMode + ''') ips
  join [' + @DatabaseName + '].sys.objects so  on so.object_id = ips.object_id
  join [' + @DatabaseName + '].sys.schemas ss  on ss.schema_id = so.schema_id
  join [' + @DatabaseName + '].sys.indexes si  on si.object_id = ips.object_id
                      and si.index_id  = ips.index_id
order by so.Name, ips.index_id
'

exec (@SQL)
datagod
la source
Sur la base du commentaire de JNK, j'ai modifié ma requête pour utiliser du SQL dynamique. Cela fonctionne, bien sûr. J'ai une requête distincte pour générer le code de défragmentation. Je vais essayer ça et à quoi ça ressemble. Accepter cela comme réponse, cependant.
mdoyle
Une chose à noter ... cela fonctionne sur SQL Server 2008R2 Enterprise Edition. La «reconstruction avec (en ligne = activé)» n'est pas prise en charge sur l'édition standard.
datagod
1
Doux, j'ai suffisamment de représentants pour voter positivement sur votre réponse maintenant. :-) Il s'agit d'une requête simple, mais j'aime bien séparer la requête génératrice de code. J'aime exécuter les résultats sur du texte et pouvoir couper-coller dans un travail. Solution très élégante tout de même - merci!
mdoyle
8

L'alternative au SQL dynamique est SQLCMD , qui peut être invoquée à partir de la ligne de commande, une étape de travail d'agent, la cmdlet Invoke-Sqlcmd Powershell ou activée dans SSMS . Votre exemple dans la syntaxe SQLCMD serait:

:SETVAR DatabaseName MyDatabase

SELECT * FROM $(DatabaseName).sys.indexes;

Le mode SQLCMD est l'une de ces fonctionnalités que j'aurais aimé connaître plus tôt. Pratique dans de nombreuses situations.

Mark Storey-Smith
la source
1
C'est également assez simple et élégant à réaliser avec PowerShell. J'aime ton approche, Mark. +1
Thomas Stringer
@Shark Bon point, je vais modifier l'applet de commande PowerShell.
Mark Storey-Smith
Aussi une bonne solution, j'examinerai également cela.
mdoyle
C'est bien, y a-t-il une bonne raison pour laquelle cela n'est pas pris en charge dans TSQL?
tbone
0

Il est généralement difficile de référencer un ensemble de tables à partir d'une procédure contenue dans une base de données différente. Si vous installez votre procédure dans Master, en tant que procédure système, elle peut être utilisée dans d'autres contextes de base de données sans essayer de se référer à elle-même.

Je pense que: si votre procédure commence par 'sp_' alors elle devient universellement visible, et si vous la définissez dans le schéma 'sys.sp_%', alors elle peut être utilisée dans d'autres contextes DB.

Cela fournirait un autre moyen de fonctionner dans plusieurs bases de données sans avoir à brancher dynamiquement le nom_db.

silvertc
la source