8

我在 SQL Server 2008 数据库中有以下表:

  • tblItem,它有一个ItemID字段;

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

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

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

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

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

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

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

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

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

4

5 回答 5

10

添加一列 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 中插入行
于 2010-09-16T16:12:28.253 回答
1

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

于 2010-09-16T15:48:40.857 回答
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
于 2010-09-16T15:48:41.753 回答
1

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

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

于 2010-09-16T15:49:10.197 回答
1

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

将 itemType 列添加到 bad 和 good items 表中。有一个检查约束来确保 itemType 在好表中是好的,在坏表中是坏的。

于 2010-09-16T16:13:08.610 回答