JOIN查询比几个查询更快吗? (您运行主查询,然后根据主查询的结果运行许多其他SELECT)
我问,因为加入它们会使我的应用程序的设计变得复杂化
如果它们更快,那么任何人都可以近似粗略估计多少?如果它是1.5倍我不在乎,但如果它是10倍我想我会这样做。
答案 0 :(得分:83)
对于内部联接,单个查询是有意义的,因为您只获得匹配的行。 对于左连接,多个查询要好得多......看看我做的以下基准:
包含5个联接的单个查询
查询: 8.074508秒
结果大小:2268000
连续5次查询
综合查询时间: 0.00262秒
结果大小:165(6 + 50 + 7 + 12 + 90)
请注意,我们在两种情况下都得到相同的结果(6 x 50 x 7 x 12 x 90 = 2268000)
左连接使用指数多的内存和冗余数据。
如果你只做两个表的连接,内存限制可能不会那么糟糕,但通常是三个或更多,并且它变得值得不同的查询。
作为旁注,我的MySQL服务器就在我的应用服务器旁边......所以连接时间可以忽略不计。如果你的连接时间是几秒钟,那么可能有一个好处
谢
答案 1 :(得分:69)
这太模糊了,无法给出与您的具体案例相关的答案。这取决于很多事情。 Jeff Atwood(本网站的创始人)实际上是wrote about this。但是,在大多数情况下,如果你有正确的索引并且你正确地进行了JOIN,那么一次旅行通常会比几次更快。
答案 2 :(得分:20)
我实际上是在回答这个问题时自己寻找答案,在阅读了给定的答案后,我只能同意比较数据库查询性能的最佳方法是获得真实世界的数字,因为有很多变量需要考虑到但是我也认为比较它们之间的数字几乎在所有情况下都没有好处。我的意思是这些数字应该总是与可接受的数字进行比较,绝对不能相互比较。
我可以理解,如果一种查询方式需要0.02秒而另一种需要20秒,这是一个巨大的差异。但是,如果一种查询方式需要0.0000000002秒,而另一种方法需要0.0000002秒呢?在这两种情况下,一种方式比另一种方式快1000倍,但在第二种情况下真的仍然“高喊”?
我亲眼看到的底线:如果表现良好,请选择简单的解决方案。
答案 3 :(得分:13)
快速测试是从50,000行表中选择一行并从100,000行表中连接一行。基本上看起来像:
$id = mt_rand(1, 50000);
$row = $db->fetchOne("SELECT * FROM table1 WHERE id = " . $id);
$row = $db->fetchOne("SELECT * FROM table2 WHERE other_id = " . $row['other_id']);
VS
$id = mt_rand(1, 50000);
$db->fetchOne("SELECT table1.*, table2.*
FROM table1
LEFT JOIN table1.other_id = table2.other_id
WHERE table1.id = " . $id);
对于50,000次读取,两种选择方法花费了3.7秒,而在家用慢速计算机上,JOIN花费了2.0秒。 INNER JOIN和LEFT JOIN并没有什么区别。获取多行(例如,使用IN SET)产生类似的结果。
答案 4 :(得分:8)
构建单独的查询和连接,然后为每个查询和连接计时 - 没有什么比现实世界的数字更有帮助。
然后更好 - 在每个查询的开头添加“EXPLAIN”。这将告诉您MySQL用于回答数据请求的子查询数,以及每次查询扫描的行数。
答案 5 :(得分:7)
根据与开发人员复杂性相比的数据库的复杂性,执行许多SELECT调用可能更简单。
尝试针对JOIN和多个SELECTS运行一些数据库统计信息。看看你的环境中JOIN是否比SELECT更快/更慢。
然后再次,如果将其更改为JOIN将意味着额外的一天/周/月的开发工作,我会坚持使用多个SELECT
干杯,
BLT
答案 6 :(得分:6)
真正的问题是:这些记录是否具有一对一关系或一对多关系? < / p>
TLDR答案:
如果一对一,请使用JOIN
声明。
如果是一对多,请在服务器端代码优化中使用一个(或多个)SELECT
语句。
为什么以及如何使用SELECT进行优化
基于一对多关系的大型记录组的 SELECT
'(使用多个查询而不是连接)会产生最佳效率,因为JOIN
具有指数内存泄漏问题。获取所有数据,然后使用服务器端脚本语言对其进行排序:
SELECT * FROM Address WHERE Personid IN(1,2,3);
结果:
Address.id : 1 // First person and their address
Address.Personid : 1
Address.City : "Boston"
Address.id : 2 // First person's second address
Address.Personid : 1
Address.City : "New York"
Address.id : 3 // Second person's address
Address.Personid : 2
Address.City : "Barcelona"
在这里,我在一个选择语句中获取所有记录。这比JOIN
更好,<?php
foreach($addresses as $address) {
$persons[$address['Personid']]->Address[] = $address;
}
?>
可以将一小组这些记录一次一个地作为另一个查询的子组件。然后我用服务器端代码解析它,看起来像......
JOIN
何时不使用JOIN进行优化
SELECT
基于与单个记录的一对一关系的大量记录与多个JOIN
语句相比,产生最佳效率,一个接一个,只需获得下一个记录类型。
但是当获得具有一对多关系的记录时,SELECT * from BlogPost
LEFT JOIN Tag ON Tag.BlogPostid = BlogPost.id
LEFT JOIN Comment ON Comment.BlogPostid = BlogPost.id;
效率很低。
示例:数据库博客有3个感兴趣的表,Blogpost,Tag和Comment。
Row1: tag1, comment1,
Row2: tag1, comment2,
Row3: tag2, comment1,
Row4: tag2, comment2,
如果有1个博文,2个标签和2条评论,您将获得如下结果:
Row1: tag1, comment1,
Row2: tag1, comment2,
Row3: tag1, comment3,
Row4: tag1, comment4,
Row5: tag2, comment1,
Row6: tag2, comment2,
Row7: tag2, comment3,
Row8: tag2, comment4,
Row9: tag3, comment1,
Row10: tag3, comment2,
Row11: tag3, comment3,
Row12: tag3, comment4,
Row13: tag4, comment1,
Row14: tag4, comment2,
Row15: tag4, comment3,
Row16: tag4, comment4,
注意每条记录是如何重复的。好的,2条评论和2个标签是4行。如果我们有4条评论和4条标签怎么办?你没有得到8行 - 你得到16行:
"%WinDir%\Microsoft.NET\Framework64\v4.0.30319\aspnet_regsql.exe" -S server\instance -E -ssadd -sstype p
添加更多表格,更多记录等,问题将迅速膨胀到数百行,这些行都充满 冗余数据。
这些重复的费用是多少?内存(在SQL服务器和尝试删除重复项的代码中)和网络资源(在SQL服务器和代码服务器之间)。
来源:https://dev.mysql.com/doc/refman/8.0/en/nested-join-optimization.html; https://dev.mysql.com/doc/workbench/en/wb-relationship-tools.html
答案 7 :(得分:5)
根据我的经验,我发现运行多个查询通常会更快,特别是在检索大型数据集时。
当从另一个应用程序(例如PHP)与数据库进行交互时,有一个参数可以通过多次访问服务器。
还有其他方法可以限制对服务器的访问次数,并且仍然运行多个查询,这些查询通常不仅更快,而且还使应用程序更易于阅读 - 例如mysqli_multi_query。
对于SQL,我不是新手,我认为开发人员有一种趋势,特别是初级人员花费大量时间尝试编写非常聪明的连接,因为它们看起来很聪明,而实际上有很多提取方法看似简单的数据。
最后一段是个人意见,但我希望这会有所帮助。我确实同意其他人,但是谁说你应该做基准测试。这两种方法都不是银弹。
答案 8 :(得分:4)
这个问题很旧,但是缺少一些基准。我将JOIN与其2个竞争对手进行了基准测试:
WHERE IN(...)
或等效查询结果很明显:在MySQL上,JOIN
的速度快了许多。 N + 1个查询会大大降低应用程序的性能:
也就是说,除非您选择很多记录,这些记录都指向极少数不同的外来记录。这是极端情况的基准:
在典型的应用程序中这是极不可能发生的,除非您加入一对多关系,在这种情况下,外键在另一个表上,并且您要多次复制主表数据。
外卖:
JOIN
有关更多信息,请参见my article on Medium。
答案 9 :(得分:3)
吞吐量方面会更快吗?大概。但它也可能一次锁定更多数据库对象(取决于您的数据库和模式),从而降低并发性。根据我的经验,人们常常误以为“数据库往返次数较少”这一论点,实际上在数据库位于同一局域网的大多数OLTP系统上,真正的瓶颈很少是网络。
答案 10 :(得分:2)
这是一个包含100个有用查询的链接,这些查询在Oracle数据库中进行了测试,但记住SQL是标准,Oracle,MS SQL Server,MySQL和其他数据库之间的区别是SQL方言:
答案 11 :(得分:1)
有几个因素意味着没有二元答案。什么是最佳性能的问题取决于您的环境。顺便说一句,如果您使用标识符的单个选择不是亚秒级,那么您的配置可能有问题。
要问的真正问题是您想如何访问数据。单选选择支持后期绑定。例如,如果您只需要员工信息,则可以从Employees表中进行选择。外键关系可用于在以后根据需要检索相关资源。选择已经有一个指向的键,所以它们应该非常快,你只需要检索你需要的东西。必须始终考虑网络延迟。
联接将立即检索所有数据。如果您要生成报告或填充网格,这可能正是您想要的。在这种情况下,编译和光学连接只会比单个选择更快。请记住,Ad-hoc连接可能不会那么快 - 您应该编译它们(进入存储过程)。速度答案取决于执行计划,该计划详细说明了DBMS检索数据所需的步骤。
答案 12 :(得分:1)
是否应该使用连接首先是关于连接是否有意义。只有在这一点上,性能甚至需要考虑,因为几乎所有其他情况都会导致更差的性能。
性能差异很大程度上与您查询的信息的相关性有关。加入工作,并且当数据相关并且您正确地索引内容时它们很快,但它们通常会导致一些冗余,有时会产生比所需更多的结果。如果你的数据集没有直接相关,那么将它们放在一个查询中就会产生所谓的笛卡尔积(基本上就是所有可能的行组合),这几乎不是你想要的。
这通常是由多对多关系引起的。例如,HoldOffHunger's answer提到了对帖子,标签和评论的单个查询。评论与帖子相关,标签也是如此...但标签与评论无关。
+------------+ +---------+ +---------+
| comment | | post | | tag |
|------------|* 1|---------|1 *|---------|
| post_id |-----| post_id |-----| post_id |
| comment_id | | ... | | tag_id |
| user_id | | | | ... |
| ... | | | | ... |
+------------+ +---------+ +---------+
在这种情况下,至少有两个单独的查询明确地更好。如果您尝试加入标记和注释,因为两者之间没有直接关系,您最终会得到标记和注释的所有可能组合。 many * many == manymany
。除此之外,由于帖子和标签不相关,您可以并行执行这两个查询,从而获得潜在的收益。
让我们考虑一个不同的场景:您希望将评论附加到帖子,以及评论者的联系信息。
+----------+ +------------+ +---------+
| user | | comment | | post |
|----------|1 *|------------|* 1|---------|
| user_id |-----| post_id |-----| post_id |
| username | | user_id | | ... |
| ... | | ... | +---------+
+----------+ +------------+
这是您应该考虑加入的地方。除了是一个更自然的查询之外,大多数数据库系统(包括MySQL)都有很多聪明的人将大量的工作投入到优化查询中。对于单独的查询,由于每个查询都取决于前一个查询的结果,因此查询不能并行完成,并且总时间不仅仅是查询的实际执行时间,还包括获取结果所花费的时间,筛选通过它们获取下一个查询的ID,将行链接在一起等等。
答案 13 :(得分:0)
是的,使用JOINS的一个查询会更快。虽然不知道您要查询的表的关系,数据集的大小或主键的位置,但几乎不可能说出更快的速度。
为什么不测试两种情况,然后你肯定会知道......