加入是为了懒惰的人?


169

我最近与另一位开发人员进行了讨论,该开发人员向我声称JOIN(SQL)是无用的。从技术上讲,这是正确的,但他补充说,使用联接比在代码(C#或Java)中发出多个请求和链接表效率低。

对于他来说,加入是针对那些不在乎性能的懒惰的人。这是真的?我们应该避免使用联接吗?


114
否。数据库经过优化以执行联接,它们非常快,特别是对于大型数据集。您不希望您的应用程序加载数万行并将它们手动合并在一起。
Halfdan

91
编程语言是为懒惰的人准备的。它们的效率不如手动编写CPU指令。:)
Michael McGowan

76
开发者的名字是什么?我想确保我从不雇用他。

39
@Michael meh,真正的程序员使用蝴蝶……
Marc Gravell

14
关于您的“这是真的”-不,不是。数据库通过集合论进行工作;参加比赛非常好和有用...
Marc Gravell

Answers:


188

不,我们应该避免持有如此难以置信的错误观点的开发人员。

在许多情况下,数据库联接比通过客户端执行的任何操作快几个数量级,因为它避免了数据库往返,并且数据库可以使用索引来执行联接。

我什至无法想像一种情况,正确使用联接会比等效的客户端操作慢。

编辑:在某些罕见的情况下,自定义客户端代码比直接的数据库联接可以更有效地执行操作(请参阅meriton的评论)。但这是非常例外。


1
三向联接怎么样?在某些情况下,最好不要在“代码中”进行操作吗?
julien_c 2011年

56
在应用服务器加入可以更加有效的,如果加入数据库上导致通过网络发送的结果集严重冗余。考虑表A和B,其中A中的每一行与B中的20行相关联,B仅具有100行,我们想从A中获取前1000行,并从B中获取相关行。联接数据库将得到20 *通过网络发送的1000个元组。如果联接是在应用服务器中完成的(首先将整个B表提取到内存中),则网络上仅发送100 + 1000行。
meriton 2011年

7
但是,您肯定是正确的,因为在大多数情况下,数据库上的连接要快得多,因此,这不仅是方便的问题,而且是必需的。
meriton

13
我很幸运地与Microsoft从事SQL Server的一些开发人员交谈。这会让您头昏眼花,听他们对查询所做的优化。任何认为自己比这更聪明的人都应该受到谴责。
riwalk 2011年

2
@meriton我有点惊讶;我希望客户端库能够优化交叉连接。
Phil Lello

83

在我看来,您的同事最好使用no-sql文档数据库或键值存储。它们本身就是非常好的工具,非常适合许多问题。

但是,关系数据库已针对使用集进行了优化。有基于连接的是查询数据的很多,很多方面远远超过许多往返的效率更高。这是rdbms的多功能性来自何处。您也可以在nosql存储区中实现相同的功能,但是您通常最终会构建一个单独的结构以适合每种不同的查询性质。

简而言之:我不同意。在RDBMS中,联接是基本的。如果不使用它们,则不会将其用作RDBMS。


46

好吧,他在一般情况下是错误的。

在优化程序提示,表索引,外键关系以及其他可能的数据库供应商特定信息的帮助下,数据库能够使用多种方法进行优化。


1
我必须承认,当我开始使用数据库时,我一直坚信我可以击败联接的性能。但是不久之后,数据库就实现了惊人的快速连接。实际上,我想说的是,在这种情况下,最好与员工进行公开讨论,而不是将其视为白痴。
LegendLength

1
@LegendLength我要说的是,如果他们不那么聪明的话,甚至是对的。无需假设聪明,因为它们犯的错误与我们记得的错误相同(事实上,对我而言,这可能意味着他们并不那么聪明...)。这很简单:它很少有助于解雇。偶尔出错也可以!
sehe

24

不,你不应该。

数据库是专门为处理数据集而设计的(显然。。。)。因此,他们在执行此操作时效率极高。通过在他自己的代码中进行本质上是手动联接的工作,他正试图接管专门为这项工作设计的角色。他的代码与数据库中的代码一样高效的机会非常渺茫。

顺便说一句,没有连接,使用数据库有什么意义?他可能还只是使用文本文件。


2
即使没有加入?自动内存中映射,自动查询缓存以及大多数其他文件系统根本不会发生的许多其他自动的东西。哦,我提到可控交易了吗?
Piskvor在2011年

19

如果将“懒惰”定义为希望编写更少代码的人,那么我同意。我同意,如果将“懒惰”定义为希望拥有工具的人去做自己擅长的事情。因此,如果他只是同意Larry Wall(关于优秀程序员的属性),那么我也同意他的观点。


我增加了lazy的精度:对于那些不关心性能而喜欢编写更少代码的懒惰人。我认为联接适用于懒惰的人,但在这种情况下,联接也比几个请求要好。
Bastien Vandamme

3
@Dran Dane:加入是给懒惰的人的,是的。它们可能表现良好的事实是正交的。
Piskvor在2011年

16

嗯,联接是关系数据库如何将表彼此关联。我不确定他在说什么。

如何多次调用数据库比一次调用更有效率?加上sql引擎在执行此类操作方面已进行了优化。

也许您的同事懒得学习SQL。


12

是的你应该。

而且由于性能,您应该使用C ++而不是C#。C#适用于懒惰的人。

不不不。由于性能原因,应使用C而不是C ++。C ++适用于懒惰的人。

不不不。由于性能原因,应使用汇编而不是C。C是懒惰的人。

是的,我在开玩笑。您可以在没有联接的情况下制作速度更快的程序,并且可以在没有联接的情况下使用更少的内存制作程序。但是在许多情况下,开发时间比CPU时间和内存更重要。放弃一点表现,享受生活。不要浪费时间一点点的性能。并告诉他:“为什么不从您的位置到办公室直走高速公路?”


1
到目前为止,我已经查看了您的所有答案,它们非常有趣。请让他们来。要么,或者我可以在哪里订阅您的博客?
格里

11

“从技术上讲,这是正确的”-类似地,SQL数据库也没有用:如果使用一个CSV文件并将其关联到代码中就可以得到相同的结果,那么使用一个数据库有什么意义呢?哎呀,任何抽象都适合懒惰的人,让我们回到在硬件上使用机器代码进行编程的方式!;)

而且,除了最复杂的情​​况之外,他的主张都是不正确的:RDBMS进行了严格的优化,以使JOIN 快速关系数据库管理系统,对吗?


2
+1短语“......在技术上真正的”会更好地工作,如果OP曾用字unnecessary,而useless在前面的句子。说联接无用显然是不正确的,不需要考虑任何技术问题。无论如何,操作人员和同事对RDBMS的误解并不罕见:stackoverflow.com/q/5575682/47550
Paul Sasik 2011年

7

我工作的最后一家公司也不使用SQL连接。相反,他们将这项工作移至旨在水平缩放的应用程序层。此设计的基本原理是避免在数据库层工作。通常,数据库成为瓶颈。它比数据库更容易复制应用程序层。可能还有其他原因。但这是我现在记得的那个。

是的,我同意与数据库连接相比,在应用程序层完成的连接效率低下。网络通讯也更多。

请注意,我对避免SQL连接并没有采取强硬立场。


好吧,这听起来像是针对您的特定情况针对JOIN的合理论证。我记得FB Engineering在他们的博客上发布了类似的内容-扩展也是他们的主要优先事项。las,只有一小部分程序员将需要这样做,但是许多人认为他们这样做是“因为OMG Facebook也这样做了”;)
Piskvor在2011年

好的,在企业解决方案中,您有足够的流量来使数据库服务器超载,这可能值得考虑,但更有可能是报告存储过程或预定的备份影响性能。数据库是擅长连接,尤其是如果有indecies以帮助
Jodrell

@Jodrell:是的,他们擅长加入;同样,在某些极端情况下,您需要降低连接的优雅度以获得更大的力量。我遇到过这样一种情况;我们尝试了所有可能的解决方案,实际上在这种非常特殊的情况下,无联接解决方案是最快的。不,在该特定服务器上没有其他任何运行。如果没有存储过程,存储过程将不会让您放慢速度;)
Piskvor在2011年

5

如果没有联接,您将如何将订单商品与订单相关联?这就是关系数据库管理系统的重点。没有联接就没有关系数据,您不妨使用文本文件来处理数据。

听起来他不理解这个概念,所以他试图使它们看起来毫无用处。他与认为excel是数据库应用程序的人是同一类型。愚弄他一巴掌,告诉他阅读有关数据库的更多信息。通过C#建立多个连接并提取数据并合并数据是错误的处理方式。


5

我不明白“ SQL中的联接是无用的”语句的逻辑。在处理数据之前过滤和限制数据是否有用?正如您其他受访者所说的那样,这就是数据库引擎的工作,这应该是他们擅长的。

也许一个懒惰的程序员会坚持使用他们熟悉的技术,并出于非技术原因而避开其他可能性。

我让你决定。


5

让我们考虑一个示例:一个带有发票记录的表,以及一个带有发票行项目记录的相关表。考虑客户端伪代码:

for each (invoice in invoices)
    let invoiceLines = FindLinesFor(invoice)
...

如果您有100,000个发票,每个发票有10行,则此代码将从100万张表中查找10条发票行,并且将执行100,000次。随着表大小的增加,选择操作的数量会增加,并且每个选择操作的成本也会增加。

如果计算机速度很快,如果您有几千条或更少的记录,您可能不会注意到这两种方法之间的性能差异。因为成本的增加不只是线性的,所以随着记录数量的增加(例如成百万的记录),您将开始注意到差异,并且随着数据集大小的增加,差异将变得难以忍受。

但是加入。将使用表的索引并合并两个数据集。这意味着您有效地扫描了第二张表,而不是随机访问了N次。如果定义了外键,则数据库已经在内部存储了相关记录之间的链接。

想象一下自己做。您有一个按字母顺序排列的学生名单和一个笔记本,其中包含所有学生的成绩报告(每堂课一页)。笔记本按学生姓名的顺序排序,顺序与清单相同。您希望如何进行?

  1. 从列表中读取一个名称。
  2. 打开笔记本。
  3. 找到学生的名字。
  4. 阅读学生的成绩,翻页,直到到达下一个学生或最后一页。
  5. 关闭笔记本。
  6. 重复。

要么:

  1. 打开笔记本到第一页。
  2. 从列表中读取一个名称。
  3. 从笔记本上读取该名称的所有成绩。
  4. 重复步骤2-3,直到结束
  5. 关闭笔记本。

5

听起来像是“ 我可以写得更好 ” 的经典案例。换句话说,他看到自己觉得有些痛苦(在SQL中编写了一堆连接),并说:“我相信我可以写得更好,并获得更好的性能。” 您应该问他是否比a)精通Oracle或SQL Server优化代码的普通人更聪明,b)受过更多教育。奇怪的是他不是。


3

他肯定是错的。尽管在C#或Java之类的语言中,数据处理具有一定的优势,但由于SQL本身的性质,联接在数据库中最快。

SQL会保留有关数据的详细统计信息,如果正确创建了索引,则可以很快找到几百万条记录中的一条。除了为什么您只想在数据库级别上将所有数据拖到C#中进行联接的事实之外?

当您需要迭代地执行某些操作时,使用C#的专家就会发挥作用。如果需要为每一行执行某些功能,则在C#中这样做可能会更快,否则,将在数据库中优化联接数据。


3

我要说的是,我遇到了这样一种情况:它更快地分解查询并执行代码联接。话虽如此,我只需要使用一个特定的MySQL版本即可。在其他所有方面,数据库可能会更快(请注意,您可能必须优化查询,但仍然会更快)。


3

我怀疑他对于应该使用什么数据库的观点有限。最大化性能的一种方法是将整个数据库读入内存。在这种情况下,您可能会获得更好的性能,并且如果需要提高内存效率,则可能需要执行联接。但是,这实际上并没有使用数据库,而是数据库IMHO。


3
无论如何,大多数数据库引擎都会在后台为您执行此操作;例如在MySQL中,您可以创建一个纯粹的内存表(MEMORY引擎)。在没有数据库的情况下重新实现数据库功能通常是NIH严重病例的征兆;)
Piskvor在2011年

@phoog:不是在这里发明的 -换句话说,“我没想到,所以它不存在”。因此,许多方形车轮被重新发明。(是的,有时重新发明轮子很有用,例如,如果您要制造赛车;重新发明“仅仅因为”不可能使您拥有更好的轮子)
Piskvor在

换句话说,“我没有做到,所以一定是垃圾”。仅在“我没有测试过它,可能不适合我的目的”的前提下,它才有一定的道理,因此在判断之前,请先对其进行测试。
彼得·劳瑞

@Piskvor:不一定,数据库只能使用其运行的系统的内存,而应用程序可以使用应用程序服务器的内存。换句话说:如果数据库位于专用主机上,则访问该缓存仍然需要网络带宽,并且会受到网络延迟的影响,但是可以以低内存访问延迟的速度查询应用程序保留的任何缓存。
meriton

2

不,不仅可以在临时C#/ Java的数据库代码中更好地优化连接,而且还可以更好地优化连接。但通常可以应用几种过滤技术,从而获得更好的性能。


2

他错了,连接才是合格的程序员所使用的。在某些有限的情况下,他提出的方法更有效(而且我可能会使用Documant数据库),但是如果有大量数据,我将看不到它。例如,使用以下查询:

select t1.field1 
from table1 t1
join table2 t2 
    on t1.id = t2.id
where t1.field2 = 'test'

假设您在表1中有1000万条记录,在表2中有100万条记录。假设表1中有900万条记录满足where子句。假设其中只有15个也位于table2中。您可以运行此sql语句,如果正确建立索引将花费毫秒,并且仅通过1列数据在网络上返回15条记录。或者,您可以通过两列数据发送一千万条记录,并通过网络分别发送另外一百万条带有一列数据的记录,并将它们组合在Web服务器上。

或者,当然,您可以随时将数据库的全部内容保留在Web服务器上,如果您拥有的数据量和不断变化的数据量不多,那就太傻了。如果您不需要关系数据库的质量,请不要使用它。但是,如果这样做,请正确使用它。


2

在我作为软件开发人员的职业生涯中,我经常听到这种说法。几乎每次陈述时,提出要求的人对关系数据库系统,它们的工作方式以及应使用此类系统的知识都不多。

是的,如果使用不当,联接似乎毫无用处,甚至很危险。但是,如果以正确的方式使用,则数据库实现有很大的潜力来执行优化并“帮助”开发人员最有效地检索正确的结果。

不要忘记使用JOIN你讲述你所期望的数据的件相互关联的,因此给数据库的更多信息的方式对数据库有什么你正在尝试做的,因此使得它能够更好地满足您的需求。

因此,答案肯定是:不,一点JOINS都没有用!


0

仅在一种在应用程序中不经常使用的情况下(当查询返回联接中所有表的所有行时),“技术上是正确的”。在大多数查询中,仅返回每个表的一部分行。数据库引擎经常使用索引来消除不需要的行,有时甚至不读取实际行,因为它可以使用存储在索引中的值。数据库引擎本身是用C,C ++等编写的,并且至少与开发人员编写的代码一样有效。


0

除非我被严重误解,否则问题中的逻辑是非常有缺陷的

如果每个A的B中有20行,则A中的1000行意味着B中的2万行。除非B中的表“ AB”很多,其中包含映射的20k行,否则B中不能只有100行。 。

因此,要获取有关100条B行中的哪20条映射到每条A行的所有信息,您还可以表AB。所以这可能是:

  • 3个结果集(分别为100、1000和20k行)和一个客户端JOIN
  • 具有20k行的单个JOINed A-AB-B结果集

因此,当您检查数据时,客户端中的“ JOIN”确实会添加任何值。并不是说这不是一个坏主意。如果我要从数据库中检索一个对象,那么将其分解为单独的结果集更有意义。对于报告类型的调用,我几乎总是将其拼合为一个。

无论如何,我要说这种规模的交叉连接几乎没有用。这是一个糟糕的例子。

您必须在某个地方加入,这就是RDBMS擅长的。我不想与任何认为自己可以做得更好的客户端代码猴子合作。

事后思考:

要加入客户端,需要持久对象,例如DataTables(在.net中)。如果您有一个扁平化的结果集,则可以通过诸如DataReader之类的轻量级对象来使用它。高容量=大量客户端资源用于避免数据库JOIN。

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.