SELECT INTO ломает триггер DDL / DML, но CREATE TABLE, за которым следует INSERT, работает? - PullRequest
0 голосов
/ 09 октября 2019

В SQL Server 2017 (SSMS v18.3) я использую триггер DDL для создания output таблиц для заданных input таблиц.

Затем я создаю триггер DML для таблиц input, чтобы заполнить таблицы 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.

и в то время как input, output, и триггер создается, данные не вставляются.

Я пробовал CATCH RETRY для каждой части запроса, но не могу определить его.

Как я могу преодолеть эту ошибку, все еще используя SELECT INTO для создания таблиц в схеме [input]?

И для какой схемы создаются таблицы SELECT INTO в?

Вот БД скрипта , демонстрирующая эту проблему

Вот еще БД скрипта , которая демонстрирует ту же проблему, содержащуюся в одной схеме с разными именами таблиц.

А вот и мой код:

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]

1 Ответ

0 голосов
/ 09 октября 2019

Поскольку вы используете, выберите * INTO [input]. [Secondtest], инструкция ниже создает постоянную таблицу на лету с той же схемой и свойствами, что и у [input]. [Firsttest]. Если таблица уже существует, и вы выполняете INTO [input]. [Secondtest], то сервер sql выдает указанную ошибку, поэтому, если вы хотите, чтобы в триггере выполнялось несколько вставок, используйте логику простой вставки

INSERT INTO [input].[secondtest](AnyColumn) SELECT AnyColumn FROM [input].[firsttest]

Вместо

SELECT * INTO [input].[secondtest] FROM [input].[firsttest]
...