Как мне проверить ограничение, которое ссылается на другую таблицу? - PullRequest
7 голосов
/ 16 сентября 2010

У меня есть следующие таблицы в базе данных SQL Server 2008:

  • tblItem , который имеет поле ItemID ;

  • tblGoodItem , который также имеет поле ItemID и имеет внешний ключ, указывающий на tblItem;

  • tblBadItem , который также имеет поле ItemID и также имеет внешний ключ, указывающий на tblItem.

Предмет не может быть и хорошим, и плохим; это должен быть либо тот, либо другой. Тем не менее, независимо от того, является ли товар хорошим или плохим, это должен быть товар.

У меня такой вопрос: как добавить ограничение к полям ItemID в tblGoodItem и tblBadItem, чтобы значение ItemID не могло существовать в обеих таблицах ?

Я прочитал несколько ответов в Stack Overflow на похожие вопросы, и я думаю об этом решении:

  • Создать представление vwItem , которое присоединяется к tblGoodItem в tblBadItem для ItemID.

  • Напишите UDF fnItem , который выполняет запрос к vwItem, чтобы увидеть, сколько записей существует в представлении.

  • Есть ограничение, которое вызывает fnItem и проверяет, что возвращаемое значение равно 0.

Это лучшая идея? У кого-нибудь есть идея получше?

Ответы [ 5 ]

9 голосов
/ 16 сентября 2010

Добавить столбец tblItem.ItemType column. Этот столбец может иметь только одно значение в любой данной строке (очевидно). Добавьте уникальное ограничение для 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. ОБНОВЛЕНИЕ ItemType строки в tblItem
  3. Вставить строку в tblBadItem
2 голосов
/ 16 сентября 2010

избавьтесь от tblGoodItem и tblBadItem и создайте новую таблицу с ItemType = "G" или "B" и поместите уникальный индекс или ключ в ItemID, тогда для tblItem не требуется никаких ограничений.

1 голос
/ 16 сентября 2010

В tblItem добавьте столбец itemType.Имейте проверочное ограничение, чтобы убедиться, что itemType является хорошим или плохим.Создайте уникальное ограничение для (ItemID, itemType)

Добавьте столбец itemType в таблицы плохих и хороших предметов.Имейте проверочное ограничение, чтобы убедиться, что itemType хорош в хорошей таблице и плох в плохой таблице.

1 голос
/ 16 сентября 2010

Я, вероятно, не понимаю ваши бизнес-требования здесь, но почему вы хотите иметь отдельную таблицу для хороших и плохих предметов? Разве это не абстракции одного и того же?

Почему бы не использовать флаг isBadItem или, более конкретно, столбец itemConditionStatus.

1 голос
/ 16 сентября 2010

Вы не можете использовать оператор SELECT в CHECK Ограничении - это не совсем то, для чего они были разработаны.

Я думаю, что ваш лучший вариант - записать проход UDF в ItemId и проверить, существует ли он. Для этого сценария это действительно самый простой вариант.

Я добавил несколько тестовых данных и пример функции.

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
...