为什么不将主键/外键匹配用于联接?


48

据我所知,许多DBMS(例如mysql,postgres,mssql)仅使用fk和pk组合来约束对数据的更改,但是它们很少用于本地自动选择要连接的列(就像自然连接使用名称一样)。这是为什么?如果您已经用pk / fk定义了2个表之间的关系,为什么数据库无法弄清楚如果我将那些表联接起来,我想在pk / fk列上联接它们?

编辑:澄清一下:

假设我有一个table1和一个table2。table1在a列上有一个外键,该外键引用了table2的b列上的主键。现在,如果我加入这些表,则必须执行以下操作:

SELECT * FROM table1
JOIN table2 ON table1.a = table2.b

但是,我已经使用键定义了table1.a引用table2.b,因此在我看来,让DBMS系统自动使用table1.a和table2.b作为联接列并不难,这样一个人可以简单地使用:

SELECT * FROM table1
AUTO JOIN table2

但是,许多DBMS似乎并未实现这样的功能。

Answers:


32

在许多情况下,联接两个表的方法不止一种。有关许多示例,请参见其他答案。当然,可以说在这些情况下使用“自动连接”将是一个错误。这样一来,只剩下少量可以使用的简单情况。

但是,有一个严重的缺点!今天正确的查询,明天可能会出错,只需在同一张表中添加第二个FK即可!

我再说一遍:通过添加列,不使用这些列的查询可能会从“正确”变为“错误”!

那是一场维护噩梦,任何理智的风格指南都将禁止使用此功能。select *出于相同的原因,大多数已经禁止!

如果可以提高性能,所有这些都是可以接受的。但是,事实并非如此。

总而言之,此功能只能在有限的一组简单情况下使用,不会提高性能,并且大多数样式指南仍然会禁止使用它。

因此,大多数数据库供应商选择将时间花在更重要的事情上并不令人惊讶。


1
实际上,这可能会对性能造成很小的影响,因为它必须找出联接列而不是将它们分散。
HLGEM

1
@HLGEM,可能会缓存它,也与较大的查询无关。优点是我们可以确保不会因人为错误而遗漏键。
Pacerier

添加和更改列也可能会中断NATURAL JOIN(这就是我通常避免使用它们的原因),但我不认为它本身意味着dbms无法实现基于外键的自动联接表的方式。
杰伊

2
很多情况下?在一千个表的数据库中,只有两个表之间的关系大于1的情况很少。无论如何,这不是问题,添加像这样的关系名称就足够了AUTO JOIN mytable THROUGH myrelation,这将非常好。
Teejay

这就是我们使用自定义的.NET SQL构建器执行的,具有intellisense,例如InnerJoin(SRC_TABLE.rDEST_TABLE.REL_NAME_F01)
Teejay

27

外键用于约束数据。即,强制执行参照完整性。而已。没有其他的。

  1. 您可以在同一张表上使用多个外键。考虑以下几点,其中装运具有起点和终点。

    table: USA_States
    StateID
    StateName
    
    table: Shipment
    ShipmentID
    PickupStateID Foreign key
    DeliveryStateID Foreign key
    

    您可能要根据代答状态加入。也许您想加入交货状态。也许您想同时执行两个联接!sql引擎无法知道您想要什么。

  2. 您经常会交叉联接标量值。尽管标量通常是中间计算的结果,但有时您会拥有一个特殊用途的表,该表仅包含1条记录。如果引擎尝试检测联接的外键...。那将是没有意义的,因为交叉联接永远不会匹配列。

  3. 在某些特殊情况下,您将加入两个都不唯一的列。因此,在那些色谱柱上不可能存在PK / FK。

  4. 你可能会认为上面的点2和3是不相关的,因为你的问题是什么时候出现IS表之间的单人PK / FK关系。但是,表之间存在单个PK / FK并不意味着您除了PK / FK之外不能再加入其他字段。sql引擎不知道您想加入哪些字段。

  5. 假设您有一个表“ USA_States”,以及另外5个表,这些表具有指向状态的FK。“五个”表之间也有一些外键。sql引擎应该自动将“五个”表与“ USA_States”联接吗?还是应该将“五个”相互联系?都?您可以设置这些关系,以便sql引擎进入一个无限循环,尝试将所有内容连接在一起。在这种情况下,sql引擎无法猜测您想要什么。

总结: PK / FK与表联接无关。它们是独立的无关的事物。您经常加入PK / FK列只是自然而然的事。

您想让sql引擎猜测它是完全,左,右还是内部联接吗?我不这么认为。尽管可以说,这比猜测各列要合并的罪要少。


7
我认为外键和规范化与表联接非常相关。

3
当普通的JOIN关键字始终尝试匹配该参数时,您的参数将成立(因为在我的示例中我做错了,我将对其进行修复)。但是,许多联接只能直接从联接派生,因此我看不出没有任何明确的语法来联接它们的原因。许多DBMS确实具有自然联接,它的功能基本上相同,但具有列名(= bad)。使用这种类型的联接可以完成相同的操作,例如,通过指定AUTO JOIN操作。

5
“您经常加入PK / FK色谱柱只是自然而然的事” –我不相信!
某天,2012年

2
“正常化?” 我认为这里的想法是,如果您从1NF relvar开始,然后分解为6NF relvars,则可能是:a)他们在实现上有外键; b)他们经常被加入查询中。
2012年

4
如果不是“ PK / FK与表联接无关”,
ypercubeᵀᴹ

11

“连接性”的概念。当且仅当具有相同名称的属性具有相同的类型时,关系r1和才r2可以连接...此概念不仅适用于这样的联接,而且也适用于各种其他操作(例如,并集)。

SQL和关系理论:如何按CJ Date编写准确的SQL代码

标准SQL已经具有称为的功能NATURAL JOIN,并已在mySQL中实现。

尽管您的建议不那么值得,但似乎是一个合理的建议。对于SQL Server(不支持NATURAL JOIN),我在Management Studio中使用了SQL Prompt:在编写INNER JOIN其InteliSense建议ON子句时,它会基于通用属性名称和外键,我发现它非常有用。不过,我非常希望看到一种新的(标准)SQL连接类型。


1
普通列上的自然连接和连接与FK-PK上的连接概念不同且正交。(请参阅我的回答。)
philipxy

@philipxy:同意,我无意暗示其他情况。(您的回答很好!)
某天,

9

SQL排在第一位!

外键和外键约束后来出现,本质上是对“事务”样式应用程序的优化。

关系数据库最初被认为是一种通过使用关系代数在数学上可证明的方式对数据集进行复杂查询的方法。对于给定的数据集和给定的查询,IE始终只有一个正确的答案。

自那时以来,关系数据库已经走了很长一段路,而作为交易系统的持久层的主要用途并不是CODD等人。所有的设想。

但是,ANSI标准组织出于所有相互冲突的目标和供应商政策,一直努力保持SQL的“数学上可证明的”属性。

如果允许数据库从“隐藏的”外键数据中推断联接属性,那么您将失去此属性(如果定义了一组以上的外键,请考虑模棱两可)。

同样,阅读SQL的程序员不一定知道当前为这两个表定义了哪些外键,并且需要检查数据库架构以弄清楚查询在做什么。


3
谢谢,这对我来说很有意义!但是,自然联接不存在相同的问题吗?尽管自然联接甚至存在更大的问题,但许多DBMS确实支持它们。IMO基于pk / fk的连接自然是正确的连接。

1
就自然联接和显式“ JOIN ... ON”而言,就大多数数据库引擎而言,没有区别。引擎分析查询,并根据各种谓词尽最大可能进行连接。使用显式联接不会强制使用特定的索引或访问路径,它在那里主要用于支持“ LEFT,OUTER,INNER”联接语法,该语法需要知道显式联接谓词才能知道何时插入“缺失”行。

6
SQL不是第一位的!关系模型(当然包括外键的概念)最早是由EFFCodd在1969年提出的。当时的SEQUEL直到1974年左右才成为现实。它的发明者从一开始就明确指出: SEQUEL / SQL旨在基于先前存在的关系模型-尽管SQL确实没有成为真正的关系语言。
nvogel

@sqlvogel-是的!应该将其表述为“首先实施SQL”。
詹姆斯·安德森

CJ Date在“数据库系统简介”(p276)中说,科德发明了外键的概念。没有说什么时候,但我认为它是在第一个SQL实现之前。
一天,2016年

7

尽管定义了外键关系,但这并不意味着您要在所有查询中联接表。这是最可能的连接表的方法,但是在某些情况下它是不正确的。

  • 您可能出于某些目的使用两个表的笛卡尔乘积或其一部分。
  • 您可能还可以在其他字段上加入其他用途。
  • 如果要联接三个或更多表,则其中一个表可能与两个或多个表相关。在这种情况下,通常只有一种可能的FK关系适合于查询。

7

您可能以错误的假设进行操作。 您说“尽您所能”,但不提供任何经验或证据。如果pk或fk是查询的最佳索引,则将使用它。我不知道为什么会这样,但是我的猜测是查询格式不正确。


现在编辑问题已被完全重写:您所描述的情况仅适用于非常小的一组查询。如果有12个表已联接怎么办?如果没有FK,该怎么办...。即使存在默认联接,我仍然总是出于可读性的目的指定联接。(我不想先查看数据,然后尝试找出要加入的内容)

某些查询工具实际上为您执行了自动联接,然后允许您删除或编辑联接。我认为MS Access的查询生成器可以做到这一点。

最后,ANSII标准规定必须指定连接。这是足够的理由,不允许这样做。


3
对不起,也许我还不够清楚。我不是在谈论索引,我是在谈论联接。假设我有table1和table2,并且table1.a上的fk指向table2.b。如果我联接了这些表,则必须明确地说我想将它们联接在列a和b上(例如'SELECT * FROM table1 JOIN table2 ON table1.a = table2.b '),而我已经在数据库中进行了定义这两个是相关的方案。问题是为什么我不能执行“ SELECT * FROM table1 JOIN table2”,而让DBMS根据fk / pk自动选择联接列。

3
尤其是可读性对我来说很有意义!但是,该标准说明了这一点,IMO并不是一个很好的论据。以前,许多标准都做出了错误的选择(例如HTML)。

3

数据库无法安全地执行此操作的原因有很多,其中包括添加/删除外键会改变预写查询(包括应用程序源代码中的查询)的含义的事实。大多数数据库也没有很好的外键集,无法涵盖您可能想要执行的所有可能的联接。同样出于更好或价值的考虑,外键通常会被删除以加速系统运行,并且不能用于从文件中以“错误”顺序加载的表。

但是,没有理由为什么查询设计工具或文本编辑器无法在外键的帮助下自动完成联接,就像它们使您对列名具有智能感知一样。如果工具出错,则可以编辑查询并保存完整定义的查询。这样的工具还可以有用地利用按“父”表名和在父/子表等中具有相同名称的列来命名外键列的约定。

(我的妻子仍然无法理解Management Studio和Sql Server之间的区别,并在她启动Management Studio时谈论了如何启动sql server!)


3

自然加入“自动”共同列平等加入,但你应该只写,如果这就是你什么根据表的意义和你desied结果。没有“自动”知道如何“连接”两个表或以任何其他方式“查询”出现在查询中的任何表。我们不需要知道要查询的约束。它们的存在仅意味着输入可能会受到限制,因此输出可能也会受到限制。您可以定义某种join_on_fk_to_pk运算符,该运算符根据声明的约束自动“连接”;但是,如果您希望查询的含义保持不变(如果仅更改约束,而不更改表的含义),那么您必须更改该查询以使用新的声明的内容。尽管有任何约束变化,但已经使含义相同

持有什么约束(包括PK,FK,UNIQUE和CHECK)不影响表的含义。当然,如果表的含义发生变化,则矛盾可能会发生变化。但是,如果约束发生变化,并不意味着查询应该发生变化。

人们不需要知道要查询的约束。了解约束意味着我们可以使用更多的表达式,这些表达式如果没有约束保持就不会返回相同的答案。例如,通过UNIQUE期望一张表有一行,因此我们可以将其用作标量。如果假定但未声明约束,则这些查询可能会中断。但是,声明查询未假定的约束不会破坏它。

是否有任何经验法则可以从易于理解的描述中构造SQL查询?


2

原因是存在语言,然后是基础主体。该语言是稀疏的,缺少许多您希望在通用语言中看到的功能。这只是碰巧是一个不错的功能,尚未添加到语言中,可能不会添加。这不是一门枯燥的语言,所以有一些希望,但我不会乐观。

正如其他人指出的那样,某些实现使用扩展名,其中join(列)基于共同的列名联接两个表,这有点相似。但是它并没有广泛传播。请注意,此扩展名与SELECT * FROM employee NATURAL JOIN department;语法不同,该语法不包括指定使用哪些列的方法。两者都不依赖于表之间的关系,这使它们不可靠(自然连接语法比扩展更多)。

“ PKFK上的内部连接表”没有根本的障碍,其中PKFK是一个关键字,意思是“两个表之间定义的外键关系”,同一个表可能存在多个fk的问题,但这可能会导致错误。问题是设计语言的人是否认为a)一个好主意和b)比其他一些语言更改更好地进行工作...


3
假定他们应该已经做好是一个好主意。他们也很可能已经考虑过并决定不这样做。在实践中,也许这是一个非常糟糕的主意:Sjoerd提到了一个示例,其中查询可能仅仅由于添加新列和FK关系而中断。Tydus勋爵还解释了外键具有不同的责任,它规定了表的连接方式。

1
@JonathanHobbs:我的意思是我的回答通常是中立的,但是放弃中立.Sjoerd的逻辑是有缺陷的。对表的更改已经破坏了查询,在表主键中添加新列将破坏查询或开始返回不正确的结果。实际上,只要保持表关系,就可以在某种程度上使您与之隔离,可以安全地完成列更改,这可能会增加FK关系的使用率,因为这对RI以外的其他情况很有用。在PK上或包含Pk。要处理多重fk,请使用列名。
jmoreno'3

1

如果假设忽略ON子句是基于参照完整性遵循字段,那么您将如何进行笛卡尔积?

编辑:使用AUTO 这样做的好处是键入的次数少了,您不必知道它们是如何联接的,也不必记住复杂的联接。如果关系芯片发生变化,它将被自动处理,但是除了早期开发,这种情况很少发生。

现在,您要做的是确定在关系更改期间是否所有AUTO联接都保持有效,以匹配select语句的意图。


@JeffO:主要优点是它以一种非常清晰的声明方式更准确地表达了意图。除列名的某些内容与其他列中的内容相似(但可能不是相同的类型)外,列名上的联接不会告诉您任何其他信息。在FK裁判联接,告诉你,有一个FK参考,不列清单将意味着有表之间只有1 FK,或者相反,有1+(考虑超过1个REF时会发生什么一个多键您可以将列c1 = fk1_c1和c2 = fk2_c2混合使用)。即使平均输入更多内容,这样做也很好。
jmoreno'3

在不使用ON的情况下使用(INNER)JOIN不是标准SQL。逗号,CROSS JOIN和(内部或任何外部)JOIN ON 0 = 0返回笛卡尔积。
philipxy

-1

数据库为什么不能弄清楚如果我联接那些表,我想在pk / fk列上联接它们?

部分原因是:

1-理论上,您可以将两个表中任意列上的表连接起来。尽管这不是普遍做法,但它是有效的。请记住,SQL就像一种编程语言,它不了解课程和名称列中包含的信息,对于SQL而言,在这方面没有多大意义。

2-联接的类型不同(左联接,右联接,内部联接)-内部联接只有其中一种。

3-SQL标准可以遵循低级语言的原则,该语言允许高级方言使用它来形成智能。如果您想到的是第四代语言与第三代语言,则比较会更加清楚。实际上,我使用的一种工具IEF允许您编写如下内容:

ReadEach Customer 
Where Customer Places Orders and That Customer LivesIn "California" 
and OrderValue > 100.00

总之,您的建议很有趣,可以作为标准的一部分或作为存储过程(默认为内部联接)来实现。


-10

Tiddo,我相信您是完全正确的,关于该主题的SQL相当愚蠢,而且我记得十年前在学习SQL 时曾想到过与外键相同的事情。

好的,考虑到这一点,我最终必须通过考试。为了通过它,我不得不放手。SQL更是一个trainwreck比任何人都可能会承认,它的标准化路径是一个完整的灾难,和一些实施来势汹汹图灵完备。总的来说,它还是很方便的。(我不是K / V luddite)

那么,外键……一点也不方便。它们是关系模型中的一个重要概念,可以,但是同名SQL功能不能很好地比较。

直截了当告诉您:遇到性能问题的大型系统之前,请不要使用该SQL功能Foreign Key。明确地告诉引擎哪个字段是一个外键和没有用于索引,它是无形的数据库用户。

有误导性吗?
是。

在人们被误导了30年之后,他们是否现在将使其变得更强大?
没有机会。

在必要之前完全忽略外键... 对我来说固定的 SQL?
是!

以及为什么所有这些首先发生呢?
好吧,我们称之为外键的功能后来又添加到了SQL中。SQL是自下而上随时间演变的标准。供应商实施了可笑的功能,而标准机体却面色苍白。

如前所述,外键仅用于索引,没有可用的JOIN构造。(在SELECT查询中加入查询,这些JOIN查询是最近才出现的,仅是为了实现别名SELECT功能)尽管它们调用了indexing flag FOREIGN KEY,但它们可能是对关系数据库理论概念的巧妙命名


13
关于外键,我认为您只接触过MySQL上的MyISAM引擎吗?因为甚至不理会那只小家伙,这个答案中的每件事都是错误的。

Fk不用于索引,实际上一个常见的问题是fk列上没有索引,这可能会对性能产生巨大影响。
jmoreno
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.