2010-05-03 41 views
0

我有以下触发工作正确;SQL Server 2008中触发不能与多个刀片

CREATE TRIGGER trFLightAndDestination 
ON checkin_flight 
AFTER INSERT,UPDATE 
AS 
BEGIN 
    IF NOT EXISTS 
    (
        SELECT 1 
      FROM Flight v 
      INNER JOIN Inserted AS i ON i.flightnumber = v.flightnumber 
      INNER JOIN checkin_destination AS ib ON ib.airport = v.airport 
      INNER JOIN checkin_company AS im ON im.company = v.company 
      WHERE i.desk = ib.desk AND i.desk = im.desk 
    ) 
    BEGIN 
     RAISERROR('This combination of of flight and check-in desk is not possible',16,1) 
     ROLLBACK TRAN  
    END 
END 

我想触发做的是检查表飞行,checkin_destination和checkin_company时添加checkin_flight的新纪录。 checkin_flight的每个记录都包含一个航班号和一个桌号,乘客需要为此目的地办理登机手续。 的表格checkin_destination和checkin_company包含约仅限于某些签入课桌公司和目的地的信息。在向checkin_flight添加记录时,我需要从航班表中获取信息以获取插入航班号的目的地和航班公司。这些信息需要根据航班,目的地和公司的可用登记组合进行检查。

我使用的触发如上所述,但是当我尝试插入错误的组合触发允许它。我在这里错过了什么?

编辑1: 我使用下面的多个插入语句

INSERT INTO checkin_flight VALUES (5315,3),(5316,3),(5316,2) 
//5315 is the flightnumber, 3 is the desknumber to checkin for that flight 

编辑2: 测试单行插入这是不可能的,那么被抛出纠正错误。所以这是多重插入似乎给出了问题。

回答

1

问题是,您的逻辑允许任何插入,其中至少包含一组有效值。只有当插入的记录的全部无效时才会失败,而不是如果的任何的插入记录无效。

将您的“IF NOT EXISTS(...)”更改为“IF EXISTS(...)”语句并更改您的SELECT语句以返回无效航班。

例如:

IF EXISTS 
(
       SELECT 1 
     FROM Flight v 
     INNER JOIN Inserted AS i ON i.flightnumber = v.flightnumber 
     LEFT JOIN checkin_destination AS ib ON ib.airport = v.airport 
      AND i.desk = ib.desk 
     LEFT JOIN checkin_company AS im ON im.company = v.company 
      AND i.desk = im.desk 
     WHERE (im.desk IS NULL OR ib.desk IS NULL) 
) 
BEGIN 
    RAISERROR('This combination of of flight and check-in desk is not possible',16,1) 
    ROLLBACK TRAN  
END 
+0

谢谢克里斯!这就是诀窍,我猜想我需要在逻辑思维上进一步训练 – Rob 2010-05-03 18:32:30

+0

一个小问题,为什么左连接而不是内连接?因为桌子可以是空的? – Rob 2010-05-03 20:20:08

+1

我假设负面情况是checkin_destination或checkin_company中的相应记录不存在的情况;检查一些不存在的内容是通过LEFT JOINing对表进行的,然后查找没有记录(因此“im.desk IS NULL”部分)。这只是一个假设,你应该调整以纠正它。我所说的目标是让您的查询找到INSERTED中无效的任何记录。 – 2010-05-04 00:03:20

1

我不知道你的业务逻辑,但你需要检查查询做正确的事情。

你的问题是IF NOT EXISTS,如果在插入的3行不存在的条件1为真。您需要将其转换为查找问题行并使用IF EXISTS然后出错。

然而,当触发错误是最好的出路是:

RAISERROR() 
ROLLBACK TRANSACTION 
RETURN 

我有点怀疑,缺乏RETURN的是你的问题,但它始终是最好的,包括三个R小号当在触发器中出错时。

+1

更好地使用当然 – gbn 2010-05-03 17:54:57

+0

的TRY/CATCH在我看来,明确的回滚是一般的触发危险的,因为它假设的基础行为,可能是没有道理的。 XACT_ABORT默认是禁用的。 – Einstein 2010-05-03 18:09:27

+1

@爱因斯坦,我不同意。如果我的触发器检测到问题,我总是希望它回滚。也许这只是我和我的代码,但我总是希望在错误点回滚,所以我可以在日志表中插入正在发生的事情。调用代码的责任是意识到发生了错误,并且可以在错误日志中添加其他信息,因为它也会终止。但是我可以看到,如果您有不同的错误处理系统,触发器中的回滚可能会导致您遇到问题(如果您不期望)。 – 2010-05-03 18:28:11

1

插入的表可以包含多行,因此在触发器中的所有逻辑必须能够适用于所有的行。想法触发器必须每行触发一次效应是WRT触发器常见的误解。 SQL Server倾向于将调用合并到触发器中,以便在同一事务中发生时提高性能。

要解决您的问题,可能会从插入的COUNT()开始,并将其与COUNT()的匹配条件进行比较,如果存在不匹配则会产生错误。

+0

不应该选择1和连接的组合照顾呢? – Rob 2010-05-03 18:01:29

+0

@Einstein表示* SQL Server倾向于将触发合并到一个触发器,以提高在同一事务中发生的性能。*什么?所以如果我做'BEGIN TRANSACTION; INSERT YourTable VALUES(...); INSERT YourTable VALUES(...); INSERT YourTable VALUES(...); COMMIT;'你是说SQL Server只会触发一次?没门! – 2010-05-03 18:29:54

+0

我不知道确切的语义,但是它的非常酷:) – Einstein 2010-05-03 23:16:12

1

问题是如果只有一个插入的记录是正确的,则条件为真。你必须检查所有记录是否正确,例如:

if (
    (
    select count(*) from inserted 
) = (
    select count(*) from flight v 
    inner join inserted i ... 
) 
) ... 
+0

啊现在我也得到什么爱因斯坦说:)听起来合乎逻辑!我会试试看。是'如果不是'正确的方式来恢复表达? – Rob 2010-05-03 18:05:10

+0

你的计数的确有窍门,但Chris Shaffers也是如此。感谢您的帮助! – Rob 2010-05-03 18:32:03

+0

@Rob:对,你需要检查计数的差别,所以你可以使用'<>'运算符而不是'='。 – Guffa 2010-05-03 18:57:01