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

发布于 2024-09-19 22:37:32 字数 763 浏览 10 评论 0原文

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

  • tblItem,其中有一个 ItemID 字段;

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

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

一个物品不可能既是好物品又是坏物品;它必须是其中之一或另一个。然而,无论物品是好还是坏,它都必须是物品。

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

我在 Stack Overflow 上读过一些关于类似问题的回复,我正在考虑这个解决方案:

  • 创建一个视图 vwItem,它将 tblGoodItem 加入到 ItemID 上的 tblBadItem 上。

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

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

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

I have the following tables in a SQL Server 2008 db:

  • tblItem, which has an ItemID field;

  • tblGoodItem, which also has an ItemID field, and has a foreign key pointing to tblItem;

  • tblBadItem, which also has an ItemID field, and also has a foreign key pointing to tblItem.

An item cannot be both a good item and a bad item; it must either be the one or the other. However, whether the item is good or bad, it must be an item.

My question is this: how do I add a constraint to the ItemID fields in both tblGoodItem and tblBadItem so that an ItemID value cannot exist in both tables?

I've read some replies in Stack Overflow on similar questions, and I'm thinking of this solution:

  • Create a view vwItem which joins tblGoodItem on tblBadItem on ItemID.

  • Write a UDF fnItem which does a query on vwItem to see how many records exist in the view.

  • Have a constraint which calls fnItem and verifies that the value returned is 0.

Is this best idea? Does anyone have a better idea?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

我的鱼塘能养鲲 2024-09-26 22:37:32

添加一列 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. DELETE row from tblGoodItem
  2. UPDATE row's ItemType in tblItem
  3. INSERT row in tblBadItem

Add a column tblItem.ItemType column. This column can have only one value on any given row (obviously). Add a unique constraint over ItemID,ItemType.

Now the trick: few people remember this, but a foreign key can reference the columns of a unique constraint.

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) 
);

If you constrain ItemType in each of the child tables to a fixed value, then a given row in tblItem can be referenced by only one child table.

It's a three-step process to change an item from good to bad, though:

  1. DELETE row from tblGoodItem
  2. UPDATE row's ItemType in tblItem
  3. INSERT row in tblBadItem
早茶月光 2024-09-26 22:37:32

摆脱 tblGoodItem 和 tblBadItem 并创建一个具有 ItemType="G" 或 "B" 的新表,并在 ItemID 上放置唯一索引或键,则不需要对 tblItem 进行约束。

get rid of tblGoodItem and tblBadItem and make a new table with a ItemType="G" or "B" and put an unique index or key on ItemID, then no constraint is needed on tblItem.

゛清羽墨安 2024-09-26 22:37:32

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

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

I'm probably not understanding your business requirements here but why do you wish to have a separate table for Good and Bad items? Are these not abstractions of the same thing?

Why not use an isBadItem flag or more specifically an itemConditionStatus column.

笨笨の傻瓜 2024-09-26 22:37:32

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

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

In tblItem, add itemType column. Have a check constraint to make sure that itemType is either good or bad. Create a unique constraint on (ItemID, itemType )

Add itemType column to both bad and good items tables. Have a check constraint to make sure that itemType is good in good table, and bad in bad table.

迷乱花海 2024-09-26 22:37:32

您不能在 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

You can't use a SELECT statement in a CHECK Constraint - thats not really what they were designed for.

I think your best option would be to write a UDF pass in the ItemId and check if it exists. For this scenario it really is the easiest option.

I've added some test data and an example function.

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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文