我在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.
这是最好的主意吗?有没有人有更好的主意?
答案 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 :(得分: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在良好的表中是好的,在坏表中是坏的。