Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/72.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql 某个值相同的记录组中的唯一约束_Sql_Sql Server_Database_Sql Server 2005_Constraints - Fatal编程技术网

Sql 某个值相同的记录组中的唯一约束

Sql 某个值相同的记录组中的唯一约束,sql,sql-server,database,sql-server-2005,constraints,Sql,Sql Server,Database,Sql Server 2005,Constraints,DBMS:MS Sql Server 2005,标准版 我想做一个表约束,使只有一条记录在表的子集中具有特定值(其中行在特定列中共享一个值)。这可能吗 示例: 我在myTable中有一个记录,它有一个非唯一外键(fk1),还有一个名为isPrimary的位列,用来指出我们的应用程序应该使用这个特殊的记录来实现特殊的逻辑 抽象地说,它是这样的: myTable ------------- pk1 (int, not null) name (varchar(50), null)

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)
我想确保对于fk1的每个唯一值,都有一条且只有一条iPrimary标志设置为1的记录

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

pk1     name     fk1    isPrimary
----    -----    -----  ----------
1       Bill     111    1
2       Tom      111    0
3       Dick     222    1
4       Harry    222    0
但这应该而不是(fk=111时不止一个):

这也不应该(fk=222时无):

有没有一种方法可以通过表约束实现这一点

更新 我现在同意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;
变化:

  • 使用“诱惑”而不是“诱惑” udf要求的可诱惑(内存中或写入磁盘中)
  • 已将@fk1作为参数传递,因此我可以在一个参数中选择唯一性 fk1值组
  • 棘手的是,即使不是,也必须通过iPrimary 逻辑的必要性 函数,否则为SQL2005 优化器不会运行检查 isPrimary为时的约束 更新

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

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

  • 您可以尝试创建函数,然后使用检查约束:

    CREATE FUNCTION ChkFn()
    RETURNS INT
    AS 
    BEGIN
       DECLARE @retval INT
       DECLARE @distinct INT
       DECLARE @top INT
       SET @retval = 0
       SELECT fk1 AS ForeignKey, SUM(isPrimary) AS PrimarySum
        INTO #TempTable 
        FROM myTable
        GROUP BY fk1
       SELECT @distinct = COUNT(DISTINCT(PrimarySum)) FROM #TempTable
       SELECT @top = top PrimarySum FROM #TempTable
       IF(@distinct=1 AND @top=1)
        BEGIN
        @retval = 1
        END
       RETURN @retval
    END;
    GO
    
    ALTER TABLE myTable
    ADD CONSTRAINT chkFkPk CHECK (dbo.ChekFn() = 1 );
    GO
    

    试试看,让我知道它是否有效。虽然不是很优雅。

    开始了一个新的答案,因为我把第一个答案弄坏了

    听起来,您可以通过稍微重新考虑一下表设计来解决这个问题,以避免在实现业务规则时强行施加约束

    从MyTable中删除IsPrimary列,然后将PrimaryPersonID列添加到引用primary person的另一个表中,怎么样


    这样,结构本身将强制每个人在FK表中只有一个条目是主条目

    在检查约束中使用UDF可能在或下失败

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

    CASE WHEN isPrimary = 1 THEN fk1 ELSE -pk1 END
    
    然后添加一个唯一的约束。或者如果这个假设不能成立,那么也许

    CASE WHEN isPrimary = 0 THEN 1.0/pk1 ELSE fk1 END
    

    4.创建一个在检查约束中使用的存储函数警告:约束中的udf可以工作,但如果它执行SELECT操作,也会导致严重的性能问题。性能损失只会发生在插入和更新时,对吗?我无法想象在选择数据时会触发检查约束。如果这是真的,我没问题,因为表不是经常写入的。你完全正确,这应该是数据模型,我希望我们可以实现它。目前,我们有一些不同的应用程序指向这一点,我们现在无法将它们全部更改以访问更新的模型。我将在未来推动这一改变。这非常接近,让我朝着正确的方向前进,所以我将把它标记为接受。我将在周一发布完成的函数。第一个问题是你不能在UDF中使用#tentrables,你必须使用@tentrable。另一个我会在更新后的帖子中提到。啊,是的,我忘记了UDF。。。不知道多行更新/快照隔离。谢谢你的文章。。我会试着从中理解。我是一个新手:-)虽然JohnFx的解决方案是最好的长期解决方案,但这是我现在的解决方案。
    CASE WHEN isPrimary = 1 THEN fk1 ELSE -pk1 END
    
    CASE WHEN isPrimary = 0 THEN 1.0/pk1 ELSE fk1 END