【问题标题】:Enforce additional constraints on N:M table server在 N:M 表服务器上实施附加约束
【发布时间】:2021-06-03 05:38:50
【问题描述】:

我在 SQL Server 2019 中有以下表格,其中每个封装可以有多个模板,每个模板可以有多个封装。

CREATE TABLE dbo.Footprint 
( 
    FootprintId INT IDENTITY(1,1) NOT NULL 
        CONSTRAINT PK_Footprint PRIMARY KEY CLUSTERED, 
    FootprintName NVARCHAR(255) NOT NULL 
        CONSTRAINT U_FootprintName UNIQUE
);

CREATE TABLE dbo.Stencil 
( 
    StencilId INT IDENTITY(1,1) NOT NULL 
        CONSTRAINT PK_Stencil PRIMARY KEY CLUSTERED, 
    StencilName NVARCHAR(255) NOT NULL 
        CONSTRAINT U_Stencil UNIQUE (StencilName),
    UseLocation NVARCHAR(255) NOT NULL,
    PartNumber NVARCHAR(255) NULL
);

CREATE TABLE dbo.FootprintStencil 
(
    FootprintId INT NOT NULL,
    StencilId INT NOT NULL, 
    CONSTRAINT PK_FootprintStencil 
        PRIMARY KEY CLUSTERED (FootprintId, StencilId),
    CONSTRAINT FK_FootprintStencil_Footprint 
        FOREIGN KEY (FootprintId) REFERENCES dbo.Footprint (FootprintId),
    CONSTRAINT FK_FootprintStencil_Stencil 
        FOREIGN KEY (StencilId) REFERENCES dbo.Stencil (StencilId)
);

有没有办法在数据库中强制执行约束 (Footprint.FootprintId, Stencil.UseLocation, Stencil.PartNumber)FootprintStencil 关系中必须是唯一的?

我也许可以用触发器来做到这一点,但有更好的方法吗?

【问题讨论】:

    标签: sql-server tsql


    【解决方案1】:

    您可以使用索引视图强制执行此操作。此技巧类似于this post,但在这种情况下,我们只想强制执行唯一性:

    CREATE OR ALTER VIEW dbo.vwFootprint_StencilLocationPart
    WITH SCHEMABINDING
    AS
    SELECT fs.FootprintId, s.UseLocation, s.PartNumber
    FROM dbo.FootprintStencil
    JOIN dbo.Stencil s ON s.StencilId = fs.StencilId;
    
    GO
    
    CREATE UNIQUE CLUSTERED INDEX IX_vwFootprint_StencilLocationPart
        ON dbo.vwFootprint_StencilLocationPart (FootprintId, UseLocation, PartNumber);
    
    GO
    

    您现在有一个多表约束。只要基础表发生更改,服务器就会自动维护它,并抛出任何唯一性违规。

    注意:索引视图有限制,其中:

    • 视图必须是模式绑定的,并防止对基础列进行更改
    • 仅限两部分名称,
    • 值必须是确定性的
    • 只允许使用 INNER JOINCROSS JOIN,不允许使用 LEFT/RIGHT/APPLY、子查询或 CTE

    【讨论】:

    • 这么简单,我怎么没想到:)。您认为从联接中删除 dbo.Footprint 会产生什么不同,因为所使用的只是已经是 dbo.FootprintStencil 一部分的 FootprintId?
    • 嗯,好点,是的,你可以。我必须说,您的设计有些地方不太对劲,我无法完全理解。也许你想重新考虑这种关系。也许UseLocation, PartNumber 无论如何都可以是唯一键?
    【解决方案2】:

    我已经通过引用标量函数的检查约束完成了这种事情:

    在 FootPrintStencil 中,我会添加约束:

    ....
    constraint [CheckExtendedRequirements] 
      check ( dbo.ExtendedRequirements( FootprintId, StencilId ) = 1 )
    ...
    

    dbo.ExtendedRequirements 类似于:

    create function dbo.ExtendedRequirements( @footprintId int, @stencilId int) 
    returns bit as
    begin
      declare 
        @useLocation nvarchar(255), 
        @partNumber nvarchar( 255 )
    
      select 
        @useLocation = UseLocation, 
        @partNumber = PartNumber
      from
        dbo.Stencil
      where
        StencilId = @stencilId
    
      return
      (
        select case when count(*) > 1 then 0 else 1 
        from
          dbo.Footprint f 
          cross join 
          dbo.Stencil s
        where 
          f.FootprintId !=  @footprintId and 
          s.StencilId != @stencilId and
          s.UseLocation = @useLocation and
          s.PartNumber = @partNumber
      )
    end
    

    意识流和未经测试...但类似的东西。

    【讨论】:

    • 感谢您的回答,我一定会牢记未来的用例。我发现@Charlieface 解决的唯一缺点是,如果仅在最初建立关系时模板已经链接到足迹后更新,则不会强制执行约束。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-14
    • 1970-01-01
    • 2021-06-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多