首先,我对数据库不是很精明,所以可能只是我,但是注意,当我必须从多个表中获取信息时,通过对单个表(可能包含一个简单的内部联接)进行多次查询并在客户端将这些数据修补在一起以尝试写入这些数据,通常“更快”一个(复杂的)联合查询,可以在一个查询中获取所有数据。
我试图将一个非常简单的示例放在一起:
SQL Fiddle
架构设置:
CREATE TABLE MASTER
( ID INT NOT NULL
, NAME VARCHAR2(42 CHAR) NOT NULL
, CONSTRAINT PK_MASTER PRIMARY KEY (ID)
);
CREATE TABLE DATA
( ID INT NOT NULL
, MASTER_ID INT NOT NULL
, VALUE NUMBER
, CONSTRAINT PK_DATA PRIMARY KEY (ID)
, CONSTRAINT FK_DATA_MASTER FOREIGN KEY (MASTER_ID) REFERENCES MASTER (ID)
);
INSERT INTO MASTER values (1, 'One');
INSERT INTO MASTER values (2, 'Two');
INSERT INTO MASTER values (3, 'Three');
CREATE SEQUENCE SEQ_DATA_ID;
INSERT INTO DATA values (SEQ_DATA_ID.NEXTVAL, 1, 1.3);
INSERT INTO DATA values (SEQ_DATA_ID.NEXTVAL, 1, 1.5);
INSERT INTO DATA values (SEQ_DATA_ID.NEXTVAL, 1, 1.7);
INSERT INTO DATA values (SEQ_DATA_ID.NEXTVAL, 2, 2.3);
INSERT INTO DATA values (SEQ_DATA_ID.NEXTVAL, 3, 3.14);
INSERT INTO DATA values (SEQ_DATA_ID.NEXTVAL, 3, 3.7);
查询A:
select NAME from MASTER
where ID = 1
结果:
| NAME |
--------
| One |
查询B:
q4312078 q
结果:
select ID, VALUE from DATA
where MASTER_ID = 1
查询C:
| ID | VALUE |
--------------
| 1 | 1.3 |
| 2 | 1.5 |
| 3 | 1.7 |
结果:
select M.NAME, D.ID, D.VALUE
from MASTER M INNER JOIN DATA D ON M.ID=D.MASTER_ID
where M.ID = 1
当然,我没有用这些来衡量任何性能,但是可以观察到:
查询A + B返回与查询C相同数量的可用信息。
A + B必须向客户端返回1 + 2x3 == 7“数据单元”
C必须返回3x3 == 9“数据单元” ”,因为有了连接,我自然会在结果集中包括一些冗余。与接收相同信息量的单个查询相比,总是必须返回更多的数据。由于数据库必须将数据拼凑在一起,因此对于大型数据集,可以假设数据库在单个联接的查询上比在单个查询上要做的工作更多,因为(至少)它必须将更多的数据返回给客户端。
会因此得出结论,当我观察到将客户端查询拆分为多个查询会产生更好的性能时,这只是解决之道,还是意味着我搞砸了联接查询? />
#1 楼
单个查询的速度是否比联接的速度快?或者:我应该尝试将客户端上想要的每个信息都压缩到一个SELECT语句中,还是只使用似乎方便的数量?
在任何性能场景中,您都必须测试和衡量解决方案,以查看哪种解决方案更快。扩展比将源行返回给客户端然后在其中加入它们更好。特别是,如果输入集很大而结果集很小,则在两种策略的上下文中考虑以下查询:将两个表(每个表大小均为5 GB)连接在一起,结果集为100行。这是一个极端,但是您明白我的意思。单个表(可能包含一个简单的内部联接)并在客户端将数据修补在一起,以尝试编写一个(复杂的)联接查询,在此我可以在一个查询中获取所有数据。 br />很可能可以改进数据库模式或索引以更好地服务于您向其抛出的查询。接收相同信息量的单个查询。
通常情况并非如此。大多数情况下,即使输入集很大,结果集也会比输入总和小得多。
根据应用程序的不同,返回给客户端的查询结果集非常大。即时的危险信号:客户端如何处理如此庞大的数据集,而这些数据又无法在数据库附近完成?至少可以说,向用户显示1,000,000行。网络带宽也是一种有限的资源。
由于数据库必须将数据拼凑在一起,因此对于大型数据集,可以假定数据库在单个联接的查询上比在单个查询上要做的工作更多,因为(至少)它必须将更多的数据返回给客户端。
不一定。如果正确索引了数据,则连接数据库更有可能在数据库中更有效地完成,而无需扫描大量数据。而且,关系数据库引擎在底层进行了特别优化,以便进行连接;客户端堆栈不是。
由此产生的结果是,当我观察到将客户端查询拆分为多个查询会产生更好的性能时,这只是方法,或者
既然您说您对数据库没有经验,我建议您学习更多有关数据库设计和性能调优的信息。我很确定这就是问题所在。编写效率低下的SQL查询也是可能的,但是使用一个简单的模式就不太可能成为问题。在某些情况下,如果打算使用某种缓存机制,则可以选择扫描中型到大型数据集并将其返回给客户端。缓存可能很棒,但是却在设计中引入了复杂性。缓存甚至可能不适合您的应用程序。
尚未在任何地方提及的一件事是保持从数据库返回的数据的一致性。如果使用单独的查询,则很可能(由于许多因素)返回的数据不一致,除非每组查询都使用快照隔离形式。
评论
OP表示JOINed数据结果集总是更大。 >联合查询总是必须返回比单个查询更多的数据。我认为这在客观上是正确的(对于> =),例如结果集的大小各不相同,因此在线上的数据更多。你有一个不正确的例子吗?如果我加入Authors-> Posts and Authors有一个名为“ biography”的字段,即1MB JSON字段,对于100个帖子的作者,我将通过有线方式传输100MB与1MB。错了吗
– Hymomo
19年6月25日在12:01
#2 楼
当然,我没有用这些来衡量任何性能。
您组合了一些不错的示例代码。您是否看过SQL Fiddle中的计时?甚至一些简短的不科学的性能测试都将表明,演示中的查询三所花费的时间与分别查询一两个人所需的时间差不多。 1和2的组合大约是3的两倍,而这是在执行任何客户端联接之前的两倍。仍然会更快。
您还应该考虑如果内部联接消除数据会发生什么。
#3 楼
也应该考虑查询优化器。它的作用是采用声明性SQL并将其转换为过程步骤。为了找到程序步骤的最有效组合,它将检查索引使用,排序,缓存中间结果集以及各种其他内容的组合。即使看起来很简单的查询,排列的数量也可能变得非常大。为了找到最佳计划而进行的许多计算都是由表中数据的分布决定的。这些分布被采样并存储为统计对象。如果这些是错误的,它们将导致优化器做出错误的选择。计划初期的选择不当会导致后来的选择更差,从而造成滚雪球效应。
返回中等数量的数据需要花费几分钟才能运行的中等大小的查询并不少见。正确的索引编制和良好的统计信息将其减少到毫秒。
#4 楼
多个查询是解决之道。如果您处理类似的简单情况,那么查询优化器的成本开销就是一个因素。有了更多的数据,联接(冗余行)的网络效率就会降低。只有大量的数据才有效率。 DBA总是说“不,请加入”,但现实是:在这种情况下,进行多个简单选择会更快。评论
联接中没有“网络效率低下”-一切都发生在数据库服务器上,因此不涉及网络(除非您通过数据库链接联接!)
–克里斯·萨克森(Chris Saxon)
13年5月24日在15:11
您可能要考虑网络层是否具有压缩功能。 Oracle的SQL * Net可以有效压缩同一列中重复的值。
–大卫·奥尔德里奇(David Aldridge)
13年5月24日在16:24
@TomTom您可能有一个要点(正如David Aldridge所说,压缩很重要),但是您的措辞令人困惑。 “网络连接效率低下”?确实,请解决此问题,以使您明白所要表达的意思。
–超立方体ᵀᴹ
13年5月24日在16:39
@ChrisSaxon肯定有,图像中有用于报告“ title-> base-> table-rows”的表,并且需要所有行,因此您可以内部连接这3个表。每个表都有长的varchars,因此对于重复这些长的varchars的每一行会发生什么。应用程序层需要为所有这些字符串分配内存,然后为模型分组它们。所以我认为这就是他的意思,发送了更多数据
– MIKE
18年6月1日在4:02
@MIKE取决于您选择的表达式,而不取决于联接。并且可能存在网络压缩。在Oracle数据库中,SQL * Net删除重复的重复值nicetheory.io/2018/01/11/…
–克里斯·萨克森(Chris Saxon)
18年6月3日在17:01
评论
评论不作进一步讨论;此对话已移至聊天。我运行了一个基准,并将结果发布在Medium上的一篇文章中。我会在这里添加一个答案,但已经在另一个问题上做到了,对多个问题发布相同的答案就不那么容易了。