J'essaie de créer un déclencheur, de modifier le classement d'une base de données lors de sa création, mais comment puis-je attraper le nom de la base de données à utiliser dans le déclencheur?
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
De toute évidence, cela ne fonctionne pas.
sql-server
sql-server-2008-r2
trigger
collation
ddl
Racer SQL
la source
la source
Réponses:
Vous ne pouvez pas, en règle générale, émettre
ALTER DATABASE
au sein d'un déclencheur (ou de toute transaction contenant d'autres déclarations). Si vous tentez de le faire, vous obtiendrez l'erreur suivante:La raison pour laquelle cette erreur n'a pas été rencontrée dans la réponse de @ sp_BlitzErik est le résultat du scénario de test spécifique fourni: l'erreur indiquée ci-dessus est une erreur d'exécution, tandis que l'erreur rencontrée dans sa réponse est une erreur de compilation. Cette erreur au moment de la compilation empêche l'exécution de la commande et il n'y a donc pas de "temps d'exécution". Nous pouvons voir la différence en exécutant ce qui suit:
Le lot ci-dessus entraînera une erreur, tandis que les suivants ne le seront pas:
Cela vous laisse deux options:
Validez la transaction dans le déclencheur DDL de sorte qu'il n'y ait aucune autre instruction dans la transaction. Ce n'est pas une bonne idée s'il existe plusieurs déclencheurs DDL qui peuvent être déclenchés par une
CREATE DATABASE
instruction, et c'est peut-être une mauvaise idée en général, mais cela fonctionne ;-). L'astuce est que vous devez également commencer une nouvelle transaction dans le déclencheur sinon SQL Server remarquera que les valeurs de début et de fin de@@TRANCOUNT
ne correspondent pas et générera une erreur liée à cela. Le code ci-dessous ne fait que cela, et émet également uniquementALTER
si le classement n'est pas celui souhaité, sinon il ignore laALTER
commande.Testez avec:
Utilisez SQLCLR pour établir un régulier / externeSqlConnection
, avecEnlist = false;
dans la chaîne de connexion, pour émettre laALTER
commande car cela ne fera pas partie de la transaction.Il semble que SQLCLR ne soit pas vraiment une option, mais pas en raison d'une limitation spécifique de SQLCLR. D'une manière ou d'une autre, taper " car cela ne fera pas partie de la transaction " directement ci-dessus n'a pas suffisamment mis en évidence le fait qu'il existe, en fait, une transaction active autour de l'
CREATE DATABASE
opération. Le problème ici est que même si SQLCLR peut être utilisé pour sortir de la transaction en cours, il n'y a toujours aucun moyen pour une autre session de modifier la base de données en cours de création jusqu'à ce que la transaction initiale soit validée.Signification, la session A crée la transaction pour la création de la base de données et le déclenchement du déclencheur. Le déclencheur, à l'aide de SQLCLR, créera la session B pour modifier la base de données qui a été créée, mais la transaction n'a pas encore été validée car elle est en attente jusqu'à la fin de la session B, ce qu'elle ne peut pas car elle attend cette transaction initiale pour Achevée. Il s'agit d'un blocage, mais il ne peut pas être détecté en tant que tel par SQL Server car il ne sait pas que la session B a été créée par quelque chose dans la session A. Ce comportement peut être vu en remplaçant la première partie de l'
IF
instruction dans l'exemple ci-dessus dans # 1 avec ce qui suit:Le
-t 15
commutateur pour SQLCMD définit le délai d'expiration de la commande / requête afin que le test n'attende pas indéfiniment avec le délai d'expiration par défaut. Mais, vous pouvez le définir pour qu'ilsys.dm_exec_requests
dure plus de 15 secondes et dans une autre session, vérifiez tous les blocages en cours ;-).Mettez l'événement en file d'attente quelque part qui sera ensuite lu à partir de cette file d'attente et exécutez l'
ALTER DATABASE
instruction appropriée . Cela permettra à l'CREATE DATABASE
instruction de se terminer et à sa transaction de se valider, après quoi uneALTER DATABASE
instruction peut être exécutée. Service Broker peut être utilisé ici. OU, créez une table, insérez le déclencheur dans cette table, puis demandez à un travail de l'Agent SQL Server d'appeler une procédure stockée qui lit à partir de cette table et exécute l'ALTER DATABASE
instruction, puis supprime l'enregistrement de la table de file d'attente.TOUTEFOIS, les options ci-dessus sont principalement fournies pour aider dans les scénarios où quelqu'un a vraiment besoin de faire un certain type de
ALTER DATABASE
déclencheur DDL. Dans ce scénario particulier, si vous ne voulez vraiment pas que des bases de données utilisent le classement par défaut au niveau système / instance, vous serez probablement mieux servi par:Setup.exe /Q /ACTION=Rebuilddatabase /INSTANCENAME=<instancename> /SQLCOLLATION=...
; cette option recrée les bases de données système, vous aurez donc besoin pour créer des scripts pour les objets au niveau du serveur, etc. pour recréer plus tard, ainsi que pour réappliquer les correctifs, etc., FUN, FUN, FUN).Ou, pour les aventuriers de cœur, il y a l'
sqlservr.exe -q
option non documentée (c'est-à-dire non prise en charge, à vos risques et périls mais pourrait très bien fonctionner) qui met à jour TOUTES les bases de données et TOUTES les colonnes (veuillez consulter Modification le classement de l'instance, des bases de données et de toutes les colonnes dans toutes les bases de données utilisateur: qu'est-ce qui pourrait éventuellement mal tourner? pour une description détaillée du comportement de cette option, ainsi que de la portée potentielle de l'impact).Quelle que soit l'option choisie: assurez-vous toujours d'avoir des sauvegardes de
master
etmsdb
avant d'essayer de telles choses.La raison pour laquelle cela vaut la peine de modifier le classement par défaut au niveau du serveur est que le classement par défaut de l'instance (c'est-à-dire au niveau du serveur) contrôle quelques domaines fonctionnels qui pourraient conduire à un comportement inattendu / incohérent, car tout le monde s'attend à ce que les opérations de chaîne fonctionnent dans le sens du classement par défaut pour toutes vos bases de données utilisateur:
Classement par défaut des colonnes de chaînes dans les tables temporaires. Il s'agit d'un problème uniquement lors de la comparaison avec / Union avec d'autres colonnes de chaînes SI il existe une incompatibilité entre les deux colonnes de chaînes. Le problème ici est que lorsque vous ne spécifiez pas explicitement le classement via le
COLLATE
mot clé, il est beaucoup plus probable (mais non garanti) de rencontrer des problèmes.Ce n'est pas un problème pour le type de données XML, les variables de table ou les bases de données contenues.
Métadonnées au niveau de l'instance. Par exemple, le
name
champ danssys.databases
utilisera le classement par défaut au niveau de l'instance. D'autres vues du catalogue système sont également affectées, mais je n'ai pas la liste complète.Les métadonnées au niveau de la base de données, telles que
sys.objects
etsys.indexes
, ne sont pas affectées.@variable
)GOTO
ÉtiquettesPar exemple, si le classement au niveau de l'instance est insensible à la casse alors que le classement au niveau de la base de données est binaire (c'est-à-dire se terminant par
_BIN
ou_BIN2
), la résolution de nom d'objet au niveau de la base de données sera binaire (par exemple[TableA] <> [tableA]
), mais les noms de variables permettront une insensibilité à la casse (par exemple@VariableA = @variableA
).la source
Vous devez utiliser SQL dynamique et la fonction EVENTDATA () .
Juste sous votre collation pour mon faux .
Maintenant, quand je crée une base de données ...
Je reçois ce message (à partir de l'impression):
Notez simplement que si d'autres bases de données (y compris tempdb) utilisent des classements différents, vous pouvez rencontrer des problèmes de comparaison des données de chaîne. Vous devez ajouter des clauses COLLATE aux comparaisons de chaînes lorsque la casse ou les accents sont importants, et même lorsqu'ils ne le sont pas, vous pouvez rencontrer des erreurs. Question connexe où j'ai rencontré un problème de code similaire ici .
la source
Vous ne pouvez pas
ALTER DATABASE
dans un déclencheur. Vous devrez faire preuve de créativité avec l'évaluation et la correction. Quelque chose comme:Bien que vous ne deviez pas utiliser sp_MSforeachdb .
la source