Ich arbeite an einer maßgeschneiderten Wartungslösung unter Verwendung der sys.dm_db_index_physical_stats
Ansicht. Ich habe es derzeit von einer gespeicherten Prozedur verwiesen. Wenn diese gespeicherte Prozedur nun in einer meiner Datenbanken ausgeführt wird, wird ausgeführt, was ich möchte, und eine Liste aller Datensätze zu einer Datenbank wird abgerufen. Wenn ich es in eine andere Datenbank lege, ruft es eine Liste aller Datensätze auf, die sich nur auf diese Datenbank beziehen.
Zum Beispiel (Code unten):
- Der Abfragelauf für Datenbank 6 zeigt [angeforderte] Informationen für die Datenbanken 1-10 an.
- Der Abfragelauf für Datenbank 3 zeigt [angeforderte] Informationen nur für Datenbank 3 an.
Der Grund, warum ich diese Prozedur speziell für Datenbank drei möchte, ist, dass ich es vorziehen würde, alle Wartungsobjekte in derselben Datenbank zu belassen. Ich möchte diesen Job in der Wartungsdatenbank haben und so arbeiten, als ob er in dieser Anwendungsdatenbank wäre.
Code:
ALTER PROCEDURE [dbo].[GetFragStats]
@databaseName NVARCHAR(64) = NULL
,@tableName NVARCHAR(64) = NULL
,@indexID INT = NULL
,@partNumber INT = NULL
,@Mode NVARCHAR(64) = 'DETAILED'
AS
BEGIN
SET NOCOUNT ON;
DECLARE @databaseID INT, @tableID INT
IF @databaseName IS NOT NULL
AND @databaseName NOT IN ('tempdb','ReportServerTempDB')
BEGIN
SET @databaseID = DB_ID(@databaseName)
END
IF @tableName IS NOT NULL
BEGIN
SET @tableID = OBJECT_ID(@tableName)
END
SELECT D.name AS DatabaseName,
T.name AS TableName,
I.name AS IndexName,
S.index_id AS IndexID,
S.avg_fragmentation_in_percent AS PercentFragment,
S.fragment_count AS TotalFrags,
S.avg_fragment_size_in_pages AS PagesPerFrag,
S.page_count AS NumPages,
S.index_type_desc AS IndexType
FROM sys.dm_db_index_physical_stats(@databaseID, @tableID,
@indexID, @partNumber, @Mode) AS S
JOIN
sys.databases AS D ON S.database_id = D.database_id
JOIN
sys.tables AS T ON S.object_id = T.object_id
JOIN
sys.indexes AS I ON S.object_id = I.object_id
AND S.index_id = I.index_id
WHERE
S.avg_fragmentation_in_percent > 10
ORDER BY
DatabaseName, TableName, IndexName, PercentFragment DESC
END
GO
quelle
Antworten:
Eine Möglichkeit wäre, eine Systemprozedur
master
in Ihre Wartungsdatenbank einzufügen und anschließend einen Wrapper zu erstellen. Beachten Sie, dass dies jeweils nur für eine Datenbank funktioniert.Erstens im Meister:
Erstellen Sie nun in Ihrer Wartungsdatenbank einen Wrapper, der dynamisches SQL verwendet, um den Kontext korrekt festzulegen:
(Der Grund, warum der Datenbankname nicht wirklich sein kann,
NULL
liegt darin, dass Sie sich nicht mit Dingen wiesys.objects
und verknüpfen können,sys.indexes
da sie unabhängig in jeder Datenbank existieren. Wenn Sie instanzweite Informationen wünschen, müssen Sie möglicherweise eine andere Prozedur anwenden.)Jetzt können Sie dies für jede andere Datenbank aufrufen, z
Und Sie können jederzeit eine
synonym
in jeder Datenbank erstellen, sodass Sie nicht einmal auf den Namen der Wartungsdatenbank verweisen müssen:Eine andere Möglichkeit wäre, dynamisches SQL zu verwenden. Auch dies funktioniert jedoch jeweils nur für eine Datenbank:
Eine weitere Möglichkeit besteht darin, eine Ansicht (oder eine Tabellenwertfunktion) zu erstellen, um die Tabellen- und Indexnamen aller Ihrer Datenbanken zu vereinen. Sie müssen jedoch die Datenbanknamen in der Ansicht fest codieren und beim Hinzufügen beibehalten / remove Datenbanken, die in diese Abfrage aufgenommen werden sollen. Im Gegensatz zu den anderen können Sie so Statistiken für mehrere Datenbanken gleichzeitig abrufen.
Erstens ist die Ansicht:
Dann die Prozedur:
quelle
Nun, es gibt schlechte Nachrichten, gute Nachrichten mit einem Haken und einige wirklich gute Nachrichten.
Die schlechten Nachrichten
T-SQL-Objekte werden in der Datenbank ausgeführt, in der sie sich befinden. Es gibt zwei (nicht sehr nützliche) Ausnahmen:
sp_
und die in der[master]
Datenbank vorhanden sind (keine gute Option: jeweils eine Datenbank, die etwas hinzufügt[master]
, möglicherweise Synonyme zu jeder Datenbank hinzufügt, was für jede neue Datenbank durchgeführt werden muss)sp_
gespeicherten Prozess in[master]
.Die gute Nachricht (mit einem Haken)
Viele (vielleicht die meisten?) Leute kennen die eingebauten Funktionen, um einige wirklich häufige Metadaten zu erhalten:
Die Verwendung dieser Funktionen kann die Notwendigkeit für die JOINs eliminieren
sys.databases
(obwohl dies kein wirkliches Problem ist),sys.objects
(gegenübersys.tables
denen, die indizierte Ansichten ausschließen , bevorzugt ) undsys.schemas
(Sie haben diese Funktion vermisst, und nicht alles ist imdbo
Schema enthalten ;-). Aber auch wenn wir drei der vier JOINs entfernen, sind wir funktionell immer noch am selben Ort, oder? Falsch!Eine der netten Eigenschaften der
OBJECT_NAME()
undOBJECT_SCHEMA_NAME()
Funktionen ist, dass sie einen optionalen zweiten Parameter für haben@database_id
. Das heißt, während der Beitritt zu diesen Tabellen (mit Ausnahme vonsys.databases
) datenbankspezifisch ist, erhalten Sie mit diesen Funktionen serverweite Informationen. Sogar OBJECT_ID () ermöglicht serverweite Informationen, indem es einen vollständig qualifizierten Objektnamen erhält .Indem wir diese Metadatenfunktionen in die Hauptabfrage integrieren, können wir sie vereinfachen und gleichzeitig über die aktuelle Datenbank hinaus erweitern. Der erste Durchgang des Refactorings der Abfrage ergibt Folgendes:
Und jetzt zum "catch": Es gibt keine Metadatenfunktion, um Indexnamen zu erhalten, geschweige denn eine serverweite. Also ist es das? Sind wir zu 90% vollständig und müssen uns immer noch in einer bestimmten Datenbank befinden, um
sys.indexes
Daten abzurufen? Müssen wir wirklich eine gespeicherte Prozedur erstellen, um mithilfe von Dynamic SQL bei jeder Ausführung unseres Hauptprozesses eine temporäre Tabelle allersys.indexes
Einträge in allen Datenbanken aufzufüllen, damit wir der Prozedur beitreten können? NEIN!Die wirklich guten Nachrichten
Ein kleines Feature, das manche Leute gerne hassen, aber bei richtiger Anwendung erstaunliche Dinge bewirken können. Ja: SQLCLR. Warum? Da SQLCLR-Funktionen offensichtlich SQL-Anweisungen übermitteln können, es sich jedoch aufgrund der Art der Übermittlung aus dem Anwendungscode um dynamisches SQL handelt. Im Gegensatz zu T-SQL-Funktionen können SQLCLR-Funktionen einen Datenbanknamen in die Abfrage einfügen, bevor sie ausgeführt werden. Das heißt, wir können unsere eigene Funktion erstellen, um die Fähigkeit von
OBJECT_NAME()
undOBJECT_SCHEMA_NAME()
zu übernehmendatabase_id
und die Informationen für diese Datenbank abzurufen.Der folgende Code ist diese Funktion. Es wird jedoch ein Datenbankname anstelle einer ID verwendet, damit der zusätzliche Schritt zum Nachschlagen nicht erforderlich ist (wodurch die Suche etwas weniger kompliziert und etwas schneller wird).
Sie werden feststellen, dass wir die Kontextverbindung verwenden, die nicht nur schnell ist, sondern auch in
SAFE
Baugruppen funktioniert . Ja, dies funktioniert in einer Assembly, die als markiert istSAFE
Daher sollte es (oder Abwandlungen davon) auch in Azure SQL Database V12 funktionieren(Die Unterstützung für SQLCLR wurde im April 2016 ziemlich plötzlich aus der Azure SQL-Datenbank entfernt .)Unser zweites Refactoring der Hauptabfrage ergibt also Folgendes:
Das ist es! Sowohl diese SQLCLR Scalar-UDF als auch Ihre gespeicherte T-SQL-Wartungsprozedur können in derselben zentralen
[maintenance]
Datenbank gespeichert werden. UND Sie müssen nicht immer nur eine Datenbank gleichzeitig verarbeiten. Jetzt haben Sie Metadaten-Funktionen für alle abhängigen Informationen, die serverweit sind.PS: Es gibt keine
.IsNull
Überprüfung der Eingabeparameter im C # -Code, da das T-SQL-Wrapperobjekt mit der folgendenWITH RETURNS NULL ON NULL INPUT
Option erstellt werden sollte:Zusätzliche Bemerkungen:
Die hier beschriebene Methode kann auch zur Lösung anderer, sehr ähnlicher Probleme bei fehlenden datenbankübergreifenden Metadatenfunktionen verwendet werden. Der folgende Microsoft Connect-Vorschlag ist ein Beispiel für einen solchen Fall. Und da Microsoft es als "Won't Fix" geschlossen hat, ist es offensichtlich, dass sie nicht daran interessiert sind, integrierte Funktionen
OBJECT_NAME()
bereitzustellen, die diese Anforderungen erfüllen (daher die Problemumgehung, die in diesem Vorschlag angegeben ist :-).Fügen Sie eine Metadatenfunktion hinzu, um den Objektnamen von hobt_id abzurufen
Um mehr über die Verwendung von SQLCLR zu erfahren, werfen Sie einen Blick auf die Treppen zu SQLCLR- Reihe, die ich in SQL Server Central schreibe (kostenlose Registrierung erforderlich; ich habe leider keine Kontrolle über die Richtlinien dieser Site).
Die
IndexName()
oben gezeigte SQLCLR-Funktion ist in einem einfach zu installierenden Skript in Pastebin vorkompiliert verfügbar. Das Skript aktiviert die Funktion "CLR-Integration", sofern sie nicht bereits aktiviert ist, und die Assembly wird als markiertSAFE
. Es wurde mit .NET Framework Version 2.0 kompiliert, sodass es in SQL Server 2005 und neueren Versionen (dh allen Versionen, die SQLCLR unterstützen) funktioniert.SQLCLR Metadatenfunktion für datenbankübergreifenden IndexName ()
Wenn sich jemand für die
IndexName()
SQLCLR-Funktion und über 320 weitere Funktionen und gespeicherte Prozeduren interessiert , ist sie in der SQL # -Bibliothek (deren Autor ich bin) verfügbar . Beachten Sie, dass die Funktion Sys_IndexName , solange es eine kostenlose Version gibt, nur in der Vollversion verfügbar ist (zusammen mit einer ähnlichen Funktion Sys_AssemblyName ).quelle