SQL Server “ПОСЛЕ ВСТАВЛЯЕТ” триггер, не видит просто вставленную строку

Сравнение 2 массивов:

var arr1 = [1,2,3];
var arr2 = [1,2,3];

function compare(arr1,arr2)
{
  if((arr1 == arr2) && (arr1.length == arr2.length))
    return true;
  else
    return false;
}

вызывающая функция

var isBool = compare(arr1.sort().join(),arr2.sort().join());
45
задан Joel Spolsky 1 January 2009 в 18:50
поделиться

10 ответов

Триггеры не могут изменить измененные данные (Inserted, или Deleted) иначе Вы могли получить бесконечную рекурсию, поскольку изменения вызвали триггер снова. Одна опция была бы для триггера для отката транзакции.

Редактирование: причина этого состоит в том, что стандарт для SQL - то, что вставленные и удаленные строки не могут быть изменены триггером. Базовая причина состоит в том, что модификации могли вызвать бесконечную рекурсию. В общем случае эта оценка могла вовлечь несколько триггеров во взаимно рекурсивный каскад. Наличие системы разумно решает, позволить ли такие обновления, в вычислительном отношении тяжело, по существу изменение на проблема остановки.

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

create table Foo (
       FooID int
      ,SomeField varchar (10)
)
go

create trigger FooInsert
    on Foo after insert as
    begin
        delete inserted
         where isnumeric (SomeField) = 1
    end
go


Msg 286, Level 16, State 1, Procedure FooInsert, Line 5
The logical tables INSERTED and DELETED cannot be updated.

Что-то вроде этого будет откатывать транзакцию.

create table Foo (
       FooID int
      ,SomeField varchar (10)
)
go

create trigger FooInsert
    on Foo for insert as
    if exists (
       select 1
         from inserted 
        where isnumeric (SomeField) = 1) begin
              rollback transaction
    end
go

insert Foo values (1, '1')

Msg 3609, Level 16, State 1, Line 1
The transaction ended in the trigger. The batch has been aborted.
48
ответ дан Ryan Kohn 8 November 2019 в 00:41
поделиться

ОБНОВЛЕНИЕ: УДАЛИТЕ из триггерных работ и над MSSql 7 и над 2008 MSSql.

я не реляционный гуру, ни зубрила стандартов SQL. Однако - вопреки принятому ответу - MSSQL имеет дело очень хорошо и с recursive и с оценкой вложенного триггера . Я не знаю о другом RDBMSs.

соответствующие опции 'рекурсивные триггеры' и 'вложенные триггеры' . Вложенные триггеры ограничены 32 уровнями и значением по умолчанию к 1. Рекурсивные триггеры прочь по умолчанию, и нет никакого разговора о пределе - но откровенно говоря, я никогда не включал их, таким образом, я не знаю то, что происходит с неизбежным переполнением стека. Я подозреваю, что MSSQL просто уничтожил бы Ваш spid (или существует рекурсивный предел).

, Конечно, который просто показывает, что принятый ответ имеет неправильное причина , не, что это неправильно. Однако до триггеров INSTEAD OF, я вспоминаю запись НА триггерах ВСТАВКИ, которые весело ОБНОВИЛИ бы справедливые вставленные строки. Это все хорошо работало, и как ожидалось.

А быстрый тест DELETEing справедливая вставленная строка также работает:

 CREATE TABLE Test ( Id int IDENTITY(1,1), Column1 varchar(10) )
 GO

 CREATE TRIGGER trTest ON Test 
 FOR INSERT 
 AS
    SET NOCOUNT ON
    DELETE FROM Test WHERE Column1 = 'ABCDEF'
 GO

 INSERT INTO Test (Column1) VALUES ('ABCDEF')
 --SCOPE_IDENTITY() should be the same, but doesn't exist in SQL 7
 PRINT @@IDENTITY --Will print 1. Run it again, and it'll print 2, 3, etc.
 GO

 SELECT * FROM Test --No rows
 GO

у Вас есть что-то еще продолжающееся здесь.

7
ответ дан Mark Brackett 8 November 2019 в 00:41
поделиться

Я думаю, что можно использовать ограничение CHECK - это точно, для чего это было изобретено.

ALTER TABLE someTable 
ADD CONSTRAINT someField_check CHECK (ISNUMERIC(someField) = 1) ;

Мой предыдущий ответ (также прямо может быть немного излишества):

я думаю, что правильный путь состоит в том, чтобы использовать триггер INSTEAD OF, чтобы препятствовать тому, чтобы неправильные данные были вставлены (вместо того, чтобы удалить его постакт)

12
ответ дан Dmitry Khalatov 8 November 2019 в 00:41
поделиться

Можно инвертировать логику. Вместо того, чтобы удалить недопустимую строку после того, как это было вставлено, запишите INSTEAD OF триггер для вставки [только 114], если Вы проверяете, что строка допустима.

CREATE TRIGGER mytrigger ON sometable
INSTEAD OF INSERT
AS BEGIN
  DECLARE @isnum TINYINT;

  SELECT @isnum = ISNUMERIC(somefield) FROM inserted;

  IF (@isnum = 1)
    INSERT INTO sometable SELECT * FROM inserted;
  ELSE
    RAISERROR('somefield must be numeric', 16, 1)
      WITH SETERROR;
END

, Если Ваше приложение не хочет обрабатывать ошибки (поскольку Joel говорит, имеет место в его приложении), то не делайте RAISERROR. Просто сделайте триггер тихо не , делают вставку, которая не допустима.

я выполнил это на SQL Server Express 2005, и это работает. Обратите внимание, что INSTEAD OF триггеры не делают рекурсия причины, если Вы вставляете в ту же таблицу, для которой определяется триггер.

39
ответ дан Bill Karwin 8 November 2019 в 00:41
поделиться

Вот моя измененная версия кода счета:

CREATE TRIGGER mytrigger ON sometable
INSTEAD OF INSERT
AS BEGIN
  INSERT INTO sometable SELECT * FROM inserted WHERE ISNUMERIC(somefield) = 1 FROM inserted;
  INSERT INTO sometableRejects SELECT * FROM inserted WHERE ISNUMERIC(somefield) = 0 FROM inserted;
END

Это позволяет вставке всегда успешно выполниться, и любые поддельные записи брошены в Ваш sometableRejects, где можно обработать их позже. Важно заставить Вашу таблицу отклонений использовать nvarchar поля для всего - не ints, tinyints, и т.д. - потому что, если они становятся отклоненными, это - потому что данные не то, чем Вы ожидали, что он будет.

Это также решает несколько - рекордная проблема вставки, которая заставит триггер счета перестать работать. При вставке десяти записей одновременно (как то, если бы Вы делаете select-insert-into), и только один из них является поддельным, триггер счета отметил бы всех их как плохо. Это обрабатывает любое количество хороших и плохих записей.

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

28
ответ дан Brent Ozar 8 November 2019 в 00:41
поделиться

Я нашел эту ссылку:

create trigger myTrigger
on SomeTable
for insert 
as 
if (select count(*) 
    from SomeTable, inserted 
    where IsNumeric(SomeField) = 1) <> 0
/* Cancel the insert and print a message.*/
  begin
    rollback transaction 
    print "You can't do that!"  
  end  
/* Otherwise, allow it. */
else
  print "Added successfully."

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

Редактирование: Конечно, там потенциал, что, если вставка произошла в в других отношениях допустимой транзакции, что wole транзакция могла откатываться так, необходимо будет принять тот сценарий во внимание и определить, составила ли вставка недопустимой строки данных абсолютно недопустимую транзакцию...

3
ответ дан BenAlabaster 8 November 2019 в 00:41
поделиться

Методы, обрисованные в общих чертах выше, описывают Ваши опции вполне прилично. Но что видят пользователи? Я не могу вообразить, как основной конфликт как это между Вами и кто бы ни ответственен за программное обеспечение, не может закончиться в беспорядке и антагонизме с пользователями.

я сделал бы все, что я мог для нахождения некоторого другого выхода из тупика - потому что другие люди могли легко видеть любое изменение, которое Вы вносите как возрастание проблемы.

РЕДАКТИРОВАНИЕ:

я выиграю, мои первые "восстанавливают после удаления" и признаются, что отправили вышеупомянутое, когда этот вопрос сначала появился. Я, конечно, пошел на попятный, когда я видел, что это было от JOEL SPOLSKY. Но похоже, что это приземлилось где-нибудь рядом. Не нужны голоса, но я помещу его на запись.

IME, триггеры так редко правильный ответ для чего-либо кроме мелкомодульных ограничений целостности вне области бизнес-правил.

0
ответ дан Joel Spolsky 8 November 2019 в 00:41
поделиться

Действительно ли возможно, что ВСТАВКА допустима, но что отдельное ОБНОВЛЕНИЕ сделано впоследствии, который недопустим, но не запустил бы триггер?

0
ответ дан SqlACID 8 November 2019 в 00:41
поделиться

От документация CREATE TRIGGER :

удалил , и вставил , логические (концептуальные) таблицы. Они структурно подобны таблице, на которой триггер определяется, то есть, таблица, на которой предпринято пользовательское действие, и содержат старые значения или новые значения строк, которые могут быть изменены пользовательским действием. Например, для получения всех значений в удаленной таблице используйте: SELECT * FROM deleted

Так, чтобы, по крайней мере, дал Вам способ видеть новые данные.

я не вижу ничего в документах, которое определяет, что Вы не будете видеть вставленные данные при запросах нормальной таблицы хотя...

4
ответ дан Jon Skeet 8 November 2019 в 00:41
поделиться

В MS-SQL есть параметр, предотвращающий срабатывание рекурсивного триггера. Это подтверждается хранимой процедурой sp_configure, в которой вы можете включать или отключать рекурсивные или вложенные триггеры.

В этом случае было бы возможно, если вы отключите рекурсивные триггеры, чтобы связать запись из вставленной таблицы через первичный ключ и внесите изменения в запись.

В конкретном случае в вопросе это не проблема, потому что результатом является удаление записи, которая не будет повторять этот конкретный триггер, но в целом это может быть правильным подходом. Таким образом мы реализовали оптимистичный параллелизм.

Код для вашего триггера, который можно использовать таким образом, будет выглядеть следующим образом:

ALTER TRIGGER myTrigger
    ON someTable
    AFTER INSERT
AS BEGIN
DELETE FROM someTable
    INNER JOIN inserted on inserted.primarykey = someTable.primarykey
    WHERE ISNUMERIC(inserted.someField) = 1
END
0
ответ дан 26 November 2019 в 20:52
поделиться
Другие вопросы по тегам:

Похожие вопросы: