В 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]