一组记录中的唯一约束,其中某些值相同

Ada*_*son 8 sql database sql-server sql-server-2005 constraints

DBMS: MS Sql Server 2005,标准版

我想创建一个表约束,使得只有一个记录在表的子集中具有特定值(其中行在特定列中共享一个值).这可能吗?

示例: 我在myTable中有一个非唯一外键(fk1)的记录,以及一个名为isPrimary的位列,用于标记我们的应用程序应该使用这个特殊逻辑.

在摘要中,它看起来像这样:

myTable
-------------
pk1       (int, not null)
name      (varchar(50), null)
fk1       (int, not null)
isPrimary (bit, not null)
Run Code Online (Sandbox Code Playgroud)

对于fk1的每个唯一值,我想确保将isPrimary标志设置为1的唯一一条记录.

数据示例:这应该是合法的:

pk1     name     fk1    isPrimary
----    -----    -----  ----------
1       Bill     111    1
2       Tom      111    0
3       Dick     222    1
4       Harry    222    0
Run Code Online (Sandbox Code Playgroud)

但这应该是(超过一个fk = 111):

pk1     name     fk1    isPrimary
----    -----    -----  ----------
1       Bill     111    1
2       Tom      111    1
3       Dick     222    1
4       Harry    222    0
Run Code Online (Sandbox Code Playgroud)

这也不应该(没有fk = 222):

pk1     name     fk1    isPrimary
----    -----    -----  ----------
1       Bill     111    1
2       Tom      111    0
3       Dick     222    0
4       Harry    222    0
Run Code Online (Sandbox Code Playgroud)

有没有办法用表约束来做到这一点?

更新 我现在已经接受了Martin Smith的回答,尽管我将在即将发布的版本中推动JohnFx的重构,因为它是最好的长期解决方案.但是我想根据Raze2dust的答案发布我更新的UDF,以防未来读者认为更适合他们的需求.

CREATE FUNCTION [dbo].[OneIsPrimaryPerFK1](@fk1 INT, @dummyIsPrimary BIT)
RETURNS INT
AS 
BEGIN
    DECLARE @retval INT;
    DECLARE @primarySum INT;
    SET @retval = 0;
    DECLARE @TempTable TABLE (
    fk1 INT,
    PrimarySum INT)

INSERT INTO @TempTable
    SELECT fk1, SUM(CAST(isPrimary AS INT)) AS PrimarySum
    FROM FacAdmin
    WHERE fk1 = @fk1
    GROUP BY fk1;

    SELECT @primarySum = PrimarySum FROM @TempTable;
    IF(@primarySum=1)
        BEGIN
            SET @retval = 1
        END
    RETURN @retval
END;
Run Code Online (Sandbox Code Playgroud)

变化:

  1. 使用@tempTable而不是

    根据udf的要求,tempTable(在内存中,写入磁盘)

  2. 传递@ fk1作为参数,因此我可以在一组fk1值中选择唯一性.
  3. 棘手不得不也传递值isPrimary即使它是没有必要的功能的逻辑,否则当值isPrimary更新的SQL2005优化器将无法运行检查约束.

Joh*_*nFx 9

自从我严重破坏第一个问题以来,开始了一个新的答案.

听起来你可以通过重新思考一下你的表设计来解决这个问题,以避免让你暴力成为实施业务规则的约束.

如何从MyTable中删除IsPrimary列并将PrimaryPersonID列添加到引用主要人员的另一个表中?

这样,结构本身就会强制执行1,并且FK表中只有1个条目是每个人的主要条目.

  • 你完全正确,这应该是数据模型,我希望我们能够实现这一点.目前,我们有几个不同的应用程序指向此,我们无法更改它们以立即访问更新的模型.我将在未来努力做出这一改变. (2认同)

Tho*_*mas 6

SQL 2005不提供将Where子句应用于SQL 2008等唯一索引的功能.但是,有几种方法可以解决SQL 2005中的问题:

  1. 创建一个过滤IsPrimary = 1的索引视图,并为该视图添加唯一索引.
  2. 创建一个触发器,确保只有一个可以是主要的.
  3. 将您的逻辑封装到存储过程中并强制用户通过存储过程从您的表中插入或更新.

  • 4.创建在检查约束中使用的存储函数 (2认同)

Mar*_*ith 4

在快照隔离或多行更新下,在检查约束中使用 UDF 可能会失败。

假设所有 fk1 和 pk1 值当前(并将始终)为正值,您可以使用以下定义创建一个计算列

CASE WHEN isPrimary = 1 THEN fk1 ELSE -pk1 END
Run Code Online (Sandbox Code Playgroud)

然后为其添加唯一约束。或者如果这个假设不能成立那么也许

CASE WHEN isPrimary = 0 THEN 1.0/pk1 ELSE fk1 END
Run Code Online (Sandbox Code Playgroud)