6

我有一个表有三列 ID、Value 和 status 的情况。对于不同的 ID,应该只有一个值为 1 的状态,并且应该允许 ID 具有多个值为 0 的状态。唯一键将阻止 ID 具有多个状态(0 或 1)。

有没有办法解决这个问题,也许使用约束?

谢谢

4

7 回答 7

4

您可以创建一个索引视图,以维护您对保持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 中,您可以改用唯一过滤索引。

于 2011-10-20T21:54:24.550 回答
1

如果表可以有重复的ID值,那么检查约束不适用于您的情况。我认为唯一的方法是使用触发器。如果您正在寻找示例,那么我可以发布一个。但总而言之,使用触发器来测试插入/更新的 ID 是否具有在同一 ID 中重复的状态 1。

ID编辑:您始终可以在and上使用唯一约束Value。我想这会给你你正在寻找的东西。

于 2011-10-20T20:17:04.057 回答
1

您可以将其放入插入/更新触发器中以检查以确保仅存在一个具有 1 值的组合;如果您的条件不满足,您可能会抛出一个可捕获的错误并强制操作回滚。

于 2011-10-20T20:21:29.950 回答
1

如果您可以使用 NULL 而不是 0 来表示零状态,那么您可以在该对上使用 UNIQUE 约束,它应该可以工作。由于 NULL 不是实际值 (NULL != NULL),因此具有多个空值的行不应发生冲突。

于 2011-10-20T20:24:10.880 回答
0

IMHO, this basically is a normalisation problem. The column named "id" does not uniquely address a row, so it can never be a PK. At least a new (surrogate) key(element) is needed. The constraint itself cannot be expressed as an expression "within the row", so it has to be expressed in terms of a FK.

So it breaks down into two tables: One with PK=id, and a FK REFERENCING two.sid

Two with PK= surrogate key, and FK id REFERENCING one.id The original payload "value" also lives here.

The "one bit variable" disappears, because it can be expressed in terms of EXISTS. (effectively table one points to the row that holds the token)

[I expect the Postgres rule system could be used to use the above two-tables-model to emulate the intended behaviour of the OP. But that would be an ugly hack...]

EDIT/UPDATE:

Postgres supports partial/conditional indices. (don't know about 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;
于 2011-10-20T21:28:44.663 回答
0

我想出了一个解决方案

首先创建一个函数

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

这样,一个 ID 可以具有单一状态 (1) 和尽可能多的状态 (0)。

于 2011-10-21T16:44:20.483 回答
0
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
于 2012-05-03T20:07:12.900 回答