单个查询是否比连接更快?

Mar*_*tin 46 performance join tuning application-design

概念性问题:单个查询是否比连接更快,或者:我应该尝试将客户端所需的所有信息都压缩到一个SELECT 语句中,还是只使用看起来方便的尽可能多的信息?

TL;DR:如果我的联合查询比运行单个查询花费的时间更长,这是我的错还是可以预料的?

首先,我不是很精通数据库,所以可能只是我,但我注意到当我必须从多个表中获取信息时,通过对单个表的多个查询来获取这些信息“通常”更快(也许包含一个简单的内部连接)并在客户端将数据拼凑在一起,以尝试编写一个(复杂的)连接查询,我可以在一个查询中获取所有数据。

我试图把一个非常简单的例子放在一起:

SQL小提琴

架构设置

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);
Run Code Online (Sandbox Code Playgroud)

查询一

select NAME from MASTER
where ID = 1
Run Code Online (Sandbox Code Playgroud)

结果

| NAME |
--------
|  One |
Run Code Online (Sandbox Code Playgroud)

查询乙

select ID, VALUE from DATA
where MASTER_ID = 1
Run Code Online (Sandbox Code Playgroud)

结果

| ID | VALUE |
--------------
|  1 |   1.3 |
|  2 |   1.5 |
|  3 |   1.7 |
Run Code Online (Sandbox Code Playgroud)

查询 C

select M.NAME, D.ID, D.VALUE 
from MASTER M INNER JOIN DATA D ON M.ID=D.MASTER_ID
where M.ID = 1
Run Code Online (Sandbox Code Playgroud)

结果

| NAME | ID | VALUE |
---------------------
|  One |  1 |   1.3 |
|  One |  2 |   1.5 |
|  One |  3 |   1.7 |
Run Code Online (Sandbox Code Playgroud)

当然,我没有用这些来衡量任何性能,但人们可能会观察到:

  • 查询 A+B 返回与查询 C 相同数量的可用信息。
  • A+B 必须向客户端返回 1+2x3==7 个“数据单元”
  • C 必须向客户端返回 3x3==9 "Data Cells",因为通过连接,我自然会在结果集中包含一些冗余。

从这个概括(尽可能牵强):

与接收相同信息量的单个查询相比,连接查询必须返回更多的数据。由于数据库必须将数据拼凑在一起,对于大型数据集,可以假设数据库必须在单个联接查询上做比在单个查询上更多的工作,因为(至少)它必须向客户端返回更多数据。

是否会由此得出结论,当我观察到将客户端查询拆分为多个查询会产生更好的性能时,这只是要走的路,还是说我搞砸了连接的查询?

Jon*_*gel 47

单个查询是否比连接更快,或者:我应该尝试将客户端所需的所有信息压缩到一个 SELECT 语句中,还是只使用看起来方便的尽可能多的信息?

在任何性能场景中,您都必须测试和衡量解决方案以查看哪个更快

也就是说,与将源行返回给客户端然后将它们连接到客户端相比,来自适当调整的数据库的连接结果集几乎总是会更快且扩展性更好。特别是,如果输入集很大而结果集很小——在这两种策略的上下文中考虑以下查询:将两个大小为 5 GB 的表连接在一起,结果集为 100 行。这是一个极端,但你明白我的意思。

我注意到,当我必须从多个表中获取信息时,通过对单个表(可能包含一个简单的内部联接)的多个查询获取此信息并在客户端将数据拼凑在一起以尝试“通常”更快编写一个(复杂的)连接查询,我可以在一个查询中获取所有数据。

很可能可以改进数据库模式或索引以更好地为您抛出的查询提供服务。

与接收相同信息量的单个查询相比,连接查询必须返回更多的数据。

通常情况并非如此。大多数情况下,即使输入集很大,结果集也会远小于输入的总和。

根据应用程序,返回给客户端的非常大的查询结果集是一个直接的危险信号:客户端如何处理如此大的数据集,而这些数据集无法在靠近数据库的地方完成?至少可以说,向用户显示 1,000,000 行是非常值得怀疑的。网络带宽也是一种有限资源。

由于数据库必须将数据拼凑在一起,对于大型数据集,可以假设数据库必须在单个联接查询上做比在单个查询上更多的工作,因为(至少)它必须向客户端返回更多数据。

不必要。如果数据被正确索引,则连接操作更有可能在数据库中更有效地完成,而无需扫描大量数据。此外,关系数据库引擎在低级别进行了特殊优化以进行联接;客户端堆栈不是。

是否会由此得出结论,当我观察到将客户端查询拆分为多个查询会产生更好的性能时,这只是要走的路,还是说我搞砸了连接的查询?

既然你说你在数据库方面缺乏经验,我建议你学习更多关于数据库设计和性能调优的知识。我很确定这就是问题所在。编写效率低下的 SQL 查询也是可能的,但使用一个不太可能成为问题的简单模式。

现在,这并不是说没有其他方法可以提高性能。在某些情况下,如果打算使用某种缓存机制,您可能会选择扫描大中型数据集并将其返回给客户端。缓存可能很棒,但它会在您的设计中引入复杂性。缓存甚至可能不适合您的应用程序。

在任何地方都没有提到的一件事是保持从数据库返回的数据的一致性。如果使用单独的查询,则更有可能(由于许多因素)返回不一致的数据,除非对每组查询使用一种快照隔离形式。


Lei*_*fel 6

当然,我没有用这些来衡量任何性能

你把一些好的示例代码放在一起。您是否查看了 SQL Fiddle 中的计时?即使是一些简短的不科学的性能测试也会表明,演示中的查询 3 与单独运行查询 1 或 2 所需的时间大致相同。组合一和二花费的时间大约是三的两倍,这是在执行任何客户端连接之前。

随着数据的增加,查询一和查询二的速度会有所不同,但数据库连接仍然会更快。

您还应该考虑如果内部联接消除数据会发生什么。