2

我在 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 
4

2 回答 2

1

好的。tblTeams如果我们可以稍微更改和的表定义tblMatches,以便它们使用 来维护自己的IDIDENTITY,那么我们可以将触发器修复为对多行插入安全:

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的值——然后尝试插入tblTeam具有相同TeamId值的行——而使用IDENTITY列时,SQL Server 会自动为我们处理这个问题.

上面唯一的小技巧是MERGE用于插入新团队 - 该WHEN MATCHED行将UPDATE对现有行执行 No-Op(因为我们知道teamName双方的匹配项),但查找现有行INSERT和单个语句中的新行。


我刚刚意识到您说过您正在使用导入数据向导。我有一种感觉,它生成的 SSIS 包使用Fast Load创建了一个目的地,并且没有指定FIRE_TRIGGERS. 那也可能让你失望。

您可以使用向导生成包,然后编辑属性,或者使用向导插入临时表,然后从该表执行普通INSERT/SELECT到我们的teams表中,然后让触发器以这种方式触发。

于 2012-09-25T12:49:34.937 回答
1

批量插入向导/数据导入向导通常会绕过目标表上的触发器。

于 2012-09-25T13:06:57.280 回答