【问题标题】:Checking sql unique value with constraint使用约束检查 sql 唯一值
【发布时间】:2011-10-20 20:13:48
【问题描述】:

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

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

谢谢

【问题讨论】:

标签: sql sql-server-2005 unique unique-constraint check-constraints


【解决方案1】:

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

【讨论】:

  • 这不会阻止基础数据上的额外行。
  • @JeremyHolovacs - 它将允许 [Status] = 0 的重复 ID,但不允许 [Status] = 1 的重复 ID。这就是我阅读问题的方式。还是你有别的想法?
  • 哈哈错过了模式绑定。是的,这会起作用。有点迂回,但很有效。
【解决方案2】:

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

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

【讨论】:

  • 我会很感激有关如何创建此触发器的建议。有没有办法让这个触发器生成一个错误,比如 SqlException 2627 for invalid unique value?
  • 我也很好奇你能不能做到这一点。我曾尝试在 Oracle 中创建一个行级触发器,用于查询同一个表中的其他行,它只会抛出“表正在变异”错误。有人知道 MS SQL 是否有同样的限制吗?
  • 同上...具有可为空列的复合唯一键在 Sql 2005 中不允许特定值和空值的多个组合。
【解决方案3】:

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

【讨论】:

  • 认真的吗?在没有解释的情况下否决这个?这将起作用。
【解决方案4】:

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

【讨论】:

  • +1 用于建议更改设计,而不仅仅是符合 OP 的结构。我也喜欢。
  • 是的,我只知道这一点是因为试图做一次完全相反的事情。我想要一个防止多个空值的检查约束,并且都是“WTH这不起作用吗??”
  • 这在 SQL Server 中效果不佳,尽管它应该(至少就 2005 年而言)...
  • 它应该符合 SQL 标准。我可以确认它确实在 Postgres 中有效。我很抱歉,因为我在 OP 更新他们使用的数据库之前发布了我的答案。
  • 由于状态列允许值 0 和 1,并且我想强制限制每个 ID 只有一个状态=1,添加涉及 sum(status)
【解决方案5】:

恕我直言,这基本上是一个标准化问题。名为“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;

【讨论】:

  • 这个例子不会在 Sql 2005 中运行。
  • 我知道,但是 OP 的 5 个标签中有 4 个不是特定于平台的。此外,根本问题更像是一种数据建模问题,而不是“在我的平台上解决我的问题”。对于数据建模的东西,恕我直言,知道不同的解决方案可能存在于不同的实现中总是好的。
【解决方案6】:

我想出了一个解决方案

首先创建一个函数

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

【讨论】:

  • 这将是有效的,但非常低效。对于大数据操作,这会破坏服务器的性能。
  • 请解释为什么这是一个消耗资源的操作?
  • 用户定义的函数对于为基于集合的操作而设计的系统是出了名的慢。 UDF 可用于此目的,但针对此表的更大的基于集合的操作将非常占用资源。 Mikael 的解决方案或使用触发器会更有效。
【解决方案7】:
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

【讨论】:

    猜你喜欢
    • 2020-04-10
    • 1970-01-01
    • 1970-01-01
    • 2011-06-10
    • 2020-12-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多