SQL - 相同表的2列的唯一键?

时间:2018-05-23 20:08:52

标签: sql sql-server unique-constraint unique-key check-constraint

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

在该表中,我有这3列(为了我的问题):

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

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

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

如果我创建一个新成员:

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

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

如果我更新现有会员:

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

-

根据我在Google上阅读的内容,应该可以使用基于函数的检查约束来执行某些操作,但我无法完成这项工作。

任何人都可以解决我的问题吗?

谢谢。

3 个答案:

答案 0 :(得分:0)

我可能误解了你的要求,但看起来你想要一个简单的upsert查询和IF EXISTS条件。

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;

答案 1 :(得分:0)

<强> TL; DR

需要重新考虑在数据库中应用这种业务规则逻辑的智慧 - 这种检查可能是您的应用程序的更好的候选者,或者作为插入门守护者而不是直接新行插入的存储过程桌子。

忽略警告

那就是说,我确实相信你想要的东西在约束UDF中是可能的,虽然可能会产生严重的性能影响 * 1 ,并且可能容易出现race conditions in set based updates

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

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

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

所以这是一个进行检查的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 up here

取消注释&#39;失败&#39;测试用例以确保上述检查约束正常工作。

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

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

答案 2 :(得分: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检查约束在基于集合的更新和/或其他各种条件下失败的可能性,触发器没有此问题。

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

相关问题