【问题标题】:How do I create a table constraint to prevent duplicate values across two columns?如何创建表约束以防止两列中的重复值?
【发布时间】:2010-10-12 05:08:34
【问题描述】:

我有下表:

CREATE TABLE [dbo].[EntityAttributeRelship](
    [IdNmb] [int] IDENTITY(1,1) NOT NULL,
    [EntityIdNmb] [int] NOT NULL,
    [AttributeIdNmb] [int] NOT NULL,
    [IsActive] [bit] NOT NULL CONSTRAINT [DF_EntityAttributeRelship_IsActive]  DEFAULT ((0)),
CONSTRAINT [PK_EntityAttributeRelship] PRIMARY KEY CLUSTERED 
([IdNmb] ASC) WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]) ON [PRIMARY]

表中的部分数据如下所示:

IdNmb    EntityIdNmb    AttributeIdNmb  IsActive
1        22             7               0
2        22             8               0
3        22             9               0
4        22             10              1

如果已经存在 IsActive = 1 的 EntityIdNmb 记录,我想添加一个约束以确保没有人添加或更新记录以使 IsActive = 1。

我该怎么做?

【问题讨论】:

  • @Eric,如果时间允许,您愿意解释一下为什么我提出的聚簇索引视图解决方案 tomfut 不能满足您的要求吗?我认为 CI Views 是解决这类问题的优雅方案。
  • @Lieven 和 @tomfut,我创建了一个触发器来执行此操作,但我也会尝试您的解决方案,因为我一开始误解了您的答案。谢谢。

标签: sql constraints alter-table


【解决方案1】:

如果您使用的是 SQLServer,则可以创建聚集索引视图。

CREATE VIEW dbo.VIEW_EntityAttributeRelship WITH SCHEMABINDING AS
SELECT EntityIdNmb 
FROM dbo.EntityAttributeRelship
WHERE IsActive = 1
GO

CREATE UNIQUE CLUSTERED INDEX UIX_VIEW_ENTITYATTRIBUTERELSHIP 
  ON dbo.VIEW_EntityAttributeRelship (EntityIdNmb)

这可确保您的表中只有一个 IsActive = 1 的 EntityIdNmb。

【讨论】:

  • 这行不通。我需要返回所有记录,而不仅仅是 IsActive = 1 的记录。
  • @Eric,你真的没有尝试就对我投了反对票吗?它会按照您的要求执行“如果已经存在 IsActive = 1 的 EntityIdNmb 的记录,则没有人添加或更新记录以使 IsActive = 1”。
  • 我很抱歉。我已经删除了反对票。我误解了你的回答。我以为你是在告诉我使用视图来检索我的数据。
【解决方案2】:

听起来您需要实现一个触发器(假设您的数据库产品支持它)。如果您只想要一个活动条目和一个非活动条目,则唯一索引将起作用。否则,您将需要编写某种自定义约束或触发器(可能是 2 - 一个用于插入,一个用于更新),以确保您没有 2 条具有相同 id 的记录都处于活动状态。

【讨论】:

  • 您能否更深入地了解要在我的触发器中写入的内容?我是执行回滚还是什么?
  • Eric,如果你发现有冲突,抛出异常。触发器与存储过程没有太大区别 - 您只需以不同方式注册它,以便 db 知道何时执行它。
【解决方案3】:

如果您使用的是 MSSQL(我认为这就是您的语法),请创建一个仅包含 IsActive = 1 的行的视图,然后在视图中的 EntityIdNmb 上放置一个唯一索引。

在我最近使用较多的 PostgreSQL 中,您可以创建部分索引: http://www.postgresql.org/docs/8.3/interactive/indexes-partial.html

【讨论】:

  • @tomfut,你的想法似乎和我一样。由于某种原因,它不能满足 OP 的要求。
  • @Lieven,您的回答首先而且更详细!很抱歉垃圾邮件。 Belair 似乎认为您的回答暗示他应该从视图中执行 SELECTS,而不是原始表。
  • @tomfut,没有伤害。也许您对从视图中进行选择的假设是正确的。 @Belair,如果您应该阅读此内容,则该假设不正确。只需选择、更新并插入到您的原始表中。 CI 视图将处理约束。
【解决方案4】:

编写触发器的事情是决定您是否要拒绝记录,将值更改为 0 而不是 1 或将旧记录更新为 0 并让这条记录为 1。如果要删除值为1的记录,是否需要将另一条记录更改为活动记录,如何选择哪条记录?一旦您可以定义要在触发器中执行的操作,我们就可以帮助您更好地设计流程。

我们执行后两个步骤以使任何地址成为我们数据库中的主要邮寄地址。我们的业务规则是一个,只有一个地址可以是主地址,如果有任何地址,则必须将一个地址标记为主地址。这种触发器的关键是记住插入/更新/删除可以成批发生(即使这不是常态),并确保触发器以基于集合的方式工作。当我到达这里时,我们通过游标实现了多行处理,当我不得不在导入中更新 200,000 个地址时,这变得很糟糕。 (新手请注意 - 永远不要在触发器中使用光标!)

【讨论】:

    【解决方案5】:

    不活动的记录将用于什么?它们是否未使用并且只是用于跟踪以前的活动记录?如果是这样,是否可以将数据拆分到多个表中?比如……

    EntityAttributeRelship(IDNmb, EntityIDNmb, AttributeIDNmb)

    EntityAttributeRelshipHistory(IDNmb, EntityIDNmb, AttributeIDNmb)

    如果它在 EntityAttributeRelship 表中,则它是活动的。如果它在历史记录表中,那么它在某个时间点被激活并被停用?

    如果您确实需要一张表中的所有内容,我会接受 todd.run 的使用触发器的建议。

    【讨论】:

    • 我确实需要一张桌子上的所有东西。这是一个现有的表,数据已经存在。我只需要添加一个约束。如果我需要一个触发器,这是可以理解的,我只需要知道要写什么。
    猜你喜欢
    • 2010-10-15
    • 2011-04-15
    • 2012-04-03
    • 2021-06-16
    • 2013-11-17
    • 1970-01-01
    • 2021-12-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多