2013-04-09 6 views
0

Мне нужна помощь с ограничениями в SQL Server. Ситуация для каждого OrderID = 1 (внешний ключ не первичный ключ, так что в таблице несколько строк с одинаковым идентификатором), поле бит может быть только 1 для одной из этих строк, а для каждой строки с OrderID = 2, поле бит может быть только 1 для одной строки и т. д. Это должно быть 0 для всех других строк с одинаковым идентификатором OrderID. Любые новые записи, поступающие с 1 в поле бит, должны отклонить, если уже есть строка с этим идентификатором OrderID, у которой бит бит установлен на 1. Любые идеи?Ограничение SQL Server (поле ограничения бит на основе внешнего ключа)

+0

Если вы не отвечаете на вопросы, особенно если в версии до 2008 года вы можете написать триггер вставки/обновления, который отклонит дубликаты – Mark

ответ

1

Вы можете более полно нормализовать схему, которая поможет вам не искать для уже установленного бита, а использовать соединение. Вам нужно удалить поле бит и вставить новую таблицу, где X содержит OrderID и первичный ключ вашей таблицы, причем основным ключом X является все эти поля.

Это означает, что при вставке вам нужно вставить его в исходную таблицу и в X f и только если бы вы установили бит в 1 на свой стол. Вставка завершится неудачно, если в X уже есть строка, которая, как если бы была уже исходная строка с битом, установленным в 1.

Недостатком является то, что это занимает больше места, чем схема, но проще в обслуживании. вы не можете получить эквивалент двух строк с битом, установленным в 1.

+0

Я буду иметь это в виду, если мне нужно изменить структуру таблицы. Благодаря! – user2263882

0

Единственный способ сделать это - подкласс родительской таблицы. Вы не упомянули об этом, но общей причиной этого шаблона является представление одной уникальной активной строки из набора всех строк с тем же общим значением ключа. Давайте предположим, что ваш битовое поле представляет активные Приказы .... Тогда я хотел бы создать отдельную таблицу под названием ActiveOrders, который будет содержать только одну строку с битовым полем, создаваемым 1

Create Table ActiveOrders(int Orderid Primary Key Null) 

и другая таблица с всех строк в нем, с его собственной уникальной Primary Key OrderId

Create Table AllOrders 
(OrderId Integer Primary Key Not Null, ActiveOrderId Integer Not Null, 
[All other data fields] 
Constraint FK_AllOrders2ActiveOrder 
    Foreign Key(ActiveOrderId) references ActiveOrders(OrderId)) 

Теперь вам больше не нужно битовое поле, так как наличие строки в таблице ActiveOrders идентифицирует ее как активный порядок ... Чтобы получить только активные Заказы (те, которые на вашей схеме будут иметь заданное битовое поле к 1), просто присоединитесь к двум таблицам.

+0

Спасибо, это хорошее предложение. Я не решаюсь изменить структуру таблицы в данный момент, но я буду помнить об этом. – user2263882

0

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

CREATE FUNCTION fnMyCheck 
    (@id INT) 
RETURNS INT 
AS 
BEGIN 
    DECLARE @i INT 

    SELECT @i = COUNT(*) 
    FROM MyTable 
    WHERE FkCol = @id 
    AND BitCol = 1 

    RETURN @i 
END 

ALTER TABLE YourTable 
ADD CONSTRAINT ckMyCheck CHECK (fnMyCheck(FkCol)<=1) 

но есть проблемы, которые могут исходить от этого с помощью UDF в проверочном ограничении, такие как this

Edit, чтобы добавить комментарий относительно проблем с этим «решением»:

Есть более простые проблемы, чем то, с чем вы связались.

INSERT INTO YourTable(FkCol,BitCol) VALUES (1,1),(1,0) 

с последующим

UPDATE YourTable SET BitCol=1 

преуспевает и оставляет два ряда с FkCol = 1 и BitCol = 1

+1

Есть более простые проблемы, чем то, с чем вы связались. 'INSERT INTO YourTable (FkCol, BitCol) VALUES (1,1), (1,0)', за которым следует 'UPDATE YourTable SET BitCol = 1', и оставляет две строки с' FkCol' = 1 и 'BitCol' = 1 –

2
CREATE UNIQUE INDEX ON UnnamedTable (OrderID) WHERE UnnamedBitField=1 

Это называется Filtered Index. Если вы на предварительно 2008 версии SQL Server, вы можете реализовать бедный-Ман эквивалент фильтрованного индекса с помощью индексированного представления:

CREATE VIEW UnnamedView 
WITH SCHEMABINDING 
AS 
    SELECT OrderID From UnnamedSchema.UnnamedTable WHERE UnnamedBitField=1 
GO 
CREATE UNIQUE CLUSTERED INDEX ON UnnamedView (OrderID) 

Вы не можете действительно сделать это как ограничение, так как SQL Server поддерживает только ограничения столбцов и ограничения строк. Существует нет (не-fudging) способ написать ограничение, которое имеет дело со всеми значениями в таблице.

+0

Я использую SQL Server 2000 (грустный знаю), поэтому я использовал представление Indexed, и это сработало для меня. Единственное различие заключалось в том, что мне пришлось использовать «СОЗДАТЬ УНИКАЛЬНЫЙ КЛАСТЕРНЫЙ ИНДЕКС» вместо «СОЗДАТЬ УНИКАЛЬНЫЙ ИНДЕКС». Благодаря! – user2263882

+0

@ user2263882 - Я обновил образец (я признаю, что мне не приходилось много лет прилагать усилия и работать с памятью). –

Смежные вопросы