tsql - 根据另一个表中的信息检查约束

时间:2017-01-07 16:05:16

标签: sql-server tsql check-constraints

给定两个表TableA(id:主键,类型:tinyint,...)和TableB(id:主键,tableAId:TableA.id上的外键,...)

TableA.type上有一个CHECK CONSTRAINT,允许值为(0,1,2,3),其他值都被禁止。

由于已知的限制,TableB中的记录只有在TableB.TableAId引用TableA中的记录时才能存在TableA.type = 0,1或2而不是3.后一种情况被禁止并导致系统无效状态。

我怎样才能保证在这种情况下INSERT到TableB会失败?

1 个答案:

答案 0 :(得分:4)

使用空索引视图的跨表约束:

表格

CREATE TABLE dbo.TableA 
(
    id integer NOT NULL PRIMARY KEY, 
    [type] tinyint NOT NULL 
        CHECK ([type] IN (0, 1, 2, 3))
);

CREATE TABLE dbo.TableB 
(
    id integer NOT NULL PRIMARY KEY, 
    tableAId integer NOT NULL 
        FOREIGN KEY
        REFERENCES dbo.TableA
);

“约束视图”

-- This view is always empty (limited to error rows)
CREATE VIEW dbo.TableATableBConstraint
WITH SCHEMABINDING AS
SELECT
    Error = 
        CASE 
            -- Error condition: type = 3 and rows join
            WHEN TA.[type] = 3 AND TB.id = TA.id
            -- For a more informative error
            THEN CONVERT(bit, 'TableB cannot reference type 3 rows in TableA.')
            ELSE NULL
        END
FROM dbo.TableA AS TA
JOIN dbo.TableB AS TB
    ON TB.id = TA.id
WHERE
    TA.[type] = 3;
GO
CREATE UNIQUE CLUSTERED INDEX cuq 
ON dbo.TableATableBConstraint (Error);

Online demo:

-- All succeed
INSERT dbo.TableA (id, [type]) VALUES (1, 1);
INSERT dbo.TableA (id, [type]) VALUES (2, 2);
INSERT dbo.TableA (id, [type]) VALUES (3, 3);

INSERT dbo.TableB 
    (id, tableAId) 
VALUES
    (1, 1),
    (2, 2);

-- Fails
INSERT dbo.TableB (id, tableAId) VALUES (3, 3);

-- Fails
UPDATE dbo.TableA SET [type] = 3 WHERE id = 1;

这在概念上与 linked answerCheck constraints that ensures the values in a column of tableA is less the values in a column of tableB 类似,但此解决方案是独立的(不需要始终包含多行的单独表)。它还会产生更多信息性错误消息,例如:

<块引用>

消息 245,级别 16,状态 1
转换 varchar 值“TableB 无法引用 TableA 中的类型 3 行”时转换失败。到数据类型位。

重要说明

错误条件必须在 CASE 表达式中完全指定,以确保在所有情况下都能正确操作。不要试图省略语句其余部分所隐含的条件。在此示例中,省略 TB.id = TA.id(由连接暗示)将是错误的。

SQL Server 查询优化器可以自由地重新排序谓词,并且不对标量表达式的计算时间或计算次数做出一般保证。特别是,scalar computations can be deferred

完全CASE 表达式中指定错误条件可确保一起评估完整的测试集,并且不会早于正确性要求。从执行计划的角度来看,这意味着与 CASE 测试关联的计算标量将出现在索引视图增量维护分支上:

execution plan

浅色阴影区域突出显示索引视图维护区域;包含 CASE 表达式的计算标量是深色阴影。