Benutzerdefinierte Funktion (UDF) in der Master-Datenbank

9

Ich habe eine Funktion, die in mehr als einem Dutzend Datenbanken verwendet wird. Ich möchte, dass auf diese Funktion in allen Datenbanken zugegriffen werden kann, indem sie nur einmal geschrieben wird. Daher möchte ich die Funktion in die Master-Datenbank schreiben, damit auf meine Funktion problemlos zugegriffen werden kann.

Gibt es ein Problem beim Schreiben der benutzerdefinierten Funktion in der Master-Datenbank?

Beim Surfen im Internet wurde empfohlen, die Funktion in eine Systemfunktion umzuwandeln. (Verwenden von "EXEC sp_ms_marksystemobject 'fn_db_name'").

Muss die in master db erstellte Funktion in eine Systemfunktion konvertiert werden? Wenn ja warum?

IT-Forscher
quelle
Wenn die Funktion einfach eine Operation für übergebene Daten ausführt und das Ergebnis zurückgibt, sollte es kein Problem geben. Ich habe Probleme bei der Verwendung einer in master erstellten gespeicherten Prozedur festgestellt, die auf Systemansichten wie information_schema verweist. Dies ist jedoch lange her. Was auch immer Sie erstellen, Sie müssen es vollständig testen.
Datum
5
Ich würde davon abraten, es in Master zu setzen. Erstellen Sie eine Dienstprogrammdatenbank und legen Sie sie dort ab. Sie müssen sicherstellen, dass Sie im Rahmen Ihres DR-Plans die Quelle für das Objekt im Master haben (normalerweise stellen Sie die Systemdatenbanken nicht wieder her) und dass diese auf dem neuesten Stand ist.
Jonathan Fite
Was genau soll diese Funktion tun?
Solomon Rutzky

Antworten:

10

Ich stimme Jonathan zu - solange die Funktion keine lokalen Daten basierend auf dem aufrufenden Datenbankkontext zurückgeben soll, stellen Sie die Funktion in eine Dienstprogrammdatenbank (hier füge ich auch Dinge wie Zahlen und Kalendertabellen, Aufteilungsfunktionen usw. Ein ):

USE UtilityDB;
GO
CREATE FUNCTION dbo.whatever() RETURNS ...
GO

Erstellen Sie jetzt in jeder Datenbank, die auf die Funktion zugreifen muss, einfach ein Synonym:

CREATE SYNONYM dbo.whatever() FOR UtilityDB.dbo.whatever();

Auf diese Weise kann jede Datenbank weiterhin mit einem einfachen zweiteiligen Namen auf die Funktion verweisen - und sie müssen nicht wissen oder sich darum kümmern, wo sie tatsächlich vorhanden ist. Und Sie müssen nur eine einzige Kopie der Funktion pflegen.

(Tatsächlich können Sie das Synonym in die modelDatenbank einfügen, sodass es automatisch in neuen Datenbanken erstellt wird.)

Der Grund, warum ich Benutzerobjekte masternicht gerne einfüge - es sei denn, sie müssen wirklich global verfügbar und kontextbezogen für die aufrufende Datenbank sein, wie Ihre eigene angepasste Version von sp_spaceusedoder sp_helpindex-, ist, dass Benutzer nicht nach Benutzerobjekten suchen masterund weniger dort auffindbar. Sie erschweren auch die Migration Ihrer Benutzerdatenbanken an einen anderen Ort, da Sie sich auch an die von Ihnen eingegebenen Benutzerdaten erinnern müssen master. Wenn Sie absolut nicht in der msdbLage sind , eine Dienstprogrammdatenbank dafür zu erstellen oder zu verwenden, ist dies meiner Meinung nach eine praktischere Wahl für einen zentralen Standort.

Aaron Bertrand
quelle
5

Obwohl ich @Jonathan und @Aaron zustimme, dass Sie eine "Dienstprogramm" -Datenbank verwenden sollten (ich nenne sogar meine Utility), und ich Aaron bei der Verwendung von Synonymen zustimme, möchte ich auf die Einschränkung aufmerksam machen, die Aaron so erwähnt hat wichtig:

solange die Funktion keine lokalen Daten basierend auf dem aufrufenden Datenbankkontext zurückgeben soll ...

Das heißt, wenn Sie noch den „aktuelle Datenbank“ Kontext müssen (im Gegensatz zu dem Code Gegensatz in der Datenbank ausgeführt wird, wo es tatsächlich existiert, ob es sein masteroder Utility), dann etwas mit , aber masternicht funktionieren.

Wenn Sie also die Funktion benötigen, um im Kontext der Datenbank ausgeführt zu werden, von der aus sie aufgerufen wird , funktioniert der Plan zur Verwendung einer Funktion ohnehin nicht, da Code in der masterDatenbank vorhanden ist, der im aktuellen Datenbankkontext ausgeführt werden kann und masternur funktioniert für gespeicherte Prozeduren, auch wenn Sie die undokumentierte sp_ms_marksystemobjectgespeicherte Systemprozedur verwenden, um Ihre Funktion als Systemobjekt zu markieren. Wenn Sie dem Namen der Funktion ein Präfix voranstellen fn_oder sogar sp_nicht zulassen, dass er außerhalb des Masters gefunden wird, ohne den Objektnamen vollständig zu qualifizieren. Selbst wenn Sie den Namen vollständig qualifizieren master, wird er auch nach dem Markieren als Systemobjekt nur im Kontext der Datenbank ausgeführt.

Sie haben also drei Möglichkeiten:

  1. Wenn Sie keinen aktuellen DB-Kontext benötigen, befolgen Sie die Anweisungen von Aaron, um eine UtilityDatenbank für das Objekt zu verwenden, und verwenden Sie Synonyme in den Datenbanken, die die Funktion verwenden müssen, um auf die UtilityDatenbank zu verweisen .
  2. Wenn Sie den aktuellen DB-Kontext benötigen:

    1. Ändern Sie die Funktion in eine gespeicherte Prozedur, benennen Sie sie zunächst um sp_und führen Sie die sp_ms_marksystemobjectgespeicherte Systemprozedur darauf aus, um sie als gespeicherte Systemprozedur zu markieren (andernfalls wird der aktuelle DB-Kontext für Abfragen nicht wiedergegeben, obwohl dies für einige erstellte Prozeduren der Fall ist -in funktioniert wie DB_NAME()). Ich bin kein großer Fan dieses Ansatzes, aber es funktioniert.
    2. Verwenden Sie SQLCLR, um eine Funktion zu erstellen, die in die UtilityDatenbank eingefügt werden kann und an die der aktuelle Datenbankname (oder ein beliebiger Datenbankname) übergeben wird, um das dynamische SQL zu erstellen, das die übermittelte Abfrage sein wird. In der folgenden Antwort habe ich ein Beispiel dafür mit SQLCLR veröffentlicht, auch auf DBA.StackExchange:

      Zentrale gespeicherte Prozedur zur Ausführung im aufrufenden Datenbankkontext

      Sie können bei diesem Ansatz auch Synonyme verwenden. Anschließend rufen Sie die Funktion beim Übergeben auf DB_NAME(), um den aktuellen Datenbanknamen automatisch einzugeben.

Solomon Rutzky
quelle