Проверка уникального значения sql с ограничением

У меня есть ситуация, когда в таблице есть три столбца ID, значение и статус. Для отдельного идентификатора должен быть только один статус со значением 1, и для идентификатора должно быть разрешено иметь более одного статуса со значением 0. Уникальный ключ не позволит идентификатору иметь более одного статуса (0 или 1).

Есть ли способ решить эту проблему, возможно, используя ограничения?

Спасибо


person mko    schedule 20.10.2011    source источник
comment
Какую СУБД/версию вы используете?   -  person Joe Stefanelli    schedule 21.10.2011
comment
Должна ли быть ровно одна Status 1 строка для каждого идентификатора или не более одной? т.е. разрешено ли иметь идентификатор без строки Status 1?   -  person Andriy M    schedule 22.10.2011


Ответы (6)


Вы можете создать индексированное представление, которое будет поддерживать ваше ограничение на сохранение уникальности ID для [Status] = 1.

create view dbo.v_YourTable with schemabinding as
select ID
from dbo.YourTable
where [Status] = 1

go

create unique clustered index UX_v_UniTest_ID on v_YourTable(ID)

В SQL Server 2008 вместо этого можно было использовать уникальный отфильтрованный индекс.

person Mikael Eriksson    schedule 20.10.2011
comment
Это не предотвратит появление дополнительных строк в базовых данных. - person Jeremy Holovacs; 21.10.2011
comment
@JeremyHolovacs - это позволит дублировать идентификаторы для [Статус] = 0, но не для [Статус] = 1. Вот как я прочитал вопрос. Или вы имеете в виду что-то другое? - person Mikael Eriksson; 21.10.2011
comment
Ха пропустил интригу. Да, это сработает. Немного окольный, но эффективный. - person Jeremy Holovacs; 21.10.2011

Если в таблице могут быть повторяющиеся значения ID, то проверочное ограничение не подойдет для вашей ситуации. Я думаю, что единственный способ - использовать триггер. Если вы ищете пример, я могу опубликовать его. Но в целом используйте триггер, чтобы проверить, имеет ли вставленный/обновленный идентификатор статус 1, который дублируется для одного и того же идентификатора.

EDIT: вы всегда можете использовать уникальное ограничение для ID и Value. Я думаю, это даст вам то, что вы ищете.

person Community    schedule 20.10.2011
comment
Я был бы признателен за предложение о том, как создать этот триггер. Есть ли способ для этого триггера генерировать ошибку, такую ​​​​как SqlException 2627, для недопустимого уникального значения? - person mko; 21.10.2011
comment
Мне тоже интересно, сможете ли вы это сделать. Однажды я попытался создать триггер на уровне строк в Oracle, который запрашивал другие строки в той же таблице, и он просто выдавал ошибку изменения таблицы. Кто-нибудь знает, есть ли у MS SQL такое же ограничение? - person Mike Christensen; 21.10.2011
comment
То же, что и выше... составной уникальный ключ со столбцом, допускающим значение NULL, не позволит использовать более одной комбинации определенного значения и значения NULL в Sql 2005. - person Jeremy Holovacs; 21.10.2011

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

person Jeremy Holovacs    schedule 20.10.2011
comment
Серьезно? Проголосовать за это без объяснения причин? Это сработает. - person Jeremy Holovacs; 21.10.2011

Если вы можете использовать NULL вместо 0 для нулевого статуса, то вы можете использовать ограничение UNIQUE для пары, и это должно работать. Поскольку NULL не является фактическим значением (NULL != NULL), строки с несколькими значениями NULL не должны конфликтовать.

person Mike Christensen    schedule 20.10.2011
comment
+1 за рекомендацию изменить дизайн, а не просто соответствовать структуре ОП. Мне это тоже нравится. - person ; 21.10.2011
comment
Да, я знаю это только потому, что однажды попытался сделать прямо противоположное. Я хотел, чтобы контрольное ограничение предотвращало множественные нули, и все, что WTH, не работает ?? - person Mike Christensen; 21.10.2011
comment
Это не так хорошо работает на SQL-сервере, хотя должно (по крайней мере, в отношении 2005 года)... - person Jeremy Holovacs; 21.10.2011
comment
Это должно соответствовать стандарту SQL. Я могу подтвердить, что это действительно работает в Postgres. Приношу свои извинения, так как я опубликовал свой ответ до того, как ОП обновил используемую БД. - person Mike Christensen; 21.10.2011
comment
поскольку столбец состояния допускает значения 0 и 1, и я хочу применить ограничение только одного состояния = 1 для каждого идентификатора, как насчет добавления ограничения, которое включает сумму (статус) ‹ = 1, сгруппированную по идентификатору? возможно ли это с помощью триггера или ограничения? - person mko; 21.10.2011
comment
Нет, я не верю, что вы можете определить ограничение на сумму значений в группе. Я считаю, что триггер - ваш самый жизнеспособный вариант здесь, извините. Хотя другой вариант - удалить разрешения INSERT/UPDATE для вашей таблицы и выполнять все обновления через sproc, который сначала проверит данные. - person Mike Christensen; 21.10.2011
comment
Я согласен, что это должно работать в соответствии со стандартом sql, но оно по-прежнему не будет работать в Sql 2005. Составной уникальный ключ со столбцом, допускающим значение NULL, позволит ровно одну комбинацию определенного числа и значения null. - person Jeremy Holovacs; 21.10.2011

ИМХО, это в основном проблема нормализации. Столбец с именем «id» не имеет однозначного адреса строки, поэтому он никогда не может быть PK. Необходим как минимум новый (суррогатный) ключ (элемент). Само ограничение не может быть выражено как выражение «внутри строки», поэтому оно должно быть выражено в терминах FK.

Таким образом, он разбивается на две таблицы: одна с PK = id и FK REFERENCING two.sid

Два с суррогатным ключом PK= и идентификатором FK ССЫЛКА на one.id Исходное «значение» полезной нагрузки также находится здесь.

«Однобитовая переменная» исчезает, потому что ее можно выразить в терминах СУЩЕСТВУЕТ. (фактически таблица one указывает на строку, содержащую токен)

[Я ожидаю, что система правил Postgres может использоваться для использования вышеупомянутой модели с двумя таблицами для эмуляции предполагаемого поведения OP. Но это был бы уродливый взлом...]

РЕДАКТИРОВАТЬ/ОБНОВИТЬ:

Postgres поддерживает частичные/условные индексы. (не знаю о ms-sql)

DROP TABLE tmp.one;
CREATE TABLE tmp.one
    ( sid INTEGER NOT NULL PRIMARY KEY -- surrogate key
    , id INTEGER NOT NULL
    , status INTEGER NOT NULL DEFAULT '0'
    /* ... payload */
    );
INSERT INTO tmp.one(sid,id,status) VALUES
  (1,1,0) , (2,1,1) , (3,1,0)
, (4,2,0) , (5,2,0) , (6,2,1)
, (7,3,0) , (8,3,0) , (9,3,1)
  ;

CREATE UNIQUE INDEX only_one_non_zero ON tmp.one (id)
    WHERE status > 0 -- "partial index" 
    ;

\echo this should succeed
BEGIN ;
UPDATE tmp.one SET status = 0 WHERE sid=2;
UPDATE tmp.one SET status = 1 WHERE sid=1;
COMMIT;

\echo this should fail
BEGIN ;
UPDATE tmp.one SET status = 1 WHERE sid=4;
UPDATE tmp.one SET status = 0 WHERE sid=9;
COMMIT;

SELECT * FROM tmp.one ORDER BY sid;
person wildplasser    schedule 20.10.2011
comment
Этот пример не будет работать в Sql 2005. - person Jeremy Holovacs; 21.10.2011
comment
Я знаю, но 4 из 5 тегов OP не зависят от платформы. Более того, основная проблема больше связана с моделированием данных, чем с решением моей проблемы на моей платформе. Для моделирования данных ИМХО всегда полезно знать, что в разных реализациях могут существовать разные решения. - person wildplasser; 22.10.2011

я придумал решение

Сначала создайте функцию

CREATE FUNCTION [dbo].[Check_Status] (@ID int)
RETURNS INT
AS
BEGIN
 DECLARE @r INT;
 SET @r =
  (SELECT SUM(status) FROM dbo.table where ID= @ID);
 RETURN @r;
END

Второе создание ограничения в таблице

([dbo].[Check_Status]([ID])<(2))

Таким образом, один идентификатор может иметь один статус (1) и как можно больше статусов (0).

person mko    schedule 21.10.2011
comment
Это будет эффективно, но ОЧЕНЬ неэффективно. Для операций с большими данными это снизит производительность вашего сервера. - person Jeremy Holovacs; 22.10.2011
comment
Пожалуйста, объясните, почему это ресурсоемкая операция? - person mko; 23.10.2011
comment
пользовательские функции, как известно, медленны для системы, предназначенной для операций на основе наборов. Для этой цели можно использовать пользовательские функции, но более крупные операции над наборами для этой таблицы будут очень ресурсоемкими. Решение Микаэля или использование триггера будет намного эффективнее. - person Jeremy Holovacs; 24.10.2011