【问题标题】:Constraint with multiple tables. UDF?约束多个表。 UDF?
【发布时间】:2015-06-02 13:29:13
【问题描述】:

我有两个表:表 A 和表 B。这两个表通过 A 中的主键和 B 中的外键链接。

表 A:

CREATE TABLE [BIO].[table_A](
    [table_A_id] [int] IDENTITY(1,1) NOT NULL,  
    [type_id] [nvarchar](2) NOT NULL    
CONSTRAINT [PK_table_A] PRIMARY KEY CLUSTERED 
(
    [table_A_id] ASC
))

表 B:

CREATE TABLE [BIO].[table_B](
    [table_B_id] [int] IDENTITY(1,1) NOT NULL,
    [table_A_id] [int] NOT NULL,
    [analysis_id] [tinyint] NOT NULL
CONSTRAINT [PK_table_B] PRIMARY KEY CLUSTERED 
(
    [table_B_id] ASC
))

ALTER TABLE [BIO].[table_B] WITH CHECK 
ADD CONSTRAINT [FK_table_B_table_A] FOREIGN KEY([table_A_id])
REFERENCES [BIO].[table_A] ([table_A_id])
GO

ALTER TABLE [BIO].[table_B] CHECK CONSTRAINT [FK_table_B_table_A]
GO

表 B 必须仅包含根据表 A 中的值的特定值。 比如我A表有BL,B表只能有1或3;如果我在 A 表中有 ST,我在 B 表中只能有 2 或 4 个。

我已经设置了一个桥接表,它定义了这些组合:BL→1 或 3,ST→2 或 4。

桥牌桌:

CREATE TABLE [QRY].[bridge_table](
    [type_id] [nvarchar](2) NOT NULL,
    [analysis_id] [tinyint] NOT NULL,
CONSTRAINT [PK_bridge_table] PRIMARY KEY CLUSTERED 
(
    [type_id] ASC,
    [analysis_id] ASC
))

我目前对每个表使用一个约束,以确保根据桥表中定义的组合,任何插入或更新都是正确的。这两个约束基于 UDF。

对表 B 的约束:

ALTER TABLE [BIO].[Table_B] WITH CHECK 
ADD CONSTRAINT [CK_chkAnalysisType] 
CHECK (([QRY].[TypeAnalysisMatch_table_B]([table_A_id])>(0)))
GO

UDF:

CREATE FUNCTION [QRY].[TypeAnalysisMatch_table_B] (@table_A_id int)
RETURNS int
AS
BEGIN
    RETURN
    (
    SELECT
        Count(BIO.table_A.table_A_id) AS cnt_rec
    FROM
        QRY.bridge_table
        INNER JOIN BIO.table_A ON QRY.bridge_table.type_id = BIO.table_A.type_id
        INNER JOIN BIO.table_B ON 
                QRY.bridge_table.analysis_id = BIO.table_B.analysis_id
            AND BIO.table_A.table_A_id = BIO.table_B.table_A_id
    WHERE
        BIO.table_A.table_A_id = @table_A_id
    )
END

它适用于INSERT,但不适用于UPDATE。此外,当我读到应避免使用约束中的 UDF 时,我正在寻找更好的解决方案。

什么是这些约束的有效替代方案?

【问题讨论】:

  • 请编辑您的问题以包含一些示例数据的 ddl+dml。另外,包括您当前使用的 UDF。
  • @ZoharPeled:使用数据定义编辑的问题。谢谢

标签: sql-server sql-server-2008-r2 constraints user-defined-functions


【解决方案1】:

你是对的,在 CHECK 约束中使用 UDF 可能会很棘手,一些 UPDATE 语句可能会绕过检查:

http://sqlblog.com/blogs/tibor_karaszi/archive/2009/12/17/be-careful-with-constraints-calling-udfs.aspx

MSSQL: Update statement avoiding the CHECK constraint

正如您在那个 SO 问题中看到的那样,答案建议使用触发器进行检查。编写一个正确有效的触发器也不是一件容易的事。


我假设您的bridge_table 包含以下数据:

type_id    analysis_id
BL         1
BL         3
ST         2
ST         4

我将只使用外键设置这些约束,而不使用 UDF。不过,这将需要一些(最少的)数据重复。我假设真正的table_Atable_B 比这个例子有更多的列。

1.table_A 中,在主键中包含type_id

CREATE TABLE [table_A](
    [table_A_id] [int] IDENTITY(1,1) NOT NULL,
    [type_id] [nvarchar](2) NOT NULL,
CONSTRAINT [PK_table_A] PRIMARY KEY CLUSTERED
(
    [table_A_id] ASC,
    [type_id] ASC
))

2.type_id 列添加到table_B。是的,与您在 table_A 中已有的列相同。这就是我上面提到的数据重复:

CREATE TABLE [table_B](
    [table_B_id] [int] IDENTITY(1,1) NOT NULL,
    [table_A_id] [int] NOT NULL,
    [type_id] [nvarchar](2) NOT NULL,
    [analysis_id] [tinyint] NOT NULL
CONSTRAINT [PK_table_B] PRIMARY KEY CLUSTERED 
(
    [table_B_id] ASC
))

3. 在两列(table_A_id, type_id) 上创建将table_Btable_A 链接的外键:

ALTER TABLE [table_B] WITH CHECK 
ADD CONSTRAINT [FK_table_B_table_A] FOREIGN KEY([table_A_id], [type_id])
REFERENCES [table_A] ([table_A_id], [type_id])

此约束保证在table_B 中重复的type_id 值将与来自table_A 的原始值一致。

4. 在两列(type_id, analysis_id) 上再次创建将table_Bbridge_table 链接的外键:

ALTER TABLE [table_B] WITH CHECK 
ADD CONSTRAINT [FK_table_B_bridge_table] FOREIGN KEY([type_id], [analysis_id])
REFERENCES [bridge_table] ([type_id], [analysis_id])

5.现在您可以测试一切是否按预期工作。

table_A添加几行:

INSERT INTO [table_A] ([type_id]) 
VALUES ('BL'), ('BL'), ('ST'), ('ZZ');

table_A_id    type_id
1             BL
2             BL
3             ST
4             ZZ

尝试将有效数据插入table_B

INSERT INTO [dbo].[table_B] ([table_A_id],[type_id],[analysis_id])
VALUES (1,'BL',1)

INSERT INTO [dbo].[table_B] ([table_A_id],[type_id],[analysis_id])
VALUES (1,'BL',3)

INSERT INTO [dbo].[table_B] ([table_A_id],[type_id],[analysis_id])
VALUES (2,'BL',3)

INSERT INTO [dbo].[table_B] ([table_A_id],[type_id],[analysis_id])
VALUES (3,'ST',2)

INSERT INTO [dbo].[table_B] ([table_A_id],[type_id],[analysis_id])
VALUES (3,'ST',2)

table_B_id    table_A_id    type_id    analysis_id
1             1             BL         1
2             1             BL         3
3             2             BL         3
4             3             ST         2
5             3             ST         2

尝试插入无效数据:

INSERT INTO [dbo].[table_B] ([table_A_id],[type_id],[analysis_id])
VALUES (3,'ST',1)

The INSERT statement conflicted with the FOREIGN KEY constraint "FK_table_B_bridge_table". 
The conflict occurred in database "tempdb", table "dbo.bridge_table".
The statement has been terminated.

这意味着ST 不能拥有analysis_id=1

INSERT INTO [dbo].[table_B] ([table_A_id],[type_id],[analysis_id])
VALUES (3,'BL',1)

The INSERT statement conflicted with the FOREIGN KEY constraint "FK_table_B_table_A". 
The conflict occurred in database "tempdb", table "dbo.table_A".
The statement has been terminated.

这意味着table_A 中带有table_A_id=3 的行在type_id 中没有BL

外键也会不断检查所有UPDATE 语句的数据一致性:

UPDATE [dbo].[table_B]
SET [type_id] = 'ST'
WHERE [table_B_id] = 1

The UPDATE statement conflicted with the FOREIGN KEY constraint "FK_table_B_table_A". 
The conflict occurred in database "tempdb", table "dbo.table_A".
The statement has been terminated.

UPDATE [dbo].[table_B]
SET [analysis_id] = 2
WHERE [table_B_id] = 1

The UPDATE statement conflicted with the FOREIGN KEY constraint "FK_table_B_bridge_table". 
The conflict occurred in database "tempdb", table "dbo.bridge_table".
The statement has been terminated.

但这一个有效:

UPDATE [dbo].[table_B]
SET [analysis_id] = 3
WHERE [table_B_id] = 1

(1 row(s) affected)

【讨论】:

  • 对于 (3),我可能会推荐“ON UPDATE CASCADE”,以便表 A 中的更新“正常工作”。我可能还建议重命名表 B 并创建一个名为 B 的视图来查询没有 type_id 列的旧表。最后,该视图上的INSERT 触发器将确保插入正确的值。应用程序继续使用AB,它们看起来和以前一样,所以重复是“隐藏的”。
  • @Vladimir Baranov 谢谢,这正是我想要的。在我的情况下,数据重复不是问题,尤其是对于 nvarachr(2)。在第 1 步中,我不得不使用唯一索引而不是多列主键来调整您的代码,但效果很好。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-08-22
  • 2011-09-10
  • 2017-02-14
  • 2018-08-04
  • 2012-05-07
  • 2020-10-12
相关资源
最近更新 更多