是否有DBMS允许外键引用视图(而不仅仅是基表)?


22

受到Django建模问题的启发:Django中具有多个多对多关系的数据库建模。数据库设计类似于:

CREATE TABLE Book
( BookID INT NOT NULL
, BookTitle VARCHAR(200) NOT NULL
, PRIMARY KEY (BookID)
) ;

CREATE TABLE Tag
( TagID INT NOT NULL
, TagName VARCHAR(50) NOT NULL
, PRIMARY KEY (TagID)
) ;

CREATE TABLE BookTag
( BookID INT NOT NULL
, TagID INT NOT NULL
, PRIMARY KEY (BookID, TagID)
, FOREIGN KEY (BookID)  REFERENCES Book (BookID)
, FOREIGN KEY (TagID)   REFERENCES Tag (TagID)
) ;

CREATE TABLE Aspect
( AspectID INT NOT NULL
, AspectName VARCHAR(50) NOT NULL
, PRIMARY KEY (AspectID)
) ;

CREATE TABLE TagAspect
( TagID INT NOT NULL
, AspectID INT NOT NULL
, PRIMARY KEY (TagID, AspectID) 
, FOREIGN KEY (TagID)   REFERENCES Tag (TagID)
, FOREIGN KEY (AspectID)  REFERENCES Aspect (AspectID)
) ;

数据库图

问题是如何定义BookAspectRating表并强制执行参照完整性,因此无法(Book, Aspect)为无效的组合添加评分。

AFAIK,复杂的CHECK约束(或ASSERTIONS)涉及子查询和一个以上的表(可能解决此问题),在任何DBMS中均不可用。

另一个想法是使用(伪代码)视图:

CREATE VIEW BookAspect_view
  AS
SELECT DISTINCT
    bt.BookId
  , ta.AspectId
FROM 
    BookTag AS bt
  JOIN 
    Tag AS t  ON t.TagID = bt.TagID
  JOIN 
    TagAspect AS ta  ON ta.TagID = bt.TagID 
WITH PRIMARY KEY (BookId, AspectId) ;

以及具有上述视图外键的表:

CREATE TABLE BookAspectRating
( BookID INT NOT NULL
, AspectID INT NOT NULL
, PersonID INT NOT NULL
, Rating INT NOT NULL
, PRIMARY KEY (BookID, AspectID, PersonID)
, FOREIGN KEY (PersonID)   REFERENCES Person (PersonID)
, FOREIGN KEY (BookID, AspectID) 
    REFERENCES BookAspect_view (BookID, AspectID)
) ;

三个问题:

  • 是否有DBMS允许(可能实现)VIEW带有PRIMARY KEY

  • 是否有DBMS允许一个FOREIGN KEYREFERENCES一个VIEW(而不仅仅是基础TABLE)?

  • 是否可以使用可用的DBMS功能来解决此完整性问题?


澄清:

由于可能没有100%令人满意的解决方案-Django问题甚至不是我的!-我对可能对问题进行攻击的一般策略而不是详细的解决方案更感兴趣。因此,完全可以接受诸如“在DBMS-X中可以通过表A上的触发器来完成”这样的答案。


将您的前两个问题作为评论发布-不一定对您有用,因为我确信您已经知道了-但SQL Server不支持视图的主键或外键。
阿龙贝特朗

@Aaron:是的,谢谢。我已经读到Oracle在视图中支持PK costraints。但是不确定在这种情况下是否会起作用。在Oracle中,第二个问题(关于视图的FK)的答案可能是否定的。
ypercubeᵀᴹ

但我有兴趣了解是否有任何其他解决方案(触发器,检查costraints或其他组合)
ypercubeᵀᴹ

Answers:


9

可以仅使用约束条件在模型中强制执行此业务规则。下表可以解决您的问题。使用它代替您的视图:

    CREATE TABLE BookAspectCommonTagLink
    (  BookID INT NOT NULL
    , AspectID INT NOT NULL
    , TagID INT NOT NULL
--TagID is deliberately left out of PK
    , PRIMARY KEY (BookID, AspectID)
    , FOREIGN KEY (BookID, TagID) 
        REFERENCES BookTag (BookID, TagID)
    , FOREIGN KEY (AspectID, TagID) 
        REFERENCES AspectTag (AspectID, TagID)
    ) ;

不错哦。我能想到的唯一问题是在插入/删除BookTags和TagAspects中引入的复杂性。例如,每次删除新的BookTag(或TagAspect)时,都必须进行搜索以删除此表中的相应行和/或将其更改TagID为与同一BookAspect组合相关的另一个Tag。
ypercubeᵀᴹ

并且必须执行类似的搜索才能插入到这两个表中。但是复杂的规则需要复杂的过程,因此看起来确实不错。
–ypercubeᵀᴹ2012年

@ypercube删除标签时,您确实需要检查并可能切换到链接同一Book和Aspect的另一个标签。但是,当您插入新标签时,无需进行任何检查,直到您需要插入等级。
AK 2012年

1
如果疑难解答程序和数据输入人员是同一个人,或者您将错误消息公开给最终用户,请确保。您对正在做任何事情的单人商店考虑太多。
阿龙贝特朗

4
@AaronBertrand您刚刚帮了我一个大忙。我正在完成一篇标题为“开发低维护性数据库”的文章,而我忘了提及应用服务器必须记录来自数据库的原始错误消息。我刚刚添加了它。谢谢您的提醒;)
AK

8

我认为您会发现,在很多情况下,不能仅通过模型来强制执行复杂的业务规则。这是其中至少在SQL Server中,我认为触发器(最好是触发器而不是触发器)更好地满足您的目的的情况之一。


嗨,亚伦,您能否解释一下为什么在这种情况下,触发器比实体和一些约束条件更好?
AK 2012年

2
@AlexKuznetsov当然,因为我没有花17个小时思考如何使用多个多列外键来实现这一点,以及处理验证和错误处理所需的所有额外逻辑?
阿龙贝特朗

2
小心天真的触发器可能会引入的比赛条件。例如,一个事务可能会断开一本书与标签的连接,而另一个事务仍然认为可以连接到相应的方面,这仅仅是因为第一个事务尚未提交。@AlexKuznetsov答案引入的复杂性可能小于为防止触发器中的竞争条件恕我直言所必需的锁定“协议”的复杂性和脆弱性。
Branko Dimitrijevic 2014年

8

在Oracle中,以声明方式实施这种约束的一种方法是创建一个物化视图,该视图被设置为在提交时快速刷新,其查询可识别所有无效行(即BookAspectRating中没有匹配的行BookAspect_view)。然后,您可以在该实例化视图上创建一个琐碎的约束,如果该实例化视图中有任何行,则会被违反。这样的好处是可以最大限度地减少您在实例化视图中重复的数据量。但是,这可能会导致问题,因为仅在提交事务时才强制执行约束-编写许多应用程序时并没有期望提交操作可能会失败-并且因为违反约束可能会有些困难与特定的行或特定的表关联。


4

SIRA_PRISE允许。

尽管FK不再被称为“ FK”,而仅被称为“数据库约束”,并且实际上甚至不必将“视图”定义为视图,您也可以在视图的声明中包含视图定义表达式。数据库约束。

您的约束看起来像

SEMIMINUS(BOOKASPECT , JOIN(BOOKTAG , TAGASPECT))

到此为止。

但是,在大多数SQL DBMS中,您必须对约束进行分析工作,确定如何违反约束并实施所有必需的触发器。


我知道。它反映了我在撰写本文时认为重要的内容。
Erwin Smout 2012年

3

在PostgreSQL中,我无法想象不涉及触发器的解决方案,但是肯定可以通过这种方式解决(无论是维护某种物化视图还是在触发器之前 BookAspectRating)。没有引用视图(ERROR: referenced relation "v_munkalap" is not a table)的外键,更不用说主键了。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.