문제

이 트리거를 고려하십시오.

ALTER TRIGGER myTrigger 
   ON someTable 
   AFTER INSERT
AS BEGIN
  DELETE FROM someTable
         WHERE ISNUMERIC(someField) = 1
END

나는 테이블이 있고, 약간의 테이블이 있고, 사람들이 나쁜 기록을 삽입하는 것을 막으려 고 노력하고 있습니다. 이 질문의 목적을 위해, 나쁜 레코드에는 숫자 인 "somefield"필드가 있습니다.

물론이 작업을 수행하는 올바른 방법은 트리거가 아니라 소스 코드를 제어하지 않습니다 ... SQL 데이터베이스 만 있습니다. 그래서 나는 나쁜 행의 삽입을 실제로 막을 수는 없지만 바로 삭제할 수 있습니다. 이는 내 필요에 충분합니다.

트리거는 한 가지 문제와 함께 작동합니다 ... 발사 될 때, 그것은 방금 삽입 된 나쁜 레코드를 삭제하지 않는 것 같습니다 ... 그것은 오래된 나쁜 레코드를 삭제하지만 방금 삽입 된 나쁜 레코드를 삭제하지는 않습니다. 따라서 다른 사람이 와서 다른 삽입을 할 때까지 삭제되지 않는 한 가지 나쁜 레코드가 떠 다니는 경우가 종종 있습니다.

이것이 트리거를 이해하는 데 문제가 있습니까? 트리거가 실행중인 동안 새로 삽입 된 행이 아직 커밋되지 않습니까?

도움이 되었습니까?

해결책

트리거는 변경된 데이터를 수정할 수 없습니다 (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.

다른 팁

논리를 뒤집을 수 있습니다. 삽입 된 후 유효하지 않은 행을 삭제하는 대신 INSTEAD OF 삽입을 트리거합니다 행이 유효한지 확인하면

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 트리거 하지 마라 트리거가 정의 된 동일한 테이블에 삽입하면 재귀를 유발하십시오.

다음은 Bill 코드의 수정 된 버전입니다.

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

이렇게하면 삽입물이 항상 성공할 수 있으며, 모든 가짜 레코드는 나중에 처리 할 수있는 곳에서 몇 번의 블러 젝트에 던져집니다. int, tinyints 등이 아닌 모든 것에 nvarchar 필드를 사용하여 거부 테이블을 사용하는 것이 중요합니다. 왜냐하면 거부 당하면 데이터가 예상했던 것이 아니기 때문입니다.

이것은 또한 다중 레코드 인서트 문제를 해결하여 Bill의 트리거가 실패하게됩니다. 10 개의 레코드를 동시에 삽입하는 경우 (선택된 인서트 인트로를하는 경우와 같이) 그중 하나만 가짜 인 경우 Bill의 방아쇠는 모든 것을 나쁜 것으로 표시했을 것입니다. 이것은 좋은 기록과 나쁜 기록을 처리합니다.

나는이 트릭을 데이터웨어 하우징 프로젝트에서 사용하여 응용 프로그램 삽입이 비즈니스 논리가 좋은지 전혀 몰랐으며 대신 트리거로 비즈니스 로직을 수행했습니다. 성능은 정말로 불쾌하지만 삽입이 실패 할 수 없다면 작동합니다.

나는 당신이 점검 제약을 사용할 수 있다고 생각합니다 - 그것은 정확히 그것이 발명 된 것입니다.

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

나의 이전 답변 (또한 바로 약간 과잉 일 수 있음) :

올바른 방법은 잘못된 데이터가 삽입되는 것을 방지하기 위해 트리거 대신 사용하는 것이라고 생각합니다 (포스트 포스트 칩텀을 삭제하는 대신).

업데이트 : MSSQL 7 및 MSSQL 2008에서 트리거에서 삭제됩니다.

나는 관계형 전문가도 아니고 SQL 표준이 아닙니다. 그러나 허용 된 답변과는 달리 MSSQL은 두 R을 모두 처리합니다.Ecursive 및 중첩 방아쇠 평가. 나는 다른 RDBMS에 대해 모른다.

관련 옵션은 다음과 같습니다 '재귀 트리거'및 '중첩 트리거'. 중첩 트리거는 32 레벨로 제한되고 기본값은 1로 제한되어 있습니다. 재귀 트리거는 기본적으로 꺼져 있으며 한계에 대한 이야기는 없습니다. 그러나 솔직히 말해서, 나는 그들을 켜지 않았으므로 피할 수없는 일이 어떻게되는지 모르겠습니다. 스택 오버플로. MSSQL이 SPID를 죽일 것이라고 생각합니다 (또는 재귀 제한이 있음).

물론, 그것은 받아 들여진 답이 잘못되었음을 보여줍니다. 이유, 그것이 틀렸다는 것이 아닙니다. 그러나 트리거 대신에 방금 삽입 된 행을 즐겁게 업데이트 할 수있는 트리거 삽입에 대한 글쓰기를 기억합니다. 이것은 모두 잘 작동했고 예상대로 작동했습니다.

방금 삽입 된 행을 삭제하는 빠른 테스트도 작동합니다.

 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

당신은 여기에 다른 일이 있습니다.

로부터 트리거를 만듭니다 선적 서류 비치:

삭제 그리고 삽입 논리적 (개념적) 테이블입니다. 이들은 트리거가 정의 된 표, 즉 사용자 조치가 시도되는 테이블과 구조적으로 유사하며 사용자 조치에 의해 변경 될 수있는 행의 기존 값 또는 새 값을 보유합니다. 예를 들어, 삭제 된 테이블의 모든 값을 검색하려면 다음을 사용하십시오. SELECT * FROM deleted

따라서 최소한 새로운 데이터를 볼 수있는 방법을 제공합니다.

문서에서 일반 테이블을 쿼리 할 때 삽입 된 데이터가 표시되지 않음을 지정하는 문서에서 아무것도 볼 수 없습니다 ...

이 참조를 찾았습니다.

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."

나는 그것을 테스트하지 않았지만 논리적으로 당신이 따르는 것을 dp해야 할 것 같습니다 ... 삽입 된 데이터를 삭제하는 대신 삽입을 완전히 방지하여 삽입을 취소 할 필요가 없습니다. 더 나은 성능이 있어야하므로 궁극적으로 더 높은 하중을 더 쉽게 처리해야합니다.

편집 : 물론 거기에 ~이다 삽입이 유효한 트랜잭션 내부에서 발생할 수있는 잠재력은 WOLE 트랜잭션을 롤백하여 해당 시나리오를 고려하여 유효하지 않은 데이터 행의 삽입이 완전히 유효하지 않은 트랜잭션을 구성 할 것인지 결정해야합니다 ...

인서트가 유효 할 가능성이 있지만 나중에 별도의 업데이트가 수행되어 유효하지만 트리거를 발사하지 않습니까?

위에서 설명한 기술은 옵션을 잘 설명합니다. 그러나 사용자는 무엇을보고 있습니까? 나는 당신과 소프트웨어에 대한 책임이있는 사람 사이의 기본적인 갈등이 사용자와 혼란스럽고 길항 할 수없는 방법을 상상할 수 없습니다.

다른 사람들이 문제를 확대하는 것으로 쉽게 변화를 볼 수 있었기 때문에 다른 방법으로 다른 방법을 찾기 위해 최선을 다할 것입니다.

편집하다:

나는 나의 첫 번째 "undelete"를 득점 하고이 질문이 처음 나타 났을 때 위의 글을 인정한다는 것을 인정할 것이다. 물론 나는 그것이 Joel Spolsky에서 온 것을 보았을 때 병아리를 쳤다. 그러나 그것은 가까운 곳에 착륙 한 것처럼 보입니다. 투표가 필요하지 않지만 기록에 넣을 것입니다.

IME, 트리거는 비즈니스 규칙 영역 이외의 세밀한 무결성 제약 이외의 다른 것에 대한 정답이 거의 없습니다.

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

"트리거"는 "트리거"가하고있는 일을하고 있습니다. SQL Server 에이전트를 실행할 수 있습니다

DELETE FROM someTable
WHERE ISNUMERIC(someField) = 1

1 초마다. 당신이 그것을하는 동안, 프로그래밍 사람들이 당신의 테이블에 오류를 삽입하는 것을 막기 위해 멋진 작은 SP를 작성하는 것은 어떻습니까? SP의 좋은 점은 매개 변수가 안전하다는 것입니다.

삽입 문 및 트리거 중에 일련의 이벤트에 대한 자세한 내용을 찾는이 질문을 발견했습니다. SQL 2016 (Express)의 행동 방식을 확인하기 위해 간단한 테스트를 코딩하게되었으며 다른 사람들이 비슷한 정보를 검색하는 데 도움이 될 수 있으므로 공유하는 것이 적절하다고 생각했습니다.

내 테스트를 기반으로 "삽입 된"테이블에서 데이터를 선택하고 삽입 된 데이터 자체를 업데이트하는 데 사용할 수 있습니다. 그리고 나에게 관심이있는 경우, 삽입 된 데이터는 트리거가 완료 될 때까지 다른 쿼리에 보이지 않습니다. 재귀 트리거 등을 테스트하지 않았습니다.

예를 들어 - 정수 필드 "필드"및 기본 키 필드 "PK"와 삽입 트리거의 다음 코드가있는 테이블 "테이블"이 있다고 가정합니다.

select @value=field,@pk=pk from inserted
update table set field=@value+1 where pk=@pk
waitfor delay '00:00:15'

"필드"에 대한 값 1의 행을 삽입 한 다음 행은 값 2로 끝납니다. 또한 SSMS에서 다른 창을 열고 시도하면 : PK = @pk 테이블에서 *를 선택하십시오.

@pk가 원래 삽입 한 기본 키 인 경우, 15 초가 만료 될 때까지 쿼리가 비어있는 다음 업데이트 된 값을 표시합니다 (필드 = 2).

트리거가 실행되는 동안 다른 쿼리에 어떤 데이터가 보이는지에 관심이있었습니다 (분명히 새 데이터는 없음). 추가 삭제로 테스트했습니다.

select @value=field,@pk=pk from inserted
update table set field=@value+1 where pk=@pk
delete from table where pk=@pk
waitfor delay '00:00:15'

다시, 삽입물은 15sec가 실행되었다. 다른 세션에서 실행되는 쿼리에는 삽입 + 트리거의 실행 중 또는 실행 중 또는 후에 데이터가 삽입되지 않은 경우에도 ID가 증가 할 것으로 예상하지만).

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top