我在Windows 7计算机上使用SQL Server 2008 R2。我有一个触发器应该在插入器上触发但不幸的是它没有。我没有SQL Profiler,因为我有快递版。有没有其他方法可以看出出了什么问题?表格团队的插入是使用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
答案 0 :(得分:1)
好。如果我们可以稍微更改tblTeams
和tblMatches
的表定义,以便他们使用ID
维护自己的IDENTITY
列,那么我们可以修复触发器以确保安全多行插入:
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
值结束 - 然后尝试将行插入{{ 1}}具有相同的tblTeam
值 - 而使用TeamId
列时,SQL Server会自动为我们处理此问题。
上面唯一的轻微吝啬是使用IDENTITY
插入新的小组 - MERGE
行会为现有行执行No-Op WHEN MATCHED
(因为我们知道{{ 1}}两边匹配),但这是一个很好的技巧,可以在一个语句中查找现有行和UPDATE
个新行。
我刚刚意识到你说你正在使用导入数据向导。我觉得它生成的SSIS包使用Fast Load创建目的地,而不指定teamName
。这也可能会打击你。
您可以使用向导生成程序包,然后编辑属性,或使用向导插入临时表,然后从该表中执行INSERT
/ FIRE_TRIGGERS
到我们的{ {1}}表,然后让触发器触发。
答案 1 :(得分:1)
批量插入向导/数据导入向导通常会绕过目标表上的触发器。