1

我使用 SQL Server 2016。我有一个名为“成员”的数据库表。

在该表中,我有这 3 列(出于我的问题的目的):

  • idMember [INT - 身份 - 主键]
  • memEmail
  • memEmailPartner

我想阻止一行使用表中已经存在的电子邮件。

两个电子邮件列都不是强制性的,因此它们可以留空 (NULL)。

如果我创建一个新成员:

如果不为空,则为“memEmail”和“memEmailPartner”(独立)输入的值不应在 memEmail 或 memEmailPartner 列的任何其他行中找到。

因此,如果我想使用电子邮件 (dominic@email.com) 创建一行,我一定不能在 memEmail 或 memEmailPartner 中找到该值的任何出现。

如果我更新现有会员:

我不能在 memEmail 或 memEmailPartner 中找到该值的任何出现,除了我正在更新已在 memEmail 或 memEmailPartner 中具有值的行 (idMembre)。

--

根据我在 Google 上阅读的内容,应该可以使用基于函数的检查约束来做一些事情,但我无法做到这一点。

有人可以解决我的问题吗?

谢谢你。

4

3 回答 3

0

我可能完全误解了您的要求,但看起来您想要一个带有 IF EXISTS 条件的简单 upsert 查询。

DECLARE @emailAddress VARCHAR(255)= 'dominic@email.com', --dummy value
        @id INT= 2;                                      --dummy value

IF NOT EXISTS
(
   SELECT 1
   FROM @Member
   WHERE memEmail = @emailAddress
         OR memEmailPartner = @emailAddress
)
    BEGIN
        SELECT 'insert';
    END;
ELSE IF EXISTS 
(
  SELECT 1
  FROM @Member
  WHERE idMember = @id
)
    BEGIN
       SELECT 'update';
    END;
于 2018-05-23T20:36:47.323 回答
0

TL;博士

需要重新考虑在数据库中应用这种业务规则逻辑的智慧 - 此检查可能更适合您的应用程序,或者充当插入门卫而不是直接将新行插入表中的存储过程。

忽略警告

就是说,我确实相信您想要的东西在约束 UDF 中是可能的,尽管可能会带来严重的性能后果*1,并且在基于集合的更新中可能容易出现竞争条件

这是一个用户定义的函数,它在两列中应用独特的电子邮件逻辑。请注意,在检查约束时,该行已经在表中,因此新行本身需要从重复检查中排除。

我的代码也依赖于 ANSI NULL 行为,即谓词NULL = NULLX IN (NULL)都返回 NULL,因此被排除在故障检查之外(为了满足您的要求,即 NULLS 不会使规则失败)。

我们还需要检查插入的两个新列是否非空,但重复。

所以这是一个进行检查的UDF:

CREATE FUNCTION dbo.CheckUniqueEmails(@id int, @memEmail varchar(50), 
                                      @memEmailPartner varchar(50))
RETURNS bit
AS 
BEGIN
   DECLARE @retval bit;
   IF @memEmail = @memEmailPartner
     OR EXISTS (SELECT 1 FROM MyTable WHERE memEmail IS NOT NULL 
                AND memEmail IN(@memEmail, @memEmailPartner) AND idMember <> @id)
     OR EXISTS (SELECT 1 FROM MyTable WHERE memEmailPartner IS NOT NULL 
                AND memEmailPartner IN(@memEmail, @memEmailPartner) AND idMember <> @id)
     SET @retval = 0
   ELSE 
     SET @retval = 1;
  RETURN @retval;
END;
GO

然后在CHECK约束中强制执行:

ALTER TABLE MyTable ADD CHECK (dbo.CheckUniqueEmails(
                                       idMember, memEmail, memEmailPartner) = 1);

我在这里放了一个 SQLFiddle

取消注释“失败”的测试用例以确保上述检查约束有效。

我还没有通过更新对此进行测试,并且根据 Martin 对链接的建议,这可能会在插入多行时中断。

*1 - 我们需要两个电子邮件地址列的索引。

于 2018-05-23T21:22:58.470 回答
0

触发器是按照您的要求进行操作的传统方式。这是一个简单的演示;

--if object_id('member') is not null drop table member
go

create table member (
    idMember INT Identity Primary Key,
    memEmail varchar(100),
    memEmailPartner varchar(100) 
)
go

create trigger trg_member on member after insert, update as
begin
    set nocount on

    if exists (select 1 from member m join inserted i on i.memEmail = m.memEmail and i.idMember <> m.idMember) or
       exists (select 1 from member m join inserted i on i.memEmail = m.memEmailPartner and i.idMember <> m.idMember) or
       exists (select 1 from member m join inserted i on i.memEmailPartner = m.memEmail and i.idMember <> m.idMember) or
       exists (select 1 from member m join inserted i on i.memEmailPartner = m.memEmailPartner and i.idMember <> m.idMember) 
    begin
        raiserror('Email addresses must be unique.', 16, 1)
        rollback
    end 
end
go

insert member(memEmail, memEmailPartner) values('a@a.com', null), ('b@b.com', null), (null, 'c@c.com'), (null, 'd@d.com')
go

select * from member

insert member(memEmail, memEmailPartner) values('a@a.com', null) -- should fail
go
insert member(memEmail, memEmailPartner) values(null, 'a@a.com') -- should fail
go
insert member(memEmail, memEmailPartner) values('c@c.com', null) -- should fail
go
insert member(memEmail, memEmailPartner) values(null, 'c@c.com') -- should fail
go

insert member(memEmail, memEmailPartner) values('e@e.com', null) -- should work
go
insert member(memEmail, memEmailPartner) values(null, 'f@f.com') -- should work
go

select * from member

-- Make sure updates still work!
update member set memEmail = memEmail, memEmailPartner = memEmailPartner

我没有对此进行过广泛的测试,但如果你想尝试这种方法,它应该足以让你开始。

StuartLC 注意到 UDF 检查约束在基于集合的更新和/或各种其他条件下失败的可能性,触发器没有这个问题。

Stuart 还建议重新考虑这是否真的应该是数据库约束或通过其他地方的业务逻辑进行管理。我倾向于同意 - 我的直觉是,迟早你会遇到需要重复使用电子邮件地址的情况,或者以其他方式不是严格唯一的。

于 2018-05-23T22:27:48.433 回答