SELECT INTO会中断DDL / DML触发器,但先执行CREATE TABLE,然后执行INSERT的工作?

问题描述 投票:0回答:1

在SQL Server 2017(SSMS v18.3)中,我正在使用DDL触发器为给定的output表创建input表。

然后,我在input表上创建DML触发器以填充output表。

[当使用CREATE TABLE,然后使用INSERT INTO时,此功能按预期工作

[使用SELECT INTO时查询抛出此错误:

Msg 539, Level 16, State 78, Line 50
Schema changed after the target table was created. Rerun the Select Into query.

并且在创建inputoutput和触发器时,没有插入任何数据。

我在查询的每个部分都尝试过CATCH RETRY,但无法固定它。

如何在仍然使用SELECT INTO[input]模式中创建表的同时如何克服此错误?

并且创建了SELECT INTO表的模式是什么?

这里是数据库小提琴https://dbfiddle.uk/?rdbms=sqlserver_2017&fiddle=db65a8f956045cf1376b35ff9e782642

这是我的代码:

USE TestData
GO
/*--
  CREATE SCHEMA input
  GO
  CREATE SCHEMA output
  GO
--*/
;IF EXISTS ( SELECT * FROM sys.triggers WHERE name = 'CreateDMLTriggerOnTable') DROP TRIGGER CreateDMLTriggerOnTable ON DATABASE;
GO 
CREATE TRIGGER CreateDMLTriggerOnTable ON DATABASE
AFTER CREATE_TABLE
AS   
  declare @schemaname as varchar(128) = 'input'
  --Only act on tables in our target scheme [input]
  ;IF EVENTDATA().value('(/EVENT_INSTANCE/SchemaName)[1]','varchar(128)') <> @schemaname RETURN;

  declare @tablename as varchar(128) = EVENTDATA().value('(/EVENT_INSTANCE/ObjectName)[1]','nvarchar(max)')
  declare @tb_input as varchar(261) = '[input].[' + @tablename + ']'
  declare @tb_output as varchar(261) = '[output].[' + @tablename + ']'

  --Create a matching [output] table for the newly created [input] table
  declare @tmpsql as nvarchar(max)
  set @tmpsql = 'CREATE TABLE ' + @tb_output + ' ( CountOfRows tinyint ) '
  print isnull(@tmpsql,'NULLED!')
  EXEC(@tmpsql)

  --Populate [output] with data from [input] table using DML trigger
  set @tmpsql = ''
  set @tmpsql = @tmpsql + ';CREATE TRIGGER InsertState_' + @tablename + ' ON ' + @tb_input + ' FOR INSERT AS '
  set @tmpsql = @tmpsql + ';INSERT INTO ' + @tb_output + ' SELECT count(*) CountOfRows FROM inserted'
  print isnull(@tmpsql,'NULLED!')
  EXEC(@tmpsql)
GO

;IF OBJECT_ID('input.firsttest','U') is not null DROP TABLE [input].[firsttest]
;IF OBJECT_ID('output.firsttest','U') is not null DROP TABLE [output].[firsttest]
;IF OBJECT_ID('input.secondtest','U') is not null DROP TABLE [input].[secondtest]
;IF OBJECT_ID('output.secondtest','U') is not null DROP TABLE [output].[secondtest]

--As long as the table is created before any inserts the trigger is also created and both tables are populated
;CREATE TABLE input.firsttest ( AnyColumn tinyint)
INSERT INTO input.firsttest VALUES (10),(20),(30)

SELECT 'input1' tb  , * FROM [input].[firsttest]
UNION ALL 
SELECT 'output1' tb , * FROM [output].[firsttest]

GO

--This is where things go wrong. Trying to create a trigger on a table that is in the process of being created by a SELECT INTO statement throws error 539
SELECT * INTO [input].[secondtest] FROM [input].[firsttest]

GO

--No data goes into either of the tables. However, 
SELECT 'input2a' tb, * FROM [input].[secondtest]
UNION ALL 
SELECT 'output2a' tb, * FROM [output].[secondtest]

GO

--This works and shows that the trigger is created on the table.
INSERT INTO [input].[secondtest] SELECT * FROM [input].[firsttest]

GO

--The table is populated as it should be by the SELECT INTO statement
SELECT 'input2b' tb, * FROM [input].[secondtest]
UNION ALL 
SELECT 'output2b' tb, * FROM [output].[secondtest]

sql-server tsql ddl database-trigger dml
1个回答
0
投票

正在使用时,选择* INTO [input]。[secondtest]下面的语句动态创建具有与[input]。[firsttest]相同的架构和属性的永久表。如果表已经存在,并且您执行INTO [input]。[secondtest],则sql server抛出上述错误,因此,如果您希望触发器中进行多次插入,请使用简单插入逻辑

插入到[输入]。[第二测试](AnyColumn)从[输入]中选择AnyColumn。[第一测试]

代替

SELECT * INTO [input]。[secondtest] FROM [input]。[firsttest]
© www.soinside.com 2019 - 2024. All rights reserved.