【问题标题】:How to prevent insertion of value if value exists in other columns如果其他列中存在值,如何防止插入值
【发布时间】:2021-02-05 09:00:24
【问题描述】:

给定表中的 3 列 tblEmails

  • email1,nvarchar(50),NULL 不允许
  • email2,nvarchar(50),允许 NULL
  • email3,nvarchar(50),允许 NULL

如果值已存在于其他列的 any 中,我如何防止向三列中的 any 插入或更新(非 NULL)值?

我希望通过检查三个表的UNION ALL 是否包含要插入的值来应用CONSTRAINT,但似乎count() 不能在CONSTRAINTs 中使用。

  • 任何可通过 SSMS gui 实施的解决方案都是理想的。
  • 我浏览了至少十几个 SO 帖子、一些 SE 帖子和在线文章,但找不到解决方案(或我能理解的解决方案)。

【问题讨论】:

  • 为什么你的表有tbl前缀?
  • ...为什么你的表是非规范化的?如果某人拥有 3 个以上的电子邮件地址怎么办?
  • 您说“三张桌子中的任何一张” - 但您只描述了一张桌子。你的意思是说“三列中的任何一个”?代替?
  • 什么是“SSMA”工具?您是指“SSMS”吗?
  • 假设他们的意思是 SQL Server 迁移助手 @Dai ,但不知道这对这里有什么帮助。

标签: sql sql-server unique


【解决方案1】:

我建议创建一个函数,然后由检查约束调用。举个例子:

CREATE FUNCTION dbo.fn_chkMail(@mail nvarchar(100)) RETURNS INT AS
BEGIN
  RETURN (SELECT COUNT(*) FROM mails WHERE mail1 = @mail) + (SELECT COUNT(*) FROM mails WHERE mail2 = @mail) + (SELECT COUNT(*) FROM mails WHERE mail3 = @mail)
END

然后

ALTER TABLE dbo.mails  WITH CHECK ADD  CONSTRAINT [CK_mails] 
CHECK  ((dbo.fn_chkMail([mail1]))+(dbo.fn_chkMail([mail2]))+(dbo.fn_chkMail([mail3]))=1)

详情见小提琴:http://sqlfiddle.com/#!18/6f375/7/1

【讨论】:

【解决方案2】:

当值已经在表中时,您希望防止使用update 修改值。不幸的是,这暗示了一个触发因素。

我认为逻辑是这样的:

CREATE TRIGGER trg_tblEmails_update ON tblEmails
AFTER UPDATE
AS BEGIN
    IF (EXISTS (SELECT 1
                FROM inserted i JOIN
                     deleted d
                     ON i.<primary key> = d.<primary key>
                WHERE (d.email1 IS NOT NULL OR
                       d.email2 IS NOT NULL OR
                       d.email3 IS NOT NULL
                      ) AND
                      (COALESCE(d.email1, '') <> COALESCE(i.email1, '') OR
                       COALESCE(d.email2, '') <> COALESCE(i.email2, '') OR
                       COALESCE(d.email3, '') <> COALESCE(i.email3, '') 
                      )
               )
       )
    BEGIN
        RAISERROR('You can not update emails when value is already present', 16, 1);
        ROLLBACK TRANSACTION;
    END;   
END;

我建议虽然可能有一个更简单的数据模型。例如,我建议将电子邮件存储在单独的表中,每封电子邮件一行。您可以将此表用作:

  • 当您在一封电子邮件中插入一个值时,您将插入所有三个。
  • 您在实体 ID 和电子邮件号码上有一个唯一索引。
  • 您不允许对表格进行更新。

编辑:

我怀疑你真的想要一个 unique 约束。您不是在查看行内,而是查看所有行。

如果是这种情况,您只是使用了错误的数据模型。您需要一张每行包含一封电子邮件的表格。这可能需要一个列来标识哪个电子邮件,但类似这样:

create table entity_emails (
    entity_email_id int identity(1, 1) primary key,
    which_email int,
    email varchar(255)
);

那么你需要以下约束:

check (which_email in (1, 2, 3));
unique (entity_id, which_email);
unique (email);

前两个将电子邮件数量限制为每个实体 3 个。第三个坚持email 在所有行和实体中都是唯一的。

使用正确的数据模型,您需要做的事情可能不需要触发器。

【讨论】:

  • 我无法完成这项工作。我的 SQL 知识有限,但我认为我输入了正确的值。对表的每次更新都会触发失败。查看代码,我不明白与整个列和原始列的比较发生在哪里;仅适用于更新和删除的行。
  • @kaanchan。 . .在您熟悉 SQL 之前,您不应该编写触发器。我怀疑其他人应该这样做。
  • 我大体上同意。我有编程和 QA 背景,并在此过程中进行检查。在某些情况下,我认为通过尝试来学习是可以的。您是否随时检查整个表(不仅仅是插入和删除的表)?如果电子邮件存在于另一个(未触及的、预先存在的)行中,您还怎么知道?
猜你喜欢
  • 2022-06-28
  • 2017-03-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-06-20
  • 1970-01-01
  • 2021-05-21
相关资源
最近更新 更多