【问题标题】:Joins are for lazy people?加入是为懒惰的人准备的?
【发布时间】:2011-08-01 12:17:20
【问题描述】:

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

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

【问题讨论】:

  • 没有。数据库经过优化以执行连接,它们非常快,尤其是对于大型数据集。您不希望您的应用程序加载数万行并将它们手动合并在一起。
  • 编程语言适合懒人;它们比手动编写 CPU 指令效率低。 :)
  • 开发者叫什么名字?我想确保我永远不会雇用他。
  • @Michael meh,真正的程序员使用蝴蝶......
  • 你的“这是真的”——不,不是。数据库通过集合论工作;集合上的连接非常好用且非常有用...

标签: c# java sql join


【解决方案1】:

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

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

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

编辑:在极少数情况下,自定义客户端代码可以比直接的 DB 连接更有效地完成任务(请参阅 Meriton 的评论)。但这在很大程度上是个例外。

【讨论】:

  • 三路连接怎么样?在某些情况下,您最好“在代码中”执行它们吗?
  • 如果加入数据库会导致通过网络发送的结果集出现严重冗余,则加入应用服务器可以更有效。考虑表 A 和 B,其中 A 中的每一行与 B 中的 20 行相关联,B 只有 100 行,我们想从 A 中获取前 1000 行以及从 B 中关联的行。加入数据库将导致 20 * 通过网络发送 1000 个元组。如果连接是在应用服务器中完成的(首先将整个 B 表提取到内存中),则只有 100 + 1000 行通过网络发送。
  • 但是,您肯定是正确的,因为在大多数情况下,数据库上的连接要快得多,因此不仅方便,而且是必要的。
  • 我有幸与一些在 Microsoft 从事 SQL Server 工作的开发人员交谈。听到他们对查询所做的优化会让你头晕目眩。任何认为自己比这更聪明的人都需要被打。
  • @meriton 我有点惊讶;我希望客户端库能够优化交叉连接。
【解决方案2】:

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

但是,关系型数据库针对使用集合进行了高度优化。有很多很多基于连接查询数据的方法大大比大量往返更有效。这就是 rdbms 的多功能性的来源。您也可以在 nosql 存储中实现相同的目的,但您最终通常会构建一个适合每种不同查询性质的单独结构。

简而言之:我不同意。在 RDBMS 中,连接是基本的。如果您不使用它们,那么您就没有将它用作 RDBMS。

【讨论】:

    【解决方案3】:

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

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

    【讨论】:

    • 我必须承认,当我开始使用数据库时,我坚信我可以击败联接的性能。但是没过多久就意识到数据库连接的速度有多快。事实上,我会说在这种情况下,最好以公开的方式与员工讨论,而不是认为他是个白痴。
    • @LegendLength 如果他们不那么聪明,我会说那是真的。不需要假设他们很聪明,因为他们犯的错误和我们记得的一样(事实上,对我来说,这可能意味着他们没有那么聪明……)它更简单:不屑一顾很少有帮助。偶尔出错也没关系!
    【解决方案4】:

    不,你不应该。

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

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

    【讨论】:

    • 即使没有连接?自动内存映射、自动查询缓存、许多其他大多数文件系统根本不会发生的自动魔术。哦,我提到精细可控的交易了吗?
    【解决方案5】:

    如果“懒惰”被定义为想要编写更少代码的人,那么我同意。如果“懒惰”被定义为希望工具做他们擅长的事情的人,我同意。所以如果他只是同意拉里沃尔(关于优秀程序员的属性),那么我同意他。

    【讨论】:

    • 我添加了lazy 的精确度:适用于不关心性能并且喜欢编写更少代码的懒人。我认为 join 是为懒惰的人准备的,但在这种情况下,join 也比几个请求更好。
    • @Dran Dane:加入是为懒惰的人准备的,是的。他们可能会表现良好的事实是正交的。
    【解决方案6】:

    嗯,连接是关系数据库将表相互关联的方式。我不知道他在说什么。

    多次调用数据库如何比一次调用更有效率?加上 sql 引擎在做这类事情时进行了优化。

    也许你的同事懒得学 SQL。

    【讨论】:

      【解决方案7】:

      是的,你应该这样做。

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

      不,不,不。出于性能考虑,您应该使用 C 而不是 C++。 C++ 适合懒人。

      不,不,不。由于性能,您应该使用汇编而不是 C。 C 代表懒惰的人。

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

      【讨论】:

      • 到目前为止,我已经查看了您的所有答案,它们非常有趣。请让他们来。或者,我可以在哪里订阅你的博客?
      【解决方案8】:

      “这在技术上是正确的” - 同样,SQL 数据库是无用的:当您可以通过使用一堆 CSV 文件并在代码中将它们关联起来时,使用一个 SQL 数据库有什么意义?哎呀,任何抽象都是为懒惰的人准备的,让我们回到硬件上的机器代码编程! ;)

      此外,除了最令人费解的情况外,他的断言在所有情况下都是不正确的:RDBMS 进行了大量优化以使 JOIN 快速关系数据库管理系统,对吧?

      【讨论】:

      • +1 如果 OP 在前一句中使用 unnecessary 而不是 useless 的话,短语“......技术上是正确的”会更好。说连接是无用的显然是不真实的,没有需要考虑的技术细节。无论如何,OP 和同事对 RDBMS 观点的误解并不少见:stackoverflow.com/q/5575682/47550
      【解决方案9】:

      我工作的最后一家公司也没有使用 SQL 联接。相反,他们将这项工作转移到旨在水平扩展的应用程序层。这种设计的基本原理是避免在数据库层工作。通常是数据库成为瓶颈。它比数据库更容易复制应用层。可能还有其他原因。但这是我现在能回忆起的。

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

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

      【讨论】:

      • 好吧,在您的具体情况下,这听起来像是反对 JOIN 的合理论据。我记得 FB Engineering 在他们的博客上发布了类似的内容——向外扩展也是他们的首要任务。唉,只有一小部分程序员需要这样做,但许多认为他们这样做“因为 OMG Facebook 也这样做”;)
      • 好的,在企业解决方案中,您有足够的流量使数据库服务器超载,这可能值得考虑,但更有可能是报告存储过程或计划备份来提高性能。数据库擅长连接,尤其是在有帮助时
      • @Jodrell:是的,他们擅长连接;同样,在某些极端情况下,您需要放弃连接的优雅以获得更多功能。我遇到过一个这样的情况;我们尝试了所有可能的解决方案,确实在那种非常具体的情况下,无连接解决方​​案是最快的。不,在那台特定的服务器上根本没有运行其他任何东西。如果您没有任何存储过程,存储过程不会减慢您的速度;)
      【解决方案10】:

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

      听起来他不明白这个概念,所以他试图让它看起来毫无用处。他是认为 excel 是数据库应用程序的同一类人。 扇他一巴掌,让他多读点数据库。通过 C# 建立多个连接并拉取数据并合并数据是错误的处理方式。

      【讨论】:

        【解决方案11】:

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

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

        我留给你决定。

        【讨论】:

          【解决方案12】:

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

          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. 关闭笔记本。

          【讨论】:

            【解决方案13】:

            听起来像是“我可以写得更好”的经典案例。换句话说,他看到了一些他认为令人头疼的事情(用 SQL 编写一堆连接)并说“我确信我可以写得更好并获得更好的性能”。您应该问他是否 a) 比对 Oracle 或 SQL Server 优化代码深入研究的典型人更聪明和 b) 受过更多教育。很可能他不是。

            【讨论】:

              【解决方案14】:

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

              SQL 会不断详细统计有关数据的统计信息,如果您正确创建了索引,可以很快找到几百万条记录中的一条。除了可以直接在数据库级别进行连接的情况下,为什么还要将所有数据拖到 C# 中进行连接?

              当您需要迭代地做某事时,使用 C# 的优点就会发挥作用。如果您需要为每一行执行一些功能,在 C# 中执行此操作可能会更快,否则,连接数据在 DB 中进行了优化。

              【讨论】:

                【解决方案15】:

                我会说我遇到过这样一种情况,它可以更快地分解查询并在代码中进行连接。话虽如此,我只需要使用一个特定版本的 MySQL 来执行此操作。其他一切,数据库可能会更快(请注意,您可能需要优化查询,但它仍然会更快)。

                【讨论】:

                  【解决方案16】:

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

                  【讨论】:

                  • 无论如何,大多数数据库引擎都会在幕后为您执行此操作;例如在 MySQL 中,您可以创建一个纯内存表(MEMORY 引擎)。在没有数据库的情况下重新实现数据库功能通常是 NIH 严重案例的标志;)
                  • @phoog: Not Invented Here - 换句话说,“我没有想到,所以它不存在”。因此,许多方轮被重新发明。 (是的,有时重新发明轮子很有用,例如,如果您正在制造赛车;重新发明“仅仅因为”不太可能让您获得更好的轮子)
                  • 换句话说,“我没有做到,所以它一定是垃圾”。仅就“我尚未对其进行测试,因此它可能不适合我的目的”而言,这具有一定的真实性,因此请在判断之前对其进行测试。
                  • @Piskvor:不一定,数据库只能使用它运行的系统的内存,而应用程序可以使用应用服务器的内存。换句话说:如果数据库在专用主机上,访问该缓存仍然需要网络带宽并且受到网络延迟的影响,但是应用程序保留的任何缓存都可以以内存访问的低延迟速度进行查询。
                  【解决方案17】:

                  不,不仅连接在数据库代码中的优化比临时 C#/Java 更好;但通常可以应用多种过滤技术,从而产生更好的性能。

                  【讨论】:

                    【解决方案18】:

                    他错了,联接是有能力的程序员使用的。可能有一些有限的情况下,他提出的方法更有效(而且我可能会使用 Documant 数据库),但如果你有足够的数据量,我看不到它。以这个查询为例:

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

                    假设您在 table1 中有 1000 万条记录,在 table2 中有 100 万条记录。假设表 1 中有 900 万条记录满足 where 子句。假设其中只有 15 个也在 table2 中。您可以运行此 sql 语句,如果索引正确,它将花费几毫秒并通过网络返回 15 条记录,其中只有 1 列数据。或者您可以通过网络发送 2 列数据的 1000 万条记录,然后单独发送另外 100 万条带有 1 列数据的记录,并在 Web 服务器上合并它们。

                    当然,您也可以始终将数据库的全部内容保存在 Web 服务器上,如果您拥有的数据量不只微不足道,而且数据还在不断变化,这简直是愚蠢的做法。如果您不需要关系数据库的特性,请不要使用。但是,如果您这样做,请正确使用它。

                    【讨论】:

                      【解决方案19】:

                      在我作为软件开发人员的职业生涯中,我经常听到这种说法。几乎每次都这样说,提出这个主张的人对关系数据库系统、它们的工作方式以及此类系统的使用方式了解不多。

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

                      不要忘记使用JOIN 告诉数据库您希望数据片段相互关联的方式,因此向数据库提供有关您正在尝试的什么的更多信息去做,从而使其能够更好地满足您的需求。

                      所以答案肯定是:不,JOINS根本没有用!

                      【讨论】:

                        【解决方案20】:

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

                        【讨论】:

                          【解决方案21】:

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

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

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

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

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

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

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

                          三思而后行:

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

                          【讨论】:

                            猜你喜欢
                            • 2016-04-29
                            • 2015-09-06
                            • 2018-01-28
                            • 1970-01-01
                            • 1970-01-01
                            • 1970-01-01
                            • 2017-01-21
                            • 1970-01-01
                            • 1970-01-01
                            相关资源
                            最近更新 更多