如何编写触发器以限制bankcustomer表中的一个人在帐户表中只能有3个帐户?

时间:2018-09-25 20:07:56

标签: sql sql-server

我有一个bankcustomer表,看起来像这样:

create table bankcustomer
(
    cpr char(10) primary key,
    name varchar(30) not null
)

还有一个如下所示的帐户表:

create table account
(
    accountnr int identity(1001,1) primary key,
    accountowner char(10) foreign key references bankcustomer, 
    created date not null,
    balance decimal(14,2) not null
)

我想在SQL Server中编写一个限制bankcustomer的触发器,以便一个bankcustomeraccount表中最多可以有3个帐户。

我通过在帐户表的accountowner列中插入与cpr表中的bankcustomer相匹配的值来创建特定银行客户的帐户(将记录插入account表)。

到目前为止,我有此代码:

create trigger mytrigger4
on account
for insert
as
    if exists (select count(*) 
               from inserted 
               join bankcustomer on inserted.accountowner = bankcustomer.cpr 
               where cpr = inserted.accountowner 
               having count(*) > 3)
    begin
        rollback tran
        raiserror('A customer must have a maximum of 3 accounts', 16, 1)
    end
go

问题是,即使客户已经有3个帐户,我仍可以继续为其创建帐户(在帐户表中插入记录)。这意味着触发器中的代码根本不起作用。

任何帮助将不胜感激!

1 个答案:

答案 0 :(得分:1)

让我们考虑一下您的代码。首先,很明显,您仅使用单行插入进行测试。通常,这可能会延续到您的sql代码中。不好,因为插入(或更新,删除或合并)会影响任意数量的行。尽管可以预期应用程序中的大多数插入可能是单行,但总有一些情况会影响多行。这是通常在编写sql代码时应始终记住的一个假设-并专门触发。

您的测试基于存在。那就是测试存在子句中查询生成的行是否存在。因此-请仔细查看您的查询。首先,您计数但不分组。因此,您正在计算查询生成的所有行。由于前面提到的假设,这是不正确的。但是让我们暂时忽略它,仅检查select语句。

您的选择语句联接被插入到上级银行客户。联接是正确的,但是为什么会有where子句?让我们回避最佳实践。始终-始终-为每个表提供有用的别名,并在引用列时使用该别名。为什么?因为这样可以使其他人更容易阅读和理解您的查询。顺便说一句-有用的别名不是单个字符。是的-编写代码可能会有点麻烦。

让我们继续。您的查询计数结果集中的所有行。如果您插入一行,那么加入的结果是什么?我们知道一个帐户与一个银行客户相关联。因此,当您在帐户中插入一行时,联接将产生“单行”。计算该单行结果集将始终产生值为-tadah-1的单行。现在,有一种方法可以使您的触发器生成错误。用一条语句插入4行或更多行。该错误可能并不准确,但是它将终止该事务并显示一条消息。

所以您看到您的逻辑有缺陷。您需要计算实际表(帐户)中未插入的行。但不是所有的行-因为那样效率低下。您只需要考虑所有“共享”插入的帐户所有者值的行。注意复数的“值”。这是单行假设失败的地方。那么,该怎么做呢?这是编写触发器的一种方法。请注意,包含了第一个查询,以使您“查看”计数查询产生的内容-这仅用于调试。生产触发器不应以任何方式返回结果集。

alter trigger mytrigger4
on account
for insert
as begin

    select cust.cpr, count(*) 
               from bankcustomer as cust join account as acc on cust.cpr = acc.accountowner
               where exists (select * from inserted as ins where ins.accountowner = cust.cpr)
               group by cust.cpr;

    if exists (select cust.cpr, count(*) 
               from bankcustomer as cust join account as acc on cust.cpr = acc.accountowner
               where exists (select * from inserted as ins where ins.accountowner = cust.cpr)
               group by cust.cpr
               having count(*) > 3)
    begin
        rollback tran
        raiserror('A customer must have a maximum of 3 accounts', 16, 1)
    end
end;
go

我将留给您进行实际测试,以证明彻底-其中包括使用插入多行插入语句。而且,当然,您将更改测试数据,以包括没有帐户,少于3个帐户,恰好3个帐户和3个以上帐户的客户(因为尽管您尽了最大努力有时会发生某些事情,并且会添加额外的帐户)。