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

Sql 如何编写一个触发器来限制bankcustomer表中的一个人在account表中只能有3个帐户?,sql,sql-server,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,

我有一个
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
)
以及一个如下所示的科目表:

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中编写一个触发器来限制
银行客户
,以便
银行客户
帐户
表中最多只能有3个帐户

我通过在账户表的
accountowner
列中插入与
bankcustomer
表中的
cpr
匹配的值(将记录插入
账户
表时),为特定银行客户创建账户

到目前为止,我有以下代码:

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个帐户,我也可以继续为客户创建帐户(在帐户表中插入记录)。这意味着触发器中的代码根本不起作用


任何帮助都将不胜感激

让我们考虑一下您的代码。首先,显然您只使用单行插入进行测试。这可能会一直延续到您的sql代码中。这很糟糕,因为插入(或更新、删除或合并)可以影响任意数量的行。虽然您可以预期来自应用程序的大多数插入可能是单行,但总会存在影响多行的情况。这是一个假设,在编写sql代码时,您应该始终牢记这一假设,特别是在编写触发器时

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

您的select对账单已插入父银行客户。联接是正确的,但为什么有where子句?让我们转向最佳实践。始终-始终-为每个表指定一个有用的别名,并在引用列时使用该别名。为什么?因为这使其他人更容易阅读和理解您的查询。顺便说一句,有用的别名不是单个字符。是的,编写代码可能是一项小小的工作

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

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

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

我将让您彻底地测试is,这包括使用插入多行的insert语句。当然,您将改变测试数据,以包括没有帐户、少于3个帐户、正好3个帐户和多于3个帐户的客户(因为有时会发生一些事情,尽管您尽了最大的努力,还是会添加额外的帐户)

您使用的是哪种dbms?Microsoft SQL Server Management Studio 17开始时,请尝试>2改为>3或更改为>=3它不会更改您的逻辑,但将“where cpr=”替换为“on bankcustomer.cpr”,以符合ANSI标准,并保留两部分名称。
cpr=inserted.accountowner
表示
inserted.cpr=inserted.accountowner
对吗?