我曾在不同的公司工作过,并且我注意到其中有些人更喜欢将视图与所有“亲戚”一起加入表中。但是,有时在应用程序中,我们只需要使用一列即可。
那么简单地进行简单选择,然后将它们“联接”到系统代码中会更快吗?
系统可能是用php,java,asp或连接数据库的任何语言。
所以问题是,从服务器端(php,java,asp,ruby,python ...)到数据库的传输速度更快?并运行一个查询以获取我们需要的一切信息,或从服务器端访问数据库并运行一次仅从一个表获取列的查询?

评论

您正在使用哪种“ SQL”实现? MySQL,Microsoft SQL Server,Oracle,Postgresql等?请更新您的标签。

Mysql和Postgresql

我的经验是,MySQL不喜欢复杂的查询,通常使用非常简单的查询(甚至更多)会更快。 Postgres的查询优化器要好得多,运行单个大型查询通常效率更高。

@a_horse_with_no_name这是非常广泛的概括,尤其是在此问题的上下文中。 MySQL优化器的设计确实确实非常简单,并且可能导致联接和子查询(特别是在旧版本的MySQL上)出现问题,否则它们会在PostgreSQL中产生更快的计划,而MySQL对于纯OLTP加载可能非常快。但是,在问题的上下文中,单个大型查询的速度会更快,例如,在更糟糕的情况下,在编程循环内进行SELECT(无论使用的是RDBMS)。

@jynus:好,这个问题很广泛(加上:我说“根据我的经验”,其他人可能会有不同的经验)。在LOOP中进行查询从来都不是一个好主意,并且几乎总是由于设计不良或缺乏对如何使用关系数据库的理解而导致的。

#1 楼

解决您的问题的方法是联接分解。
根据本书的第209页,

您可以通过运行多个单表查询而不是多表联接来分解联接,并且然后在应用程序中执行联接。例如,代替以下单个查询:
SELECT * FROM tag
JOIN tag_post ON tag_post.tag_id = tag.id
JOIN post ON tag_post.post_id = post.id
WHERE tag.tag = 'mysql';

您可以运行以下查询:
SELECT * FROM tag WHERE tag = 'mysql';
SELECT * FROM tag_post WHERE tag_id=1234;
SELECT * FROM post WHERE post.id IN (123,456,567,9098,8904);

为什么要这样做?乍一看似乎很浪费,因为您增加了查询数量而没有得到任何回报。但是,这样的重组实际上可以提供显着的性能优势:

缓存可以更高效。许多应用程序缓存直接映射到表的“对象”。在此示例中,如果标记为mysql的对象已被缓存,则应用程序将跳过第一个查询。如果在缓存中找到ID为123、567或908的帖子,则可以将其从IN()列表中删除。查询缓存也可以从该策略中受益。如果只有一个表经常更改,则分解联接可以减少高速缓存失效的次数。
单独执行查询有时可以减少锁争用
在应用程序中执行联接可以更轻松地扩展数据库将表放在不同的服务器上。
查询本身可以提高效率。在此示例中,使用IN()列表而不是联接,MySQL可以对行ID进行排序,并比联接可能获得的查询更优化。
您可以减少冗余的行访问。在应用程序中进行联接意味着只检索每行一次,而查询中的联接本质上是一种非规范化,可能会重复访问相同的数据。出于同样的原因,这种重组也可能会减少总的网络流量和内存使用量。
在某种程度上,您可以将此技术视为手动实现哈希联接,而不是MySQL用于执行联接的嵌套循环算法。哈希联接可能会更有效。

因此,当您缓存和重用早期查询中的大量数据,在多个服务器之间分配数据,用IN()列表替换联接,或者联接多次引用同一张表。
观察
我喜欢第一个要点,因为InnoDB在交叉检查查询缓存时有点笨拙。


Sep 05, 2012:频繁查询缓存失效的开销值得吗?


Jun 07, 2014:为什么默认情况下从MySQL 5.6开始禁用query_cache_type?关于最后一个要点,我在2013年3月11日写了一篇帖子(描述了嵌套循环算法)(JOIN条件和WHERE条件之间有执行差异吗?)。阅读完之后,您将看到联接分解的效果如何。
对于本书中的所有其他要点,开发人员确实将性能作为底线。有些依赖于外部方法(应用程序外部)来提高性能,例如使用快速磁盘,获得更多的CPU /核心,调整存储引擎以及调整配置文件。其他人会妥协并编写更好的代码。有些人可能会在存储过程中对所有商业智能进行编码,但仍不应用联接分解(请参阅反对将逻辑放在数据库层的论点或将逻辑放在其他层的论点)。这完全取决于每个开发人员商店的文化和容忍度。
有些人可能会对性能感到满意,不再接触代码。其他人根本没有意识到,如果他们尝试加入组合,就会有很多好处。
对于那些愿意...的开发人员来说,
尝试一下吧!

评论


至于关于更改为3个查询的链接...我了解并尊重Baron,Vadim和Peter,但我不同意这种误导性建议。多数赞成拆分的论点很少见,值得一提。坚持使用JOIN进行单个查询,然后让我们对其进行改进。

–里克·詹姆斯(Rick James)
16-10-29在19:31

@RickJames我同意您的评论精神。多年来,我看到一些人参加了分解工作,而另一些人却失败了。即使使用适当的SQL技能,如果连接分解未正确完成,它也会对您不利。在我目前的雇主那里,许多部门都喜欢扩大规模和扩大规模,特别是当涉及到遗留代码并且财大气粗时。对于那些有鱼子酱口味但鸡蛋沙拉预算有限的人,加入分解可能值得冒险,但必须正确进行。

– RolandoMySQLDBA
16-10-29在20:11



如果有时间和权利,我很想看看这在Oracle环境中如何工作。

–里克·亨德森(Rick Henderson)
17年7月7日14:34在

另一种更快的方式是,如果您要进行订购,订购一个较小的列表要比订购一个较大的列表总的计算量少。

–埃文·西洛基(Evan Siroky)
18-10-30在17:46

在应用程序代码中手动执行联接比MySQL使用的联接算法更有效?哇,我刚刚失去了尊重配发的MySQL。

–丹·卡特
3月25日22:27

#2 楼

在Postgres(可能还有类似程度的RDBMS,程度较小的MySQL)中,更少的查询几乎总是更快。

解析和计划多个查询的开销已经远远超过了任何可能的收益。在大多数情况下。

不要说要在客户端中完成其他工作,将结果结合起来,这样做通常要慢得多。 RDBMS专门从事这种任务,并且操作基于原始数据类型。请勿强制转换为text并返回中间结果或转换为客户端的本机类型,这甚至可能导致更不正确(或不正确!)的结果。考虑浮点数...

您还可以在DB服务器和客户端之间传输更多数据。对于一堆充满价值的东西来说,这可能是微不足道的,或者起着巨大的作用。

如果多个查询意味着多次往返数据库服务器,那么您还会收集到多次网络延迟和事务开销,这可能甚至连接开销。损失很大。

根据您的设置,仅网络延迟可能比所有其余时间花费几个数量级。

有关SO的相关问题:


哪个更好?在sql或应用程序中执行计算

对于大型,长期运行的查询可能会有一个转折点,因为事务会在途中收集数据库行上的锁。非常大的查询可能会长时间保留许多锁,这可能会导致与并发查询产生冲突。

评论


出于好奇,您认为很大吗?

– Sablefoste
17年1月9日在20:14

@Sablefoste:很大程度上取决于您的访问模式。一个关键点是并发事务开始排队,等待锁释放,或者如果您积累了足够的锁以占用大量资源。或者,如果您的查询运行足够长的时间来干扰自动清理...

–欧文·布兰德斯特
17年1月10日在4:26

但是,如果采取某种典型的情况-使用外部联接并为“父”表返回大量冗余数据的查询,则必须由应用(很可能是一些ORM库)对应用程序进行解析和排序小选择首先获取所有必需的ID,然后再使用IN()而不是外部联接进行另一个小选择?第二种方法是否会更有效(考虑数据库和应用程序消耗的CPU和通信带宽)?

– JustAMartin
17年9月14日在7:47

@JustAMartin:这听起来像是一种查询,如果由RDBMS的查询计划者处理,则几乎可以肯定它会更快-假设查询正确。关于为“父”表返回大量冗余数据:为什么要返回冗余数据?只返回您需要的数据。

–欧文·布兰德斯特
18年1月10日在13:35

通过外部联接,RDBMS从父表返回的数据对于每个联接的子代都是重复的,这意味着一些网络和内存开销,然后在ORM工具中进行一些额外的解析以丢弃重复的父代值,并仅保留一个具有n个子代的父代。因此,通过单个查询,我们可以节省RDBMS查询计划程序的有效工作,减少网络(或本地管道)请求,但会丢失其他不必要的有效负载并在ORM库中转移数据。我想,这和往常一样-在优化之前先进行测量。

– JustAMartin
18年1月10日在14:23



#3 楼

我不知道在(大多数)SQL版本中是否可行,我只真正知道事务处理SQL(Microsoft)

性能损失主要是通过将所有内容首先结合在一起而不是最后通过过滤来实现的。 where子句。

如何将where集成到join ON语句中,如果您有多个联接,请对它们进行智能排序,以便在第一个联接中进行最多的过滤,以减少在后续联接中使用的行。

像:

SELECT * FROM tag
JOIN tag_post ON tag.tag = 'mysql' AND tag_post.tag_id = tag.id 
JOIN post ON tag_post.post_id = post.id


在t-sql中,这可以大大提高查询速度,因为tag_post中的大多数行都被过滤掉了。联接的第一步(假设您有许多不同的标记)。

任何人都可以发表评论,如果这在其他sql方言中可能实现,以及是否像执行多个查询一样高效,或者多个查询仍然具有性能提升?

评论


与将tag ='mysql'条件放入WHERE子句相比,我非常怀疑这是否会更改任何内容。毫无疑问,这不会通过像样的优化程序来提高“ T-SQL”(= SQL Server)或任何其他现代数据库的速度。如果这确实在MySQL中运行得更快,那么我认为这是他们的查询优化器的主要缺陷。

– a_horse_with_no_name
4月29日12:06