我在 Windows 7 计算机上使用 SQL Server 2008 R2。我有一个应该在插入件上触发的触发器,但不幸的是它没有。我没有 SQL Profiler,因为我有 Express 版本。还有其他方法可以查看出了什么问题吗?向表 team 的插入是通过 SQL Server 的导入向导完成的,即从 .CSV 导入到表中。
CREATE TRIGGER teams.process ON teams
AFTER INSERT
AS
BEGIN
DECLARE @homeTeamId INT
DECLARE @awayTeamId INT
DECLARE @maxTeamId INT
DECLARE @matchId INT
SELECT @maxTeamId = 0
SELECT @maxTeamId = ISNULL(MAX(teamId), 0) from tblTeams
--- Check if home team has already been inserted into the table.
SELECT @homeTeamId = -1
SELECT
@homeTeamId = teamId
FROM
tblTeams t
JOIN inserted i
ON t.teamName = i.hometeam
IF (@homeTeamId = -1)
BEGIN
SELECT @homeTeamId = @maxTeamId + 1
SELECT @maxTeamId = @maxTeamId + 1
INSERT INTO tblTeams SELECT @homeTeamId, i.hometeam FROM inserted i
END
--- Check if away team has already been inserted into the table.
SELECT @awayTeamId = -1
SELECT
@awayTeamId = teamId
FROM
tblTeams t
JOIN inserted i
ON t.teamName = i.awayteam
IF (@awayTeamId = -1)
BEGIN
SELECT @awayTeamId = @maxTeamId + 1
SELECT @maxTeamId = @maxTeamId + 1
INSERT INTO tblTeams SELECT @awayTeamId, i.awayteam FROM inserted i
END
-- insert a record into the matches table with the home team ID and away team ID.
SELECT @matchId = 0
SELECT @matchId = ISNULL(MAX(MatchId), 0) FROM tblMatches
INSERT INTO tblMatches
SELECT @matchId + 1, @homeTeamId, @awayTeamId, i.score
FROM inserted i
END
最佳答案
好的。如果我们可以稍微更改 tblTeams
和 tblMatches
的表定义,以便它们使用 IDENTITY
维护自己的 ID
列>,然后我们可以修复触发器以确保多行插入安全:
create table teams (
hometeam varchar(10) not null,
awayteam varchar(10) not null,
score int not null
)
create table tblteams (
teamId int IDENTITY(1,1) not null,
teamName varchar(10) not null
)
create table tblmatches (
matchId int IDENTITY(1,1) not null,
HomeTeamID int not null,
AwayTeamID int not null,
Score int not null
)
go
CREATE TRIGGER process ON teams
AFTER INSERT
AS
SET NOCOUNT ON
declare @TeamIDs table (TeamID int not null,TeamName varchar(10) not null)
;with AllTeams as (
select hometeam as teamName from inserted
union
select awayteam from inserted
)
merge into tblTeams tt using AllTeams at on tt.teamName = at.teamName
when matched then update set teamName = at.teamName
when not matched then insert (teamName) values (at.teamName)
output inserted.TeamID,inserted.teamName into @TeamIDs;
insert into tblmatches (HomeTeamID,AwayTeamID,Score)
select ht.TeamID,at.TeamID,i.Score
from inserted i
inner join @TeamIDs ht on i.hometeam = ht.TeamName
inner join @TeamIDs at on i.awayteam = at.TeamName
GO
然后我们测试一下:
insert into teams (hometeam,awayteam,score) values
('abc','def',10),
('def','ghi',5),
('jkl','mno',7)
go
insert into teams (hometeam,awayteam,score) values
('abc','ghi',19),
('pqr','stu',11)
go
select * from tblteams
select * from tblmatches
现有触发器的问题是它无法处理包含多行的inserted
- 每个语句触发一次触发器,而不是每行触发一次。所以例如这些行是错误的:
SELECT @homeTeamId = @maxTeamId + 1
SELECT @maxTeamId = @maxTeamId + 1
INSERT INTO tblTeams SELECT @homeTeamId, i.hometeam FROM inserted i
因为可能有多个 homeTeam
值需要处理。
它也不能很好地处理并发性 - 并行发生的对触发器的两次调用可能最终会得到相同的 @maxTeamId
值 - 然后尝试插入将具有相同 TeamId
值的行放入 tblTeam
中 - 而使用 IDENTITY
列,SQL Server 会自动为我们处理此问题。
上面唯一的小改动是使用 MERGE
插入新团队 - WHEN MATCHED
行将执行 No-Op UPDATE
对于现有行(因为我们知道 teamName
两边都匹配),但在单个语句中查找现有行和 INSERT
新行是一个很好的技巧.
我刚刚意识到您说过您正在使用导入数据向导。我有一种感觉,它生成的 SSIS 包使用 Fast Load 创建目标,并且未指定 FIRE_TRIGGERS
。这也可能会毁掉你。
您可以使用向导生成包,然后编辑属性,或者使用向导插入到临时表中,然后执行简单的 INSERT
/SELECT
从该表进入我们的 teams
表,然后让触发器以这种方式触发。
关于sql-server - 我如何知道触发器是否被触发?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12582615/