Проверка уникального значения sql с ограничением - PullRequest
6 голосов
/ 21 октября 2011

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

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

Спасибо

Ответы [ 7 ]

4 голосов
/ 21 октября 2011

Вы можете создать индексированное представление, которое будет поддерживать ваше ограничение сохранения 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 вместо этого можно использовать уникальный фильтрованный индекс.

1 голос
/ 21 октября 2011

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

1 голос
/ 21 октября 2011

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

1 голос
/ 21 октября 2011

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

РЕДАКТИРОВАТЬ: Вы всегда можете использовать уникальное ограничение для ID и Value.Я думаю, что это даст вам то, что вы ищете.

0 голосов
/ 04 мая 2012
create function dbo.IsValueUnique
(
     @proposedValue varchar(50)
    ,@currentId int
)
RETURNS bit
AS
/*
--EXAMPLE
print dbo.IsValueUnique() -- fail
print dbo.IsValueUnique(null) -- fail
print dbo.IsValueUnique(null,1) -- pass
print dbo.IsValueUnique('Friendly',1) -- pass
*/
BEGIN
    DECLARE @count  bit

    set @count =
    (
        select      count(1)
        from        dbo.MyTable
        where       @proposedValue is not null
        and         dbo.MyTable.MyPkColumn != @currentId
        and         dbo.MyTable.MyColumn = @proposedValue
    )

    RETURN case when @count = 0 then 1 else 0 end
END
GO
ALTER TABLE     MyTable
WITH CHECK
add constraint  CK_ColumnValueIsNullOrUnique
CHECK           ( 1 = dbo.IsValueNullOrUnique([MyColumn],[MyPkColumn]) )
GO
0 голосов
/ 21 октября 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).

0 голосов
/ 21 октября 2011

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

Итак, он разбит на две таблицы: Один с PK = id и FK, ссылающийся на two.sid

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

«Однобитовая переменная» исчезает, потому что она может быть выражена в терминах EXISTS. (фактически таблица 1 указывает на строку, содержащую токен)

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

EDIT / UPDATE:

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