Как справиться с IDENTITY_INSERT для триггера INSTEAD OF для таблицы со столбцом IDENTITY? - PullRequest
0 голосов
/ 13 мая 2018

У меня есть таблица со столбцом IDENTITY, и у меня есть триггер INSTEAD OF для этой таблицы. Если включить IDENTITY_INSERT, я бы хотел вставить значение, указанное вручную для столбца IDENTITY.

Есть ли способ правильно справиться с этим сценарием, например, определить значение IDENTITY_INSERT? Судя по чтению, похоже, что обнаружить текущее значение IDENTITY_INSERT для конкретной таблицы в прошлом было невозможно. Я не знаю, возможно ли это с более новыми версиями SQL Server.

Создание таблицы SQL:

CREATE TABLE [TestTable]
(
    [Id]            INTEGER IDENTITY    NOT NULL    PRIMARY KEY,
    [ExampleField]  BIT                 NOT NULL    DEFAULT(1)
)

Это то, что я сейчас пробовал, но это кажется довольно расточительным, поскольку IDENTITY_INSERT, вероятно, будет отключен в большинстве случаев, то есть он всегда будет терпеть неудачу при первой попытке вставки, которая кажется расточительной, С точки зрения производительности.

CREATE TRIGGER [dbo].[trTestTable_ioi] ON [dbo].[TestTable] INSTEAD OF INSERT
AS
BEGIN
    BEGIN TRY
        INSERT INTO [TestTable]([Id],[ExampleField])
        SELECT [Id], [ExampleField]
        FROM [inserted]
    END TRY
    BEGIN CATCH
        INSERT INTO [TestTable]([ExampleField])
        SELECT [ExampleField]
        FROM [inserted]
    END CATCH
END

Ответы [ 2 ]

0 голосов
/ 13 мая 2018

Если ваши IDENTITY начальное значение и приращение таковы, что сгенерированное значение всегда будет отличным от нуля (положительное начальное значение и приращение или отрицательное начальное значение и приращение), вы можете проверить ненулевые значения в виртуальной таблице inserted.и используйте это значение, когда оно присутствует.Это основано на моем наблюдении, что значение идентичности равно нулю при INSTEAD OF триггере и IDENTITY_INSERT OFF.Однако я не смог найти это поведение специально задокументированным, поэтому вам следует проверить его в своей среде и использовать на свой страх и риск.

Пример:

CREATE TRIGGER [dbo].[trTestTable_ioi] ON [dbo].[TestTable] INSTEAD OF INSERT
AS
SET NOCOUNT ON;
IF EXISTS(SELECT * FROM inserted WHERE ID <> 0)
BEGIN
    --IDENTITY_INSERT is ON
    INSERT INTO [TestTable]([Id],[ExampleField])
    SELECT [Id], [ExampleField]
    FROM [inserted];
END
ELSE
BEGIN
    --IDENTITY_INSERT is OFF
    INSERT INTO [TestTable]([ExampleField])
    SELECT [ExampleField]
    FROM [inserted];
END;
GO

SET IDENTITY_INSERT dbo.TestTable OFF;
GO
--identity value auto-assigned
INSERT INTO TestTable VALUES(1);
GO

SET IDENTITY_INSERT dbo.TestTable ON;
GO
--explict identity value specified
INSERT INTO TestTable(ID, ExampleField) VALUES(11, 1);
GO

SET IDENTITY_INSERT dbo.TestTable OFF;
GO
--fails as expected because identity value cannot be specified with IDENTITY_INSERT OFF
INSERT INTO TestTable(ID, ExampleField) VALUES(11, 1);
GO

SET IDENTITY_INSERT dbo.TestTable ON;
GO
--fails as expected because identity value must be specified with IDENTITY_INSERT ON
INSERT INTO TestTable VALUES(1);
GO
0 голосов
/ 13 мая 2018

Документация для set identity_insert состояний:

В любое время только одна таблица в сеансе может иметь IDENTITY_INSERT свойство установлено на ON. Если для таблицы уже установлено это свойство, и инструкция SET IDENTITY_INSERT ON выдается для другой таблицы, SQL Server возвращает сообщение об ошибке, в котором говорится, что SET IDENTITY_INSERT уже включен и сообщает таблицу, для которой он включен.

A ужасная идея состоит в том, чтобы создать временную таблицу и попробовать установить для нее identity_insert on. Если есть ошибка, перехватите сообщение и извлеките имя таблицы.

NB: Каждый раз, когда вы перехватываете и анализируете сообщение об ошибке, вы играете с каким-то хрупким кодом. Ты должен бежать, а не уходить, прочь сейчас .

create table #Placeholder ( Id Int Identity );
begin try
  set identity_insert #Placeholder on;
end try
begin catch
  -- Sample error: IDENTITY_INSERT is already ON for table 'Scratch.dbo.Foo'. Cannot perform SET operation for table '#Placeholder'.
  declare @Message as NVarChar(2048) = Error_Message();
  print @Message;
  declare @Prefix as NVarChar(2048) = 'IDENTITY_INSERT is already ON for table ''';
  declare @Suffix as NVarChar(2048) = '''. Cannot perform SET operation for table ''#Placeholder''.';
  declare @TableName as NVarChar(2048) = NULL;
  if ( Left( @Message, Len( @Prefix ) ) = @Prefix and Right( @Message, Len( @Suffix ) ) = @Suffix )
    set @TableName = Substring( @Message, Len( @Prefix ) + 1, Len( @Message ) - Len( @Prefix ) - Len( @Suffix ) );
  else
    print 'Unexpected error!';
  print @TableName;    
end catch
drop table #Placeholder;

Кроме того, производительность влияет на создание / удаление временной таблицы в триггере.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...