如何使用引用另一个表的检查约束?

时间:2010-09-16 15:43:02

标签: sql sql-server tsql sql-server-2008 check-constraints

我在SQL Server 2008 db中有以下表格:

  • tblItem ,其中包含 ItemID 字段;

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

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

项目既不是好项目,也不是坏项目;它必须是一个或另一个。但是,无论项目是好还是坏,都必须是项目。

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

我已经在类似的问题上阅读了Stack Overflow中的一些回复,我正在考虑这个解决方案:

  • 创建一个视图 vwItem ,它在ItemID上的tblBadItem上连接tblGoodItem。

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

  • 有一个约束调用fnItem并验证返回的值是否为0.

这是最好的主意吗?有没有人有更好的主意?

5 个答案:

答案 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. 在tblItem中更新行的ItemType
  3. 在tblBadItem中插入行

答案 1 :(得分:2)

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

答案 2 :(得分:1)

您不能在SELECT约束中使用CHECK语句 - 这实际上不是它们的设计目的。

我认为你最好的选择是在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

答案 3 :(得分:1)

我可能不了解您的业务需求,但为什么您希望有一个单独的表来表示好的和坏的项目?这些不是同一事物的抽象吗?

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

答案 4 :(得分:1)

在tblItem中,添加itemType列。有一个检查约束,以确保itemType好或坏。在(ItemID,itemType)

上创建唯一约束

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