使用约束检查 sql 唯一值

发布于 2024-12-11 05:43:10 字数 147 浏览 0 评论 0原文

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

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

谢谢

I have a situation where a table has three columns ID, Value and status. For a distinct ID there should be only one status with value 1 and it should be allowed for ID to have more then one status with value 0. Unique key would prevent ID of having more then one status (0 or 1).

Is there a way to solve this, maybe using constraints?

Thanks

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

青芜 2024-12-18 05:43:10

您可以创建一个索引视图,以保持 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 中,您可以使用唯一的筛选索引。

You can create an indexed view that will uphold your constraint of keeping ID unique for [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)

In SQL Server 2008 you could use a unique filtered index instead.

屌丝范 2024-12-18 05:43:10

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

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

If the table can have duplicate ID values, then a check constraint wouldn't work for your situation. I think the only way would be to use a trigger. If you are looking for an example then I can post one. But in summary, use a trigger to test if the inserted/updated ID has a status of 1 that is duplicated across the same ID.

EDIT: You could always use a unique constraint on ID and Value. I'm thinking that will give you what you are looking for.

风筝有风,海豚有海 2024-12-18 05:43:10

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

You could put this into an insert/ update trigger to check to make sure only one combination exists with the 1 value; if your condition is not met, you could throw a trappable error and force the operation to roll back.

初见你 2024-12-18 05:43:10

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

If you can use NULL instead of 0 for a zero-status, then you can use a UNIQUE constraint on the pair and it should work. Since NULL is not an actual value (NULL != NULL), then rows with multiple nulls should not conflict.

街角迷惘 2024-12-18 05:43:10

恕我直言,这基本上是一个标准化问题。名为“id”的列并不唯一地寻址一行,因此它永远不可能是 PK。至少需要一个新的(代理)键(元素)。约束本身不能表示为“行内”的表达式,因此必须用 FK 来表示。

所以它分为两个表:
一个 PK=id,FK REFERENCING Two.sid

两个 PK= 代理键,FK id REFERENCING one.id
原始有效负载“值”也存在于此。

“一位变量”消失了,因为它可以用 EXISTS 来表示。 (表一有效地指向保存令牌的行)

[我希望 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;

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;
心意如水 2024-12-18 05:43:10

我想出了一个解决方案

首先创建一个函数

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

I came up with a solution

First create a function

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

Second create a constraint in table

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

In this way one ID could have single status (1) and as many as possible status (0).

诗笺 2024-12-18 05:43:10
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
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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文