Ich versuche, einen Trigger zu erstellen, um die Sortierung einer Datenbank bei ihrer Erstellung zu ändern. Wie kann ich jedoch den Datenbanknamen abrufen, der im Trigger verwendet werden soll?
USE master
GO
CREATE TRIGGER trg_DDL_ChangeCOllationDatabase
ON ALL SERVER
FOR CREATE_DATABASE
AS
declare @databasename varchar(200)
set @databasename =db_name()
ALTER DATABASE @databasename COLLATE xxxxxxxxxxxxxxxxxxx
GO
Offensichtlich funktioniert das nicht.
sql-server
sql-server-2008-r2
trigger
collation
ddl
Racer SQL
quelle
quelle
Antworten:
Sie können im Allgemeinen keine Ausgabe
ALTER DATABASE
innerhalb eines Triggers (oder einer Transaktion mit anderen Anweisungen) ausgeben . Wenn Sie dies versuchen, wird folgende Fehlermeldung angezeigt:Der Grund, warum dieser Fehler in der Antwort von @ sp_BlitzErik nicht aufgetreten ist, ist ein Ergebnis des angegebenen speziellen Testfalls: Der oben gezeigte Fehler ist ein Laufzeitfehler, während der in seiner Antwort aufgetretene Fehler ein Fehler zur Kompilierungszeit ist. Dieser Fehler bei der Kompilierung verhindert die Ausführung des Befehls und daher gibt es keine "Laufzeit". Wir können den Unterschied erkennen, indem wir Folgendes ausführen:
Der obige Stapel wird fehlerhaft sein, während der folgende nicht:
Damit haben Sie zwei Möglichkeiten:
Übernehmen Sie die Transaktion innerhalb des DDL-Triggers so, dass die Transaktion keine weiteren Anweisungen enthält. Dies ist keine gute Idee, wenn es mehrere DDL-Trigger gibt, die von einer
CREATE DATABASE
Anweisung ausgelöst werden können , und ist möglicherweise eine schlechte Idee im Allgemeinen, aber es funktioniert ;-). Der Trick besteht darin, dass Sie auch eine neue Transaktion im Trigger starten müssen, da SQL Server sonst feststellt, dass die Anfangs- und Endwerte für@@TRANCOUNT
nicht übereinstimmen, und einen diesbezüglichen Fehler auslöst. Der folgende Code macht genau dies und gibt auch nur dann aus,ALTER
wenn die Sortierung nicht die gewünschte ist, andernfalls wird derALTER
Befehl übersprungen .Test mit:
Verwenden SQLCLR ein regelmäßigen / extern zu schaffenSqlConnection
, mitEnlist = false;
der Connection String, der zur Ausgabe vonALTER
Befehl als das nicht Teil der Transaktion sein wird.Es scheint, dass SQLCLR keine echte Option ist, obwohl dies nicht auf eine spezifische Einschränkung von SQLCLR zurückzuführen ist. Irgendwie hat die Eingabe von " da dies nicht Teil der Transaktion sein wird " direkt oben die Tatsache nicht ausreichend hervorgehoben, dass es tatsächlich eine aktive Transaktion um die
CREATE DATABASE
Operation gibt. Das Problem hierbei ist , dass während SQLCLR kann zu Schritt außerhalb der aktuellen Transaktion verwendet wird, gibt es noch keine Möglichkeit für eine weitere Sitzung der Datenbank zu ändern , zur Zeit erstellt wird , bis dass die erste Transaktion Commits.Das heißt, Sitzung A erstellt die Transaktion für die Erstellung der Datenbank und das Auslösen des Triggers. Der Abzug, mit SQLCLR wird Session B erstellen , die Datenbank zu ändern , die erstellt wurde, aber die Transaktion noch nicht begangen , da sie in der Warteschleife, bis Session B abgeschlossen ist , was es kann nicht , weil es für diese erste Transaktion wartet auf Komplett. Dies ist ein Deadlock, kann jedoch von SQL Server nicht als solcher erkannt werden, da nicht bekannt ist, dass Sitzung B von etwas in Sitzung A erstellt wurde. Dieses Verhalten kann durch Ersetzen des ersten Teils der
IF
Anweisung im Beispiel festgestellt werden oben in # 1 mit folgendem:Der
-t 15
Schalter für SQLCMD legt das Befehls- / Abfragezeitlimit fest, damit der Test mit dem Standardzeitlimit nicht ewig wartet. Aber du kannst es länger als 15 Sekunden einstellen und in einer anderen Sitzung überprüfensys.dm_exec_requests
, ob all die schönen Blockierungen stattfinden ;-).Stellen Sie das Ereignis an eine Stelle, die dann aus dieser Warteschlange gelesen wird, und führen Sie die entsprechende
ALTER DATABASE
Anweisung aus. Auf diese Weise kann dieCREATE DATABASE
Anweisung abgeschlossen und ihre Transaktion festgeschrieben werden. AnschließendALTER DATABASE
kann eine Anweisung ausgeführt werden. Service Broker könnte hier verwendet werden. ODER erstellen Sie eine Tabelle, lassen Sie den Trigger in diese Tabelle einfügen, und lassen Sie einen SQL Server-Agent-Job eine gespeicherte Prozedur aufrufen, die aus dieser Tabelle liest, dieALTER DATABASE
Anweisung ausführt und dann den Datensatz aus der Warteschlangentabelle entfernt.Die oben genannten Optionen werden jedoch hauptsächlich zur Unterstützung von Szenarien bereitgestellt, in denen jemand wirklich eine Art von
ALTER DATABASE
DDL-Trigger ausführen muss. Wenn Sie in diesem speziellen Szenario wirklich nicht möchten, dass Datenbanken die Standardkollatierung auf System- / Instanzebene verwenden, werden Sie wahrscheinlich am besten bedient von:Setup.exe /Q /ACTION=Rebuilddatabase /INSTANCENAME=<instancename> /SQLCOLLATION=...
. B. diese Option erstellt die System-DBs neu, sodass Sie sie benötigen um Objekte auf Serverebene usw. zu skripten, um sie später neu zu erstellen, und um Patches usw. erneut anzuwenden (FUN, FUN, FUN).Oder für Abenteuerlustige gibt es die undokumentierte
sqlservr.exe -q
Option (dh nicht unterstützte Option, die auf eigenes Risiko verwendet wird, aber möglicherweise sehr gut funktioniert) , mit der ALLE DBs und ALLE Spalten aktualisiert werden (siehe Ändern Die Zusammenstellung der Instanz, der Datenbanken und aller Spalten in allen Benutzerdatenbanken: Was könnte möglicherweise schief gehen? ( für eine detaillierte Beschreibung des Verhaltens dieser Option sowie des möglichen Einflussbereichs).Unabhängig von der gewählten Option: Stellen Sie immer sicher, dass Sie Backups von
master
und haben,msdb
bevor Sie solche Dinge versuchen.Der Grund, warum es sich lohnt, die Standardkollatierung auf Serverebene zu ändern, besteht darin, dass die Standardkollatierung der Instanz (dh auf Serverebene) einige Funktionsbereiche steuert, die zu unerwartetem / inkonsistentem Verhalten führen können, da jeder erwartet, dass Zeichenfolgenoperationen funktionieren in Anlehnung an die Standardkollatierung für alle Ihre Benutzerdatenbanken:
Standardkollatierung für Zeichenfolgenspalten in temporären Tabellen. Dies ist nur beim Vergleich mit / Unioning mit anderen Zeichenfolgenspalten ein Problem, wenn zwischen den beiden Zeichenfolgenspalten eine Nichtübereinstimmung besteht. Das Problem hierbei ist, dass
COLLATE
es viel wahrscheinlicher (wenn auch nicht garantiert) zu Problemen kommt, wenn die Sortierung nicht explizit über das Schlüsselwort angegeben wird.Dies ist kein Problem für den XML-Datentyp, Tabellenvariablen oder enthaltene Datenbanken.
Metadaten auf Instanzebene. In dem
name
Feld insys.databases
wird beispielsweise die Standardkollatierung auf Instanzebene verwendet. Andere Systemkatalogansichten sind ebenfalls betroffen, aber ich habe nicht die vollständige Liste.Metadaten auf Datenbankebene wie
sys.objects
undsys.indexes
sind nicht betroffen.@variable
)GOTO
EtikettenWenn bei der Sortierung auf Instanzebene beispielsweise die Groß- und Kleinschreibung nicht berücksichtigt wird, während die Sortierung auf Datenbankebene binär ist (dh mit
_BIN
oder endet_BIN2
), ist die Auflösung von Objektnamen auf Datenbankebene binär (z. B.[TableA] <> [tableA]
), bei Variablennamen wird jedoch die Groß- und Kleinschreibung nicht berücksichtigt (zB@VariableA = @variableA
).quelle
Sie müssten dynamisches SQL und die Funktion EVENTDATA () verwenden .
Geben Sie einfach Ihre Sammlung für meine Fälschung ein .
Jetzt, wenn ich eine Datenbank erstelle ...
Ich bekomme diese Nachricht (aus dem Druck):
Beachten Sie nur, dass beim Vergleich anderer Zeichenfolgendaten Probleme auftreten können, wenn andere Datenbanken (einschließlich Tempdb) andere Kollatierungen verwenden. Sie müssten COLLATE-Klauseln zu Zeichenfolgenvergleichen hinzufügen, bei denen Groß- und Kleinschreibung oder Akzente eine Rolle spielen, und selbst wenn dies nicht der Fall ist, können Sie Fehler feststellen. Verwandte Frage , wo ich in einem ähnlichen Code Problem lief hier .
quelle
Sie können nicht
ALTER DATABASE
in einem Auslöser. Sie müssen kreativ mit der Bewertung und Korrektur werden. Etwas wie:Sie sollten sp_MSforeachdb jedoch nicht verwenden .
quelle