Triggers 防止数据库丢失的SQL Server触发器

Triggers 防止数据库丢失的SQL Server触发器,triggers,sql-server-2012,eventtrigger,Triggers,Sql Server 2012,Eventtrigger,我想放置一个服务器级触发器,以防止删除任何不是数据库快照的数据库。乍一看,下面这句话似乎应该管用,但从来没有管用过。我试着颠倒逻辑,但没用。有人知道我做错了什么吗 DECLARE @DBName NVARCHAR(100), @eventData XML; SET @eventData = EVENTDATA(); SELECT @DBName = @eventData.value('data(/EVENT_INSTANCE/DatabaseName)[

我想放置一个服务器级触发器,以防止删除任何不是数据库快照的数据库。乍一看,下面这句话似乎应该管用,但从来没有管用过。我试着颠倒逻辑,但没用。有人知道我做错了什么吗

DECLARE @DBName NVARCHAR(100),
        @eventData XML;

SET @eventData = EVENTDATA();           
SELECT  @DBName = @eventData.value('data(/EVENT_INSTANCE/DatabaseName)[1]', 'SYSNAME');

RAISERROR('Attempting delete of %s.', 10, 1, @DBName);

IF @DBName IN (SELECT name
               FROM sys.databases 
               WHERE source_database_id IS NOT NULL)
    BEGIN
        RAISERROR('[%s] was successfully dropped.', 10, 1, @DBname) WITH LOG;
    END;
ELSE         
    BEGIN
        RAISERROR('[%s] cannot be deleted without first disabling the server trigger "tgr_prevent_db_drop".', 10, 1, @DBname) WITH LOG;
        ROLLBACK;
    END;

顶部的RAISERROR始终确认正确的数据库(例如,正在删除的数据库),当我手动运行SELECT from sys.databases时,它始终返回适当的数据。不幸的是,无论我做什么,对于真正的数据库和它们的数据库快照,这总是属于“.was successfully drop”部分。

这种情况总是正确的

IF @DBName IN (SELECT name
               FROM sys.databases 
               WHERE source_database_id IS NOT NULL)
    BEGIN
        RAISERROR('[%s] was successfully dropped.', 10, 1, @DBname) WITH LOG;
    END;
所以,无论发生什么情况,您的数据库都将被删除。请改为将下面的else子句移到上面的块

RAISERROR('[%s] cannot be deleted without first disabling the server trigger "tgr_prevent_db_drop".', 10, 1, @DBname) WITH LOG;
        ROLLBACK;

我也尝试过让它工作,但由于sys.databases只返回当前用户可见的值,因此遇到了太多的权限障碍。(我无法通过“执行为”的尝试获得足够可靠的通用解决方案。)

最后,我决定使用数据库名称作为过滤器。例如:

DECLARE @DBName NVARCHAR(100),
        @eventData XML;

SET @eventData = EVENTDATA();           
SELECT  @DBName = @eventData.value('data(/EVENT_INSTANCE/DatabaseName)[1]', 'SYSNAME');

RAISERROR('Attempting delete of %s.', 10, 1, @DBName);

IF Right(@DBName, 9) <> '_SnapShot' --Checking via db name due to permissions affecting sys.databases
    BEGIN
        RAISERROR('[%s] was successfully dropped.', 10, 1, @DBname) WITH LOG;
    END;
ELSE         
    BEGIN
        RAISERROR('[%s] cannot be deleted without first disabling the server trigger "tgr_prevent_db_drop".', 10, 1, @DBname) WITH LOG;
        ROLLBACK;
    END;
DECLARE@DBName NVARCHAR(100),
@事件数据XML;
设置@eventData=eventData();
选择@DBName=@eventData.value('data(/EVENT_INSTANCE/DatabaseName)[1],'SYSNAME');
RAISERROR('正在尝试删除%s',10,1,@DBName);
如果正确(@DBName,9)'u SnapShot'--由于权限影响sys.databases,请通过db name进行检查
开始
RAISERROR(“[%s]已成功删除。”、10、1、@DBname)与日志;
结束;
其他的
开始
RAISERROR('[%s]无法删除,除非先禁用带有日志的服务器触发器“tgr\u protect\u db\u drop”。',10,1,@DBname);
回降;
结束;

Hi@TheGameIswar,我没有跟踪。如果数据库是一个快照,它将在source_database_id列中有一个值,并显示在SELECT中,因此该条件应始终为true。如果它不是快照,则源数据库id必须为NULL,因此我希望它的计算结果为false。如果我在DDL触发器之外运行它,它将按预期工作。只有在DDL触发器内部才会失败。最终,我希望允许删除快照,但阻止删除父数据库。@PseudoToad,sys.databases存在基于用户权限返回值的问题。如果您的触发器未在sys.databases中看到快照数据库,则触发器将作为没有权限的用户执行。