8

SQL Server 2008 データベースに次のテーブルがあります。

  • ItemIDフィールドを持つtblItem 。

  • tblGoodItemにも ItemID フィールドがあり、tblItem を指す外部キーがあります。

  • tblBadItemにも ItemID フィールドがあり、tblItem を指す外部キーもあります。

アイテムは、良いアイテムと悪いアイテムの両方になることはできません。どちらか一方でなければなりません。しかし、その商品が良くも悪くも、それは商品でなければなりません。

私の質問はこれです: ItemID 値が両方のテーブルに存在できないように、tblGoodItem と tblBadItem の両方の ItemID フィールドに制約を追加するにはどうすればよいですか?

同様の質問に対するスタック オーバーフローの回答をいくつか読みましたが、この解決策を考えています。

  • ItemID で tblBadItem の tblGoodItem を結合するビューvwItemを作成します。

  • ビューにいくつのレコードが存在するかを確認するために、vwItem に対してクエリを実行する UDF fnItemを記述します。

  • fnItem を呼び出して、返される値が 0 であることを確認する制約があります。

これは最高のアイデアですか?誰かがより良いアイデアを持っていますか?

4

5 に答える 5

10

列 tblItem.ItemType 列を追加します。この列は、任意の行で値を 1 つだけ持つことができます (当然のことです)。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 つの子テーブルのみから参照できます。

ただし、アイテムを良いものから悪いものに変更するのは 3 ステップのプロセスです。

  1. tblGoodItem から行を削除
  2. tblItem の UPDATE 行の ItemType
  3. tblBadItem に行を挿入
于 2010-09-16T16:12:28.253 に答える
1

tblGoodItem と tblBadItem を取り除き、ItemType="G" または "B" で新しいテーブルを作成し、ItemID に一意のインデックスまたはキーを配置すると、tblItem に制約は必要ありません。

于 2010-09-16T15:48:40.857 に答える
1

Constraintで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 列を追加します。itemType が適切なテーブルでは適切であり、不適切なテーブルでは不適切であることを確認するためのチェック制約があります。

于 2010-09-16T16:13:08.610 に答える