Programmgesteuertes Referenzieren der Datenbank über T-SQL

11

Ich schreibe eine gespeicherte Prozedur, die einen Datenbanknamen als Argument verwendet und eine Tabelle der Indizes dieser Datenbank und ihrer Fragmentierungsstufe zurückgibt. Diese gespeicherte Prozedur befindet sich in unserer DBA-Datenbank (der Datenbank, die Tabellen enthält, die die DBAs zur Überwachung und Optimierung verwenden). Bei den fraglichen Systemen handelt es sich ausschließlich um SQL Server 2008 R2, wenn dies einen Unterschied macht.

Ich habe die grundlegende Abfrage ausgearbeitet, versuche aber nicht, die tatsächlichen Namen der Indizes anzugeben. Nach meinem besten Wissen sind diese Informationen in der sys.indexes-Ansicht jedes Einzelnen enthalten. Mein spezifisches Problem besteht darin, diese Ansicht programmgesteuert aus der gespeicherten Prozedur einer anderen Datenbank heraus zu referenzieren.

Zur Veranschaulichung ist dies der Teil der fraglichen Abfrage:

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

Die Abfrage funktioniert einwandfrei, wenn sie aus der durch @db_id angegebenen Datenbank ausgeführt wird, da sie die richtige Ansicht sys.indexes verwendet. Wenn ich versuche, dies aus der DBA-Datenbank aufzurufen, wird jedoch alles null angezeigt, da die Ansicht sys.indexes für die falsche Datenbank ist.

Allgemeiner gesagt muss ich in der Lage sein, so etwas zu tun:

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

oder

USE @db_name;

Ich habe versucht, Datenbanken zu wechseln oder andere Datenbanken mit Kombinationen aus Zeichenfolgenverkettung und OBJECT_NAME / OBJECT_ID / DB_ID-Funktionen zu referenzieren, und nichts scheint zu funktionieren. Ich würde mich über Ideen der Community freuen, aber ich vermute, dass ich diese gespeicherte Prozedur umrüsten muss, um sie in jeder einzelnen Datenbank zu speichern.

Vielen Dank im Voraus für alle Vorschläge.

mdoyle
quelle
1
Ich vermute, Sie müssen dafür dynamisches SQL verwenden ...
JNK

Antworten:

10

Dynamic SQL ist praktisch für diese Art von Verwaltungsaufgaben. Hier ist ein Ausschnitt aus einer gespeicherten Prozedur, die ich geschrieben habe und die nicht nur die Defragmentierungsstufen abruft, sondern auch den Code für die Defragmentierung generiert:

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
quelle
Aufgrund des Kommentars von JNK habe ich meine Abfrage geändert, um dynamisches SQL zu verwenden. Es funktioniert natürlich. Ich habe eine separate Abfrage, um den Defragmentierungscode zu generieren. Ich werde es versuchen und wie es aussieht. Akzeptiere dies jedoch als Antwort.
Mdoyle
Eine Sache zu beachten ... dies läuft auf SQL Server 2008R2 Enterprise Edition. Die "Neuerstellung mit (online = on)" wird in der Standard Edition nicht unterstützt.
Datum
1
Süß, ich habe genug Repräsentanten, um deine Antwort jetzt zu verbessern. :-) Dies ist eine raffinierte Abfrage, aber ich mag es, wenn die Code generierende Abfrage separat ist. Ich mag es, die Ergebnisse als Text auszuführen und in einen Job ausschneiden und einfügen zu können. Trotzdem sehr elegante Lösung - danke!
Mdoyle
8

Die Alternative zu dynamischem SQL ist SQLCMD , das über die Befehlszeile, einen Agentenjobschritt , das Cmdlet Invoke-Sqlcmd Powershell oder in SSMS aktiviert aufgerufen werden kann . Ihr Beispiel in der SQLCMD-Syntax wäre:

:SETVAR DatabaseName MyDatabase

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

Der SQLCMD-Modus ist eine der Funktionen, von denen ich mir wünschte, ich hätte sie früher gewusst. Praktisch in vielen Situationen.

Mark Storey-Smith
quelle
1
Dies ist auch mit PowerShell recht einfach und elegant zu bewerkstelligen. Ich mag deinen Ansatz, Mark. +1
Thomas Stringer
@ Shark Guter Punkt, ich werde das Powershell-Cmdlet in bearbeiten.
Mark Storey-Smith
Auch eine gute Lösung, ich werde mich auch darum kümmern.
Mdoyle
Das ist schön, gibt es einen guten Grund, warum dies in TSQL nicht unterstützt wird?
Knochen
0

Es ist normalerweise schwierig, einen Satz von Tabellen aus einer Prozedur zu referenzieren, die in einer anderen Datenbank enthalten ist. Wenn Sie Ihre Prozedur in Master als Systemprozedur installieren, kann sie in anderen DB-Kontexten verwendet werden, ohne dass versucht wird, auf sich selbst zurückzugreifen.

Ich denke das: Wenn Ihre Prozedur mit 'sp_' beginnt, wird sie allgemein sichtbar, und wenn Sie sie im Schema 'sys.sp_%' definieren, kann sie in anderen DB-Kontexten verwendet werden.

Dies würde eine alternative Möglichkeit bieten, in mehreren DBs zu arbeiten, ohne den DB-Namen dynamisch einstecken zu müssen.

silvertc
quelle