加入是为懒人准备的吗?

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

对于他来说,连接是为那些不关心性能的懒人准备的。这是真的吗? 我们应该避免使用连接吗?

7788 次浏览

他在一般情况下是错的。

数据库能够使用各种方法进行优化,这得益于优化器提示、表索引、外键关系以及可能的其他数据库供应商特定信息。

嗯,连接是关系数据库之间相互关联表的方式。我不知道他的意思是什么。

对数据库进行多次调用如何能比一次调用更有效率?另外,sql 引擎在做这类事情时已经进行了优化。

也许你的同事懒得学 SQL。

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

如果没有连接,您将如何将订单项与订单联系起来? 这就是关联式资料库管理系统的意义所在。 如果没有连接,就没有关系数据,还不如使用文本文件 来处理数据。

听起来他不明白这个概念所以他试图让它们看起来毫无用处。他也是那种认为 Excel 是一个数据库应用程序的人。 打他一耳光,让他多了解一些数据库知识。建立多个连接、提取数据并通过 C # 合并数据是错误的做法。

不,我们应该避免那些持有如此错误观点的开发人员。

在许多情况下,数据库连接比通过客户机完成的任何数量级都要快几倍,因为它避免了数据库往返,而且数据库可以使用索引来执行连接。

在我的头脑中,我甚至无法想象一个正确使用的连接会比等效的客户端操作慢的场景。

编辑: 在一些罕见的情况下,自定义客户机代码可以比直接的 DB 连接更有效地完成任务(参见 meriton 的注释)。但这是个例外。

不,你不应该。

数据库是专门用来操作数据集的(显然... ...)。因此,他们在这方面的效率令人难以置信。通过在自己的代码中执行本质上是手动连接的操作,他试图接管专门为这项工作设计的某个角色。他的代码能像数据库中的代码一样高效的机会非常渺茫。

顺便说一句,如果没有连接,使用数据库有什么意义呢? 他还不如直接使用文本文件。

不,不仅在数据库代码(ad-hoc C #/Java)中对连接进行了更好的优化,而且通常还可以应用几种过滤技术,这会带来更好的性能。

他大错特错了。虽然在 C # 或 Java 等语言中进行数据操作有一定的优点,但由于 SQL 本身的特性,连接在数据库中是最快的。

SQL 保留了关于数据的详细统计信息,如果正确创建了索引,可以非常快速地在几百万个索引中找到一个记录。除此之外,当你可以在数据库级别上正确地进行连接时,为什么还要将所有数据拖到 C # 中进行连接呢?

当您需要迭代地执行某些操作时,使用 C # 的优点就会发挥作用。如果您需要为每一行执行一些函数,那么在 C # 中执行这些操作可能会更快,否则,连接数据将在 DB 中得到优化。

我工作的上一家公司也没有使用 SQL 连接。相反,他们将这项工作移动到设计为水平伸缩的应用程序层。这种设计的基本原理是避免在数据库层进行工作。通常是数据库成为瓶颈。复制应用程序层比复制数据库更容易。可能还有其他原因。但这是我现在能想起来的。

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

请注意,我并没有强烈要求避免 SQL 连接。

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

然而,关系数据库在处理集合方面进行了大量优化。有很多很多基于连接查询数据的方法,它们比大量的往返更有效率。这就是 RDBMS 多功能性的来源。您也可以在 nosql 存储中实现同样的功能,但是您通常最终会构建一个适合每种不同查询性质的单独结构。

简而言之: 我不同意。在 RDBMS 中,连接是 基本原理。如果不使用它们,就不能将其用作 RDBMS。

我不明白“连接到 SQL 是无用的”这句话的逻辑。 在处理数据之前对数据进行过滤和限制是否有用?正如你的其他受访者所说,这是什么数据库引擎做,这应该是他们擅长的。

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

我让你来决定。

我要说的是,我遇到过这样一种情况,它更快地分解查询并在代码中执行连接。也就是说,只有在一个特定版本的 MySQL 中我才需要这么做。在其他方面,数据库可能会更快(注意,您可能必须优化查询,但它仍然会更快)。

我怀疑他对数据库应该用于何种用途的看法有限。最大化性能的一种方法是将整个数据库读入内存。在这种情况下,您可能会获得更好的性能,并且为了提高效率,您可能希望执行连接。然而,恕我直言,这并不是真正使用数据库作为数据库。

“这在技术上是正确的”——类似地,SQL 数据库是无用的: 如果使用一组 CSV 文件就可以得到相同的结果,并将它们用代码关联起来,那么使用一个 SQL 数据库又有什么意义呢?见鬼,任何抽象都是为懒人准备的,让我们回到硬件上的机器代码编程吧!;)

此外,除了最复杂的情况之外,他的断言在所有情况下都是不正确的: RDBMS 进行了大量优化,以使 JOIN 很快关系数据库管理系统,对吗?

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

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. 合上笔记本。

他错了,连接是有能力的程序员使用的。可能有一些有限的情况下,他提出的方法更有效(在这些情况下,我可能会使用文档数据库) ,但我不能看到它,如果你有任何适当的数据量。以下面这个查询为例:

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 子句。假设表2中也只有15个。您可以运行这个 sql 语句,如果正确地编制索引,它将花费毫秒的时间,并在整个网络中返回15条记录,其中只有1列数据。或者,您可以发送包含两列数据的1000万条记录,然后分别发送包含一列数据的另外100万条记录,这些记录可以通过网络发送,并在 Web 服务器上进行组合。

当然,你也可以在任何时候都将数据库的全部内容保存在 Web 服务器上,如果你有大量的数据和数据不断变化的话,这就显得非常愚蠢了。如果你不需要关系数据库的素质那就别用。但是如果你这样做,那么正确地使用它。

在我作为一个软件开发人员的职业生涯中,我经常听到这样的争论。几乎每次提出这个观点的人都对关系数据库系统、它们的工作方式以及这些系统的使用方式知之甚少。

是的,当使用 错误的时,连接看起来是无用的甚至是危险的。但是,如果以正确的方式使用,数据库实现就有很大的潜力来执行优化,并“帮助”开发人员最有效地检索正确的结果。

不要忘记,使用 JOIN,你可以告诉数据库你期望的数据片段之间相互关联的方式,从而给数据库更多关于 什么的信息,从而使它能够更好地满足你的需求。

所以答案是肯定的: 不,JOINSare 不是没用的!

听起来像是典型的 我可以写得更好换句话说,他看到了一些他认为有点麻烦的东西(在 SQL 中编写大量连接) ,并说“我确信我可以编写得更好,获得更好的性能。”您应该问他是否: a)比那些深谙 Oracle 或 SQLServer 优化代码的普通人更聪明,b)受教育程度更高。很可能不是。

是的,你应该。

你应该使用 C + + 而不是 C # ,因为 C # 是为懒人准备的。

不,不,不。你应该使用 C 而不是 C + + ,因为性能。 C + + 是给懒人用的。

不,不,不。你应该使用汇编而不是 C,因为性能。 C 是为懒人准备的。

是的,我在开玩笑。你可以在没有联接的情况下编写更快的程序,也可以在没有联接的情况下使用更少的内存来编写程序。但是在许多情况下,开发时间比 CPU 时间和内存更重要。放弃一点表演,享受你的生活。不要浪费你的时间在小小的表演上。然后告诉他“你为什么不直接从你家到你的办公室?”

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

除非我严重误解了,否则这个问题的逻辑是错误的

如果每个 A 在 B 中有20行,那么 A 中的1000行意味着 B 中的20k 行。 B 中不可能只有100行,除非有许多表“ AB”,其中包含20k 行的映射。

因此,为了获得关于100个 B 行中的哪20个行映射到每个 A 行的所有信息,表 AB 也是如此。所以要么是:

  • 3个包含100、1000和20k 行的结果集和一个客户端 JOIN
  • 一个具有20k 行的连接的 A-AB-B 结果集

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

在任何情况下,我都会说这种程度的交叉连接几乎没有任何用处。这是个不好的例子。

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

事后想想:

要加入客户端,需要持久性对象,如 DataTables (在。网)。如果您有一个扁平化的结果集,那么可以通过更轻量级的设备(如 DataReader)使用它。大容量 = 大量用于避免数据库 JOIN 的客户机资源。