我在表创建后获得了一个数据库触发器。
CREATE TRIGGER tr_Timestamps_TableTriggersCreation
ON DATABASE
AFTER CREATE_TABLE
AS
BEGIN
SET NOCOUNT ON
DECLARE @tableName SYSNAME
DECLARE @schemaName SYSNAME = NULL
DECLARE @totalRows INT = 0;
SELECT @tableName = EVENTDATA().value('(/EVENT_INSTANCE/ObjectName)[1]','SYSNAME')
SELECT TOP 1 @schemaName = s.name
FROM sys.tables t
JOIN sys.columns c ON c.object_id = t.object_id
JOIN sys.schemas s ON s.schema_id = t.schema_id
WHERE t.name = @tableName
AND c.name = 'ID'
IF (@schemaName != '')
BEGIN
EXEC u_general.sp_Timestamps_TriggerForTableCreation @tableName, @schemaName, 'I'
EXEC u_general.sp_Timestamps_TriggerForTableCreation @tableName, @schemaName, 'U'
END
它背后的想法是为在该数据库上创建的每个表创建2个触发器。
我的问题如下,
当有人使用MSSQL'编辑器'并使用' Design'选项而不是编写脚本来进行更改,例如标记列以及“否则为空”,更改数据类型等... 我的数据库触发器将其计为新表创建,并启动为该表创建触发器的过程。 最终结果是它尝试再次创建这些触发器,并且我收到一条错误消息,表明这些触发器已经存在。
ALTER PROCEDURE [u_general].[sp_Timestamps_TriggerForTableCreation] ( @tableName sysname, @schemaName sysname, @actionType char(1))
AS
DECLARE @TrigerName NVARCHAR(50)
DECLARE @AfterActionName NVARCHAR(50)
IF @actionType = 'I'
BEGIN
SET @TrigerName = 'tr_Timestamps_CaptureAfterInsert_' + @tableName
SET @AfterActionName = 'INSERT'
END
ELSE -- 'U'
BEGIN
SET @TrigerName = 'tr_Timestamps_CaptureAfterUpdate_' + @tableName
SET @AfterActionName = 'UPDATE'
END
DECLARE @SQLCommand nvarchar(max)=
'CREATE TRIGGER ' + @TrigerName +'
ON ' + @schemaName + '.' + @tableName +
' AFTER '+ @AfterActionName +'
AS
DECLARE @auditBody XML
DECLARE @RowID int
SELECT @RowID = INSERTED.ID FROM INSERTED
SELECT @auditBody =
''<Timestamps_Request>
<DataBaseName>'' + DB_NAME() + ''</DataBaseName>
<SchemaName>'' + ''' + @schemaName + ''' + ''</SchemaName>
<TableName>'' + ''' + @tableName + '''+ ''</TableName>
<RowID>'' + CAST(@RowID AS NVARCHAR(30)) + ''</RowID>
<Action>'' + '''+@actionType+''' + ''</Action>
</Timestamps_Request>''
EXEC u_general.sp_Timestamps_SendBrokerMessage @FromService = ''Timestamps_RequestService'',
@ToService = ''Timestamps_ProcessingService'',
@Contract = ''Timestamps_Contract'',
@MessageType = ''Timestamps_Request'',
@MessageBody = @auditBody ';
EXEC sp_executeSQL @SQLCommand
我的问题是,
我可以做些什么来确保每次有人决定使用设计师来改变这种情况时都不会发生这种情况。一张桌子?
P.S。根据我的理解,它与SSMS(导致drop和create?)及其工作方式有关,这就是为什么我在使用脚本修改表而不是MSSQL设计器时没有这个问题。
答案 0 :(得分:1)
快速注释。
将DDL Events与CREATE_TABLE
一起使用并确认您未在触发器定义中使用Alter_TABLE
,因为CREATE_TABLE
和Alter_TABLE
是完全分开的事件。< / p>
按照下一个演示了解更多详情。
<强>演示: - 强>
Create table table1 (col1 int, col2 nvarchar(10) not null )
go
Create TRIGGER NoCreateNewTables ON DATABASE
FOR CREATE_TABLE
AS
Print 'Prevent Table Creation'
BEGIN
ROLLBACK;
END
GO
让Alter
Table1
通过下一个:
ALTER TABLE table1 ALTER COLUMN col2 nvarchar(10) NULL
<强>结果: - 强>
让Create
新表Table2
通过下一个:
Create table table2 (col1 int, col2 nvarchar(10) not null )
<强>结果: - 强>
直接答案从这里开始
在某些情况下,SQL Server在使用SSMS
设计器时删除并重新创建表:
常见情况: -
经过一些调查,我注意到Object_Name
通过SSMS
设计师改编的Tmp_
以SSMS
开头。
所以根据这个信息,我们可以通过创建触发器来阻止通过/* Create Trigger for Preventing altering table Via SSMS while table recreated */
Create TRIGGER TrgPreventAlterTableIfTableRecreatedViaSSMSDesign ON DATABASE
FOR CREATE_TABLE
AS
BEGIN
DECLARE
@eventInfo XML,
@ObjectName varchar(100)
SET
@eventInfo = EVENTDATA()
select @ObjectName = CONVERT(SYSNAME, @eventInfo.query('data(/EVENT_INSTANCE/ObjectName)'))
if (Left(@ObjectName,3) = 'Tmp')
begin
exec sp_addmessage 50001, 16,
N'Cannot modify Table Via SSMS, use [Alter Table] Code instead';
RAISERROR(50001,16,1)
exec sp_dropmessage 50001
rollback;
end
END
设计器改变表,以防止在重新创建表时从SSMS中更改表。
<强>演示: - 强>
Table1
现在尝试改变SSMS
通过NULL
设计师从NOT NULL
到{{1}}
将提出下一个流行消息: -