【问题标题】:SQL Server constraint HelpSQL Server 约束帮助
【发布时间】:2009-11-27 17:51:13
【问题描述】:

我在 SQL Server 2005 中提出足够的限制时遇到问题。我的问题涉及以下表格:

Table PKTable
{
  pk integer primary key,
  property integer,
}

Table FKTable
{
  pk integer primary key,
  fk integer references PKTable(pk), 
}

我真的想让记录 fk_rec 在它引用的 PKTable 记录的属性 = 5 时不存在。我尝试做的第一件事是使用以下查询创建模式绑定视图并创建一个唯一的UniqueCol 字段的索引。

SELECT     'True' AS UniqueCol, 'uh oh' AS DiffCol
FROM         FKTable INNER JOIN
                      PKTable ON FKTable.fk = PKTable.pk
WHERE      PKTable.property = 5
UNION
SELECT     'True' AS UniqueCol, 'default' AS DiffCol

所以基本上,('True', 'default') 记录将始终存在于视图中,并且当有人尝试将行插入 FKTable 并且它引用的 PKTable 记录具有属性 = 5 时,我得到约束违规因为上面联合查询的第一部分会返回一些东西。反正就是这个想法。但是,SQL Server 2005 不允许对涉及联合的视图进行索引。然后我尝试创建另一个视图:

SELECT UniqueCol, DiffCol FROM TheViewAbove

在这种情况下,在 UniqueCol 上放置唯一索引会失败,因为您不能在引用另一个视图的视图上设置索引。我知道这是一个很长的镜头。有什么办法可以解决这个问题?

谢谢!

【问题讨论】:

    标签: sql sql-server sql-server-2005


    【解决方案1】:

    两种方式。

    1. 创建一个子类型表(假设它称为NonProp5s,其中只有非属性 = 5 行,然后从 FKTable 而不是主 PKTable 引用此子类型表中的 pk。这不需要除了其中的 pk 列之外,(Create Table NonProp5s (pk Integer Primary Key Not Null) 的关键是插入到父 pkTable 中总是会在 NonProp5s 中插入一行,具有相同的 pk 值,除了属性值等于 5。(每当 PKTable 中的属性值更改为 5 或从 5 更改时,您还需要一个更新触发器来插入/删除行。)
    2. 创建 2 个触发器。如果您尝试插入引用具有属性 = 的 PK 行的记录,则对 FK 表的插入/更新会回滚事务 5,如果在 FKTable 中有任何子记录时尝试将属性更改为 value = 5,则更新 PK 表时会触发回滚事务的第二个触发器。

    【讨论】:

      【解决方案2】:
      1. 在 PKTable(pk,property) 上创建唯一约束
      2. 将列 PK_property 添加到您的子表。有一个检查约束确保 (property5)
      3. 有一个外键将 (pk,pk_property) 引用到 PKTable(pk,property)

      【讨论】:

      • @Nitai - 那行不通。您是否正确阅读了问题? @Alex - 我可能最终会按照你的建议去做。我以前做过,我不知道为什么我不愿改变这里的表格。
      【解决方案3】:

      我真的不想更改表格或表格之间的关系,所以这就是我所做的:

      我创建了一个名为DummyTable 的表,其中包含多条记录。我放了2个。然后我建了一个视图:

      SELECT pk FROM FKTable 
          INNER JOIN PKTable ON FKTable.fk = PKTable.pk
          INNER JOIN DummyTable ON 1 = 1
      WHERE PKTable.property = 5
      

      然后我在视图中的 pk 字段上放了一个唯一索引。这样,当存在违反我的约束的行时,我会得到重复。

      【讨论】:

        【解决方案4】:

        您还可以使用触发器来实现约束,如下所示:

        首先创建表:

        CREATE TABLE tPK(
        ID int NOT NULL PRIMARY KEY
        ,[Property] int NULL
        )
        go
        
        CREATE TABLE tFK(
        ID int NOT NULL PRIMARY KEY
        ,fk int NOT NULL
        )
        go
        
        ALTER TABLE tFK ADD
        CONSTRAINT FK1_tFK FOREIGN KEY(fk) REFERENCES tPK(ID)
            ON UPDATE CASCADE
            ON DELETE CASCADE
        go  
        

        然后将触发器添加到父表

        CREATE TRIGGER trgPK ON tPK
            AFTER INSERT, UPDATE
        AS
        IF UPDATE(Property) 
            BEGIN
                DELETE  FROM tFK 
                WHERE   fk IN ( SELECT  T.ID
                                FROM    tPK AS T
                                WHERE   T.[Property] = 5 )
        
            END
        go
        

        在子表上:

        CREATE TRIGGER trgFK ON dbo.tFK
            AFTER INSERT, UPDATE
        AS
        IF UPDATE(fk) 
            BEGIN
                DELETE  FROM tFK 
                WHERE   fk IN ( SELECT  T.ID
                                FROM    tPK AS T
                                WHERE   T.[Property] = 5 )
        
            END
        GO
        

        如果你有很多加载要做,那么:

        1. 禁用两个触发器,
        2. 加载数据,
        3. 启用两个触发器,
        4. 更新任一表中的一行。

        【讨论】:

          猜你喜欢
          • 2011-09-05
          • 2011-04-19
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-01-17
          • 2011-11-06
          相关资源
          最近更新 更多