SQL Server 复杂的SQL约束

xnifntxz  于 2022-11-21  发布在  其他
关注(0)|答案(3)|浏览(198)

我 想 防止 使用 约束 写入 数据 , 但 约束 很 复杂 。
我 有 3 列 :A 、 B 、 C
如果 A & B 与 现有 记录 匹配 , 且 A & C 与 现有 记录 匹配 , 则 记录 是 重复 的 , 但 B & C 与 现有 记录 匹配 是 有效 的 , 除非 A = 2 。
例如 , 假设 现有 记录

A=1,B=2,C=3

中 的 每 一 个
如果 我 插入

A=1,B=2,C=4

格式
我 应该 得到 一 个 错误
如果 我 插入

A=1,B=99,C=3

格式
我 应该 得到 一 个 错误
如果 我 插入 :

A=99,B=2,C=3

格式
那 就 成功 了 。
如果 我 插入 :

A=2,B=2,C=3

格式
我 应该 得到 一 个 错误 。
检查 必须 在 INSERT 时 完成 。 我 不能 先 执行 SELECT 然后 再 执行 INSERT , 因为 数据 可能 在 SELECT 和 INSERT 之间 发生 了 更改 。
思路 :

  • 如果 我 把 逻辑 放在 一 个 TRIGGER 中 , 那 会 起 作用 吗 ? 我 不 确定 触发 器 是否 是 100% 原子 的 。
  • TRANSACTION 是否 有效 ? 我 不 想 锁定 表 , 因为 其他 进程 将 不断 插入 数据 。
  • 我 可以 添加 一 个 或 多 个 约束 吗 ? 是否 有 方法 在 INSERT 时 检查 约束 ?
t9eec4r0

t9eec4r01#

对于列A & BA & C的唯一性的简单组合,可以使用唯一索引或唯一约束来解决这个问题。然而,对于后一个问题,这就不那么容易了。
我在这里使用了TRIGGER,并检查是否找到了具有相同值BC的匹配行(其中ID不同,否则行将与其自身匹配),并且其中插入的行具有A的值2,或者现有行具有A的值。如果找到匹配,则错误为THROWn,从而导致INSERT/UPDATE发生故障。

CREATE TABLE dbo.YourTable (ID int IDENTITY,
                            A int NOT NULL,
                            B int NOT NULL,
                            C int NOT NULL);
GO

CREATE UNIQUE INDEX UX_YourTable_AB ON dbo.YourTable (A,B);
GO
CREATE UNIQUE INDEX UX_YourTable_AC ON dbo.YourTable (A,C);
GO

CREATE TRIGGER trg_chk_YourTable_BC_A2 ON dbo.YourTable
AFTER INSERT, UPDATE AS
BEGIN

    IF EXISTS (SELECT 1
               FROM inserted i
                    JOIN dbo.YourTable YT ON i.B = YT.B
                                         AND i.C = YT.C
                                         AND i.ID != YT.ID
                WHERE 2 IN (i.A,YT.A))
        THROW 54697, N'The check trigger ''trg_chk_YourTable_BC_A2'' on table ''dbo.YourTable'' failed. Cannot insert duplicate row on columns ''B'' and ''C'' where at least one row has the value of ''2''.',16;
END;
GO

INSERT INTO dbo.YourTable (A,B,C)
VALUES(1,2,3);
GO

INSERT INTO dbo.YourTable (A,B,C)
VALUES(1,2,4); --Fails
GO

INSERT INTO dbo.YourTable (A,B,C)
VALUES(1,99,3); --Fails
GO
INSERT INTO dbo.YourTable (A,B,C)
VALUES(99,2,3); --Works
GO
INSERT INTO dbo.YourTable (A,B,C)
VALUES(2,2,3); --Fails
GO

INSERT INTO dbo.YourTable (A,B,C)
VALUES(4,5,6); --Works

GO

INSERT INTO dbo.YourTable (A,B,C)
VALUES(5,5,6); --Works
GO

INSERT INTO dbo.YourTable (A,B,C)
VALUES(2,5,6); --Fails
GO

INSERT INTO dbo.YourTable (A,B,C)
VALUES(3,7,9); --Works
GO

INSERT INTO dbo.YourTable (A,B,C)
VALUES(2,7,9); --Fails

GO
SELECT *
FROM dbo.YourTable;

GO
DROP TABLE dbo.YourTable;

显然,您可能希望更改错误号和消息,但我写了一些合理的内容。db<>fiddle

zzoitvuj

zzoitvuj2#

粗略地看,我认为您可以使用唯一筛选索引。
1.唯一:A、B --始终
1.唯一:A、C --始终
1.唯一B、C,其中A==2

5n0oy7gb

5n0oy7gb3#

我会尝试使用公用表表达式并在插入语句中添加where子句。如果插入返回了零个更新的行,则认为插入失败?

-- Assuming A and B do not permit NULLs
; WITH cteAB AS (
    SELECT DISTINCT a,b FROM YourTable
)
, cteBC AS (
    SELECT DISTINCT b,c FROM YourTable
)
INSERT  YourTable ( a, b )
SELECT NewA, NewB
FROM YourTable yt 
LEFT JOIN cteAB ab ON ab.a=NewA AND ab.b=NewB
LEFT JOIN cteBC bc ON bc.a=NewA AND bc.b=NewB
WHERE ( ab.a IS NULL AND bc.a IS NULL )
    OR NewA=2

相关问题