2010-09-16 54 views
7

我有一个SQL Server下表2008分贝:如何使用引用另一个表的检查约束?

  • tblItem,具有项目ID场;

  • tblGoodItem,它还有一个ItemID字段,并有一个指向tblItem的外键;

  • tblBadItem,它也有一个ItemID字段,并且还有一个指向tblItem的外键。

一件物品不能既是好物品又是坏物品;它必须是一个或另一个。但是,无论项目是好还是坏,它都必须是一个项目。

我的问题是这样的:如何向tblGoodItem和tblBadItem中的ItemID字段添加一个约束,以便两个表中都不能存在ItemID值

我读过的堆栈溢出一些回答类似的问题,我想这个解决方案:

  • 创建一个视图vwItem它连接tblGoodItem上tblBadItem上项目ID。

  • 编写一个UDF fnItem它对vwItem进行查询以查看视图中存在多少记录。

  • 有哪些要求fnItem并验证返回的值是0

这是最好的办法约束?有没有人有更好的主意?

回答

9

添加一列tblItem.ItemType列。这个列在任何给定的行上只能有一个值(显然)。在ItemID,ItemType上添加唯一约束。

现在的技巧:很少有人记住这一点,但外键可以引用唯一约束的列。

CREATE TABLE tblItem (
    ItemID INT PRIMARY KEY, 
    ItemType CHAR(1), 
    UNIQUE KEY (ItemID, ItemType) 
); 

CREATE TABLE tblGoodItem (
    ItemID INT PRIMARY KEY, 
    ItemType CHAR(1), 
    CHECK (ItemType='G') 
    FOREIGN KEY (ItemID, ItemType) REFERENCES tblItem(ItemID, ItemType) 
); 

CREATE TABLE tblBadItem (
    ItemID INT PRIMARY KEY 
    ItemType CHAR(1), 
    CHECK (ItemType='B') 
    FOREIGN KEY (ItemID, ItemType) REFERENCES tblItem(ItemID, ItemType) 
); 

如果你限制的ItemType在每个子表为一个固定值,那么在tblItem给定行只能由一个子表引用。

这是一个三步过程,一个项目从好变成坏的,虽然:

  1. 删除行从tblGoodItem
  2. UPDATE行的的ItemType在tblItem
  3. 插入行中tblBadItem
+0

你打败了我几秒钟。 – 2010-09-16 16:14:43

+0

我很喜欢这个想法。无需担心从坏到好 - 事实上,我并不希望发生这样的变化。 – 2010-09-16 16:35:12

2

摆脱tblGoodItem和tblBadItem,并使用ItemType =“G”或“B”创建一个新表,并在ItemID上放置一个唯一的索引或键,然后在tblItem上不需要约束。

+0

不行,恐怕。好的物品有一些不好的物品没有的特性,反之亦然。 – 2010-09-16 16:05:24

+0

如果只有少数,10个以内的差异,那么我仍然将它们合并,让它们允许NULL并添加一个检查约束来要求它们基于类型列 – 2010-09-16 17:43:43

1

您不能在CHECK约束中使用SELECT声明 - 这不是他们的设计。

我认为你最好的选择是在ItemId中编写一个UDF通道并检查它是否存在。对于这种情况,它确实是最简单的选择。

我已经添加了一些测试数据和一个示例函数。

CREATE FUNCTION dbo.fn_CheckItems(@itemId INT) RETURNS BIT 

AS BEGIN 

DECLARE @i INT, 
     @rv BIT 


SET @i = 0 

IF (SELECT COUNT(*) FROM tblBadItem WHERE ItemId = @ItemId) > 0 
BEGIN 
SET @i = 1 
END 


IF (SELECT COUNT(*) FROM tblGoodItem WHERE ItemId = @ItemId) > 0 
BEGIN 
SET @i = @i + 1 
END 

IF (@i > 1) 
BEGIN 
    SET @rv = 1 
END 
ELSE 
BEGIN 
    SET @rv =0 
END 


RETURN @rv 

END 
GO 

CREATE TABLE tblItem (
    ItemID INT IDENTITY(1,1) PRIMARY KEY, 
    DateAdded DATETIME 
) 
GO 

CREATE TABLE tblGoodItem (
    ItemID INT PRIMARY KEY, 
    CHECK (dbo.fn_CheckItems(ItemId) = 0) 

) 
GO 

CREATE TABLE tblBadItem (
    ItemID INT PRIMARY KEY, 
    CHECK (dbo.fn_CheckItems(ItemId) = 0) 
) 
GO 

INSERT INTO tblItem (DateAdded) 
VALUES (GETDATE()) 

INSERT INTO tblGoodItem(ItemID) 
SELECT ItemId FROM tblItem 

--This statement will fail as the ItemId is already in GoodItems 
INSERT INTO tblBadItem(ItemID) 
SELECT ItemId FROM tblItem 


DROP TABLE tblItem 
DROP TABLE tblGoodItem 
DROP TABLE tblBadItem 
DROP FUNCTION dbo.fn_CheckItems 
+0

这样的UDF对于多行更新无法正确工作 – 2010-09-16 16:13:52

+0

@AlexKuznetsov - 你能详细说明为什么这不起作用。我创建了一个测试,它工作正常吗? – codingbadger 2010-09-16 16:26:35

+0

我的歉意,我错了。但是,这种UDF非常慢,如果使用快照隔离,您可能偶尔在并发下得到错误的结果。 – 2010-09-16 19:40:12

1

我可能在这里不了解您的业务需求,但您为什么希望为Good and Bad项目设置单独的表格?这些不是同一件事物的抽象吗?

为什么不使用isBadItem标志或更具体地说itemConditionStatus列。

1

在tblItem中,添加itemType列。有一个检查约束,以确保itemType是好还是坏。在(ItemID,itemType)上创建唯一约束

将itemType列添加到不良项目表和良品项表中。有一个检查约束,以确保itemType在良好的表中良好,在坏的表中不好。