결정하는 오래된 기본 키에 SQL 트리거
-
03-07-2019 - |
문제
나는 이 일을 행하기 전에 어딘가에 나가 확실하다.
나는 SQL Server2000 테이블을 필요로 로그를 변경하는 필드에 삽입 업데이트 및으로 두 번째 로깅이다.간단한 버전을의 구조는 내용은 다음과 같습니다.
MainTable
ID varchar(10) PRIMARY KEY
DESCRIPTION varchar(50)
LogTable
OLDID varchar(10)
NEWID varchar(10)
다른 분야가 이와 같은 작동:
Select i.DESCRIPTION As New, d.DESCRIPTION As Old
From Inserted i
LEFT JOIN Deleted d On i.ID=d.ID
...하지만 분명히 가입하세이 실패하는 경우 ID 를 변경되었습니다.
나는 수정할 수 없습니다 테이블에서 방법으로,이만 힘이 나는 이 데이터베이스를 만드는 것입니다.
또는 있는 사람을 가르칠 수 있는 나에게 여행하는 시간 및 다운로드가 가능합니다.으로 자신에게 그때 나는 어떻게 이런 짓을 했죠?환호:)
편집:
나는 생각을 명확히 할 필요가 몇 일들이 여기에 있습니다.이 실제로 나는 데이터베이스,그것은 기존 시스템 내가 거의 없음의 제어,다른 것보다 이것을 쓰고 있다.
나의 질문은 어떻게 검색 할 수 있습니다 오래된 기본 키가 있는 경우 말했 기본 키가 변경되었습니다.내가 필요로 하지 않는 말을하지 않았어 변경 또는 기본 키에 대해 쫓는 외국습니다.는 내 문제가:)
해결책
트리거에 제시된 삽입 및 삭제 된 테이블이 동일한 순서로 보장된다고 가정 할 수 있습니까?
다른 팁
DECLARE @OldKey int, @NewKey int;
SELECT @Oldkey = [ID] FROM DELETED;
SELECT @NewKey = [ID] FROM INSERTED;
이것은 단일 행이있는 경우에만 작동합니다. 그렇지 않으면 오래된 행과 새 행을 연결할 "앵커"가 없습니다. 따라서 삽입 된> 1에 대한 트리거를 확인하십시오.
나는 그것이 가능하지 않다고 생각합니다. 테이블에 4 개의 행이 있다고 상상해보십시오.
1 Val1
2 Val2
3 Val3
4 Val4
이제 다음 업데이트를 발행합니다.
UPDATE MainTable SET
ID = CASE ID WHEN 1 THEN 2 WHEN 2 THEN 1 ELSE ID END
Description = CASE ID WHEN 3 THEN 'Val4' WHEN 4 THEN 'Val3' ELSE Description END
이제, 당신은 어떻게 행 1 & 2 행에서 일어난 일과 3 행 4에서 일어난 일을 어떻게 구별 할 것인가? 그리고 더 중요한 것은 그들 사이의 다른 점을 설명 할 수 있습니까? 어떤 열이 업데이트되었는지 알려주는 모든 것들이 도움이되지 않습니다.
이 경우 테이블에 추가 키가있을 수 있다면 (예 : 설명은 고유합니다) 업데이트 규칙이 허용되면 트리거를 작성하여 두 키에 대한 동시 업데이트를 방지 할 수 있습니다. 두 테이블을 상관시키기 위해 업데이트되었습니다.
다중 줄 인서트/업데이트를 처리해야하고 변경하지 않아야 할 대체 키가 없으면이 작업을 수행 할 수있는 유일한 방법은 트리거 대신 사용하는 것입니다. 예를 들어 트리거에서 원래 삽입/업데이트 명령을 행당 하나의 명령으로 나누고 삽입/업데이트 전에 각각의 ID를 잡을 수 있습니다.
SQL Server의 트리거 내에서 삭제 및 삽입 된 두 개의 테이블에 액세스 할 수 있습니다. 이 두 가지 모두 이미 언급되었습니다. 트리거가 발사되는 조치에 따라 다음과 같은 기능은 다음과 같습니다.
작동을 삽입하십시오
- 삭제 - 사용되지 않습니다
- 삽입 - 테이블에 추가되는 새 행이 포함되어 있습니다.
작업 삭제
- 삭제 - 테이블에서 제거되는 행이 포함되어 있습니다.
- 삽입 - 사용되지 않습니다
업데이트 작업
- 삭제 - 업데이트 작업 전에 존재하는 행을 포함합니다.
- 삽입 - 업데이트 작업 후 존재하는 행이 포함되어 있습니다.
테이블과 같은 모든 방식으로 이러한 기능. 따라서 다음과 같은 것과 같은 행 기반 작업을 전적으로 사용하는 것이 가능합니다 (작업 테이블에만 운영이 존재합니다. DateChanged와 같이).
INSERT INTO MyAuditTable
(ID, FirstColumn, SecondColumn, ThirdColumn, Operation, DateChanged)
VALUES
SELECT ID, FirstColumn, SecondColumn, ThirdColumn, 'Update-Before', GETDATE()
FROM deleted
UNION ALL
SELECT ID, FirstColumn, SecondColumn, ThirdColumn, 'Update-After', GETDATE()
FROM inserted
---- 새 ---- 응용 프로그램이 변경 될 수없는 테이블에 ID 열을 추가 한 다음 해당 새 열을 사용하여 트리거 내의 삭제 된 테이블에 삽입 된 삽입 된 테이블에 가입 할 수 있습니다.
ALTER TABLE YourTableName ADD
PrivateID int NOT NULL IDENTITY (1, 1)
GO
---- Old ---- 키 값을 업데이트/변경하지 마십시오. 어떻게 이렇게하고 모든 외국 열쇠를 수정할 수 있습니까?
행을 처리 할 수없는 트리거를 사용하지 않는 것이 좋습니다.
키를 변경 해야하는 경우 적절한 새 키와 값으로 새 행을 삽입하면 Scope_Identity ()를 사용하는 경우 사용하는 경우 사용하십시오. 이전 행을 삭제하십시오. 오래된 행을 로그인 새 행의 키로 변경되었는데, 이제 가야합니다. 로그에 변경된 키에 외국 키가 없기를 바랍니다 ...
테이블 maintable (예 : CorrelationId와 같은 이름)에 새 ID 열을 만들 수 있으며이 열을 사용하여 삽입 및 삭제 된 테이블을 상관시킬 수 있습니다. 이 새 열은 기존 코드의 경우 투명해야합니다.
INSERT INTO LOG(OLDID, NEWID)
SELECT deleted.id AS OLDID, inserted.id AS NEWID
FROM inserted
INNER JOIN deleted
ON inserted.correlationid = deleted.correlationid
주의를 기울이면 로그 테이블에 중복 레코드를 삽입 할 수 있습니다.
물론 아무도해야 합 변화하는 기본 키 테이블에 하나는 정확하게 트리거가 있어야에 대한(부분),은 사람들을 유지하는 일을 하기에서 그들이 하면 안되는 행동을 했어요.그것은 간단한 작업에서 Oracle MySQL 쓰 트리거을 가로채는 변경을 기본 키고 그들을 중지하지만,모든 SQL Server.
당신의 사랑을 할 수 있는 것이 단순히 다음과 같은 것이 가능합니다.
if exists
(
select *
from inserted changed
join deleted old
where changed.rowID = old.rowID
and changed.id != old.id
)
... [roll it all back]
는 이유는 사람들이 밖으로 나가서 인터넷 검색에 대한 SQL 서버의 동 ROWID.론,SQL 서버는 그것이 없습니다;그래서 당신은 다른 접근 방식이다.
빠른하지만,슬프게도지 않는 방탄,버전을 쓰는 대신 업데이트의 트리거를 보이는지 여부를 확인 어떤 행 삽입 기본 키가 있에서 발견되지 않 업데이트된 테이블 또는 그 반대입니다.이를 잡을 것이 전부는 아니지만 대부분의 오류:
if exists
(
select *
from inserted lost
left join updated match
on match.id = lost.id
where match.id is null
union
select *
from deleted new
left join inserted match
on match.id = new.id
where match.id is null
)
-- roll it all back
그러나 이것은 여전히지 않는 catch 업데이트처럼...
update myTable
set id = case
when id = 1 then 2
when id = 2 then 1
else id
end
나는 지금도 가정을 만드는 삽입과 삭제한 테이블에서 주문하는 그런 방법으로 cursoring 를 삽입과 삭제는 테이블 동시에 당신을 줄 것이 제대로 일치하는 행이 있습니다.이 나타납니다.효과에서 당신이 설정 트리거로 동의한 각 행 트리거를 사용할 수 있습에서 오라클 및 필수에서 MySQL...그러나 나는 상상 성능이 나쁜 것입에 대규모 업데이트 않기 때문에 이 기본 동작하는 SQL Server.또한 그에 따라 달라집 가정할 수 없는 실제로 찾기 문서화 어디서나 그래서 꺼려에 따라 달라집니다.하지만 코드를 구성하는 방법으로 나타나 제대로 작동하려면에 내 SQL Server2008R2 설치합니다.스크립트 끝에서 이 게시물의 하이라이트 모두의 동작이 빠르지 않음-방탄 솔루션과 동작의 두 번째,의사 Oracle 솔루션입니다.
면 누구나 다 할 수 있다는 점을 곳으로는 나의 가정은 문서화하고 보장하여 Microsoft 내가 매우 감사 사람이...
begin try
drop table kpTest;
end try
begin catch
end catch
go
create table kpTest( id int primary key, name nvarchar(10) )
go
begin try
drop trigger kpTest_ioU;
end try
begin catch
end catch
go
create trigger kpTest_ioU on kpTest
instead of update
as
begin
if exists
(
select *
from inserted lost
left join deleted match
on match.id = lost.id
where match.id is null
union
select *
from deleted new
left join inserted match
on match.id = new.id
where match.id is null
)
raisError( 'Changed primary key', 16, 1 )
else
update kpTest
set name = i.name
from kpTest
join inserted i
on i.id = kpTest.id
;
end
go
insert into kpTest( id, name ) values( 0, 'zero' );
insert into kpTest( id, name ) values( 1, 'one' );
insert into kpTest( id, name ) values( 2, 'two' );
insert into kpTest( id, name ) values( 3, 'three' );
select * from kpTest;
/*
0 zero
1 one
2 two
3 three
*/
-- This throws an error, appropriately
update kpTest set id = 5, name = 'FIVE' where id = 1
go
select * from kpTest;
/*
0 zero
1 one
2 two
3 three
*/
-- This allows the change, inappropriately
update kpTest
set id = case
when id = 1 then 2
when id = 2 then 1
else id
end
, name = UPPER( name )
go
select * from kpTest
/*
0 ZERO
1 TWO -- WRONG WRONG WRONG
2 ONE -- WRONG WRONG WRONG
3 THREE
*/
-- Put it back
update kpTest
set id = case
when id = 1 then 2
when id = 2 then 1
else id
end
, name = LOWER( name )
go
select * from kpTest;
/*
0 zero
1 one
2 two
3 three
*/
drop trigger kpTest_ioU
go
create trigger kpTest_ioU on kpTest
instead of update
as
begin
declare newIDs cursor for select id, name from inserted;
declare oldIDs cursor for select id from deleted;
declare @thisOldID int;
declare @thisNewID int;
declare @thisNewName nvarchar(10);
declare @errorFound int;
set @errorFound = 0;
open newIDs;
open oldIDs;
fetch newIDs into @thisNewID, @thisNewName;
fetch oldIDs into @thisOldID;
while @@FETCH_STATUS = 0 and @errorFound = 0
begin
if @thisNewID != @thisOldID
begin
set @errorFound = 1;
close newIDs;
deallocate newIDs;
close oldIDs;
deallocate oldIDs;
raisError( 'Primary key changed', 16, 1 );
end
else
begin
update kpTest
set name = @thisNewName
where id = @thisNewID
;
fetch newIDs into @thisNewID, @thisNewName;
fetch oldIDs into @thisOldID;
end
end;
if @errorFound = 0
begin
close newIDs;
deallocate newIDs;
close oldIDs;
deallocate oldIDs;
end
end
go
-- Succeeds, appropriately
update kpTest
set name = UPPER( name )
go
select * from kpTest;
/*
0 ZERO
1 ONE
2 TWO
3 THREE
*/
-- Succeeds, appropriately
update kpTest
set name = LOWER( name )
go
select * from kpTest;
/*
0 zero
1 one
2 two
3 three
*/
-- Fails, appropriately
update kpTest
set id = case
when id = 1 then 2
when id = 2 then 1
else id
end
go
select * from kpTest;
/*
0 zero
1 one
2 two
3 three
*/
-- Fails, appropriately
update kpTest
set id = id + 1
go
select * from kpTest;
/*
0 zero
1 one
2 two
3 three
*/
-- Succeeds, appropriately
update kpTest
set id = id, name = UPPER( name )
go
select * from kpTest;
/*
0 ZERO
1 ONE
2 TWO
3 THREE
*/
drop table kpTest
go