当前位置: 首页 > 面试题库 >

WHERE子句中的字段顺序是否会影响MySQL的性能?

陈俊誉
2023-03-14
问题内容

我在表中有两个索引字段- typeuserid(单个索引,不是复合索引)。

type的字段值非常有限(假设它仅为0或1),因此50%的表记录具有相同的typeuserid另一方面,值来自更大的集合,因此具有相同userid值的记录量很小。

这些查询中的任何一个都会比另一个查询运行得更快吗?

select * from table where type=1 and userid=5
select * from table where userid=5 and type=1

另外,如果两个字段都没有索引,是否会改变行为?


问题答案:

SQL被设计为一种声明性语言,而不是一种过程性语言。因此,查询优化器应该 不会 考虑在决定如何应用它们的where子句谓词的顺序。

我可能会过分简化SQL查询优化程序的以下讨论。一年前,我按照这些思路写书(这真是太有趣了!)。如果您真的想研究现代查询优化,请参阅O’Reilly的Dan
Tow的SQL Tuning。

在简单的SQL查询优化器中,首先将SQL语句编译为 关系代数 运算的树。这些操作每个都将一个或多个表作为输入,并产生另一个表作为输出。 扫描
是从数据库中读取表的顺序扫描。 排序 产生一个排序表。 Select 产生一个表,该表根据某些选择条件从另一个表中选择行。 Project
生成一个表,该表仅包含另一个表的某些列。 Cross Product使用 两个表并生成一个输出表,该输出表由它们的行的每个可能的配对组成。

令人困惑的是,SQL SELECT子句被编译成关系代数 Project ,而WHERE子句变成了关系代数 Select 。FROM子句变成一个或多个
Joins ,每个 Joins 引入两个表并产生一个表。还有其他关系代数运算,涉及集合并集,交集,差和隶属关系,但让我们保持简单。

这棵树确实需要优化。例如,如果您有:

select E.name, D.name 
from Employee E, Department D 
where E.id = 123456 and E.dept_id = D.dept_id

在500个部门中有5,000名员工,执行未优化的树将盲目地产生一个雇员和一个部门( 交叉产品 )的所有可能组合,然后仅 选择 所需的一个组合。该
扫描 员工会产生一个5000记录表中, 扫描 系将产生500记录表中, 跨产品 这两个表会产生250万的记录表,并 选择
在E.id将采取2500000记录表和丢弃所有记录,只剩下一个。

[当然,真正的查询处理器将尽量不要在内存中实现所有这些中间表。]

因此,查询优化器遍历整个树并应用各种优化。一种是将每个 Select 分解为 Selects 链,每个 Select
的顶级条件分别与一个条件和-ed一起。(这被称为“合取范式”。)然后,将较小的单个 Selects
在树中四处移动,并与其他关系代数运算合并以形成更有效的运算。

在上面的示例中,优化程序首先将E.id = 123456上的 Select 向下推到昂贵的 Cross Product 操作以下。这意味着“
交叉产品” 仅产生500行(该员工和一个部门的每个组合一个)。然后,顶级 Select for E.dept_id =
D.dept_id筛选出499个不需要的行。不错。

如果在Employee的id字段上有一个索引,则优化器可以将Employee 的 Scan 与E.id = 123456上的 Select
结合起来以形成快速索引 Lookup 。这意味着从磁盘将只有一个Employee行读入内存,而不是5,000行。事情正在好转。

最后一个主要的优化是采取 选择 上E.dept_id = D.dept_id和与它相结合 的交叉产品 。这将其转换为关系代数的 等值
运算。它本身并不能做很多事情。但是,如果在Department.dept_id上有一个索引,则可以将提供 Equijoin 的Department
的较低级别顺序 Scan 转换为对我们一个员工的Department记录的快速索引 查找 。 ____

较少的优化涉及降低 项目 运营。如果查询的顶层只需要E.name和D.name,而条件需要E.id,E.dept_id和D.dept_id,则
Scan 操作不必与其他所有表一起构建中间表列,在查询执行期间节省空间。我们已经将一个非常慢的查询变成了两个索引查询,而没有其他太多的查询了。

进一步了解原始问题,假设您已经:

select E.name 
from Employee E 
where E.age > 21 and E.state = 'Delaware'

未优化的关系代数树在执行时将扫描5,000名员工,并产生例如特拉华州的126名年龄超过21岁的员工。查询优化器还对数据库中的值有一些粗略的了解。它可能知道E.state列包含公司所在的14个州,以及有关E.age分布的信息。因此,它首先查看是否对任何字段建立索引。如果是E.state,则使用该索引根据其最近计算的统计信息来选择查询处理器怀疑在特拉华州的少数雇员是有意义的。如果只有E.age,查询处理器可能会认为这不值得,因为96%的员工年龄在22岁以上。因此,如果为E.state建立索引,我们的查询处理器将中断
Select 并将E.state =’Delaware’与 Scan 合并,将其转变为效率更高的 Index Scan

假设在此示例中,在E.state和E.age上没有索引。组合的 Select 操作在Employee的顺序“扫描”之后进行。首先 选择
哪个条件会有所不同吗?可能不是很多。查询处理器可能会将它们保留在SQL语句中的原始顺序中,或者可能更复杂一些,并查看预期的费用。从统计数据中,它会再次发现E.state
=“特拉华”条件应具有更高的选择性,因此它将颠倒条件并首先执行该条件,因此只有126 E.age>
21个比较,而不是5,000个。或者它可能意识到字符串相等性比较比整数比较昂贵得多,并且不考虑顺序。

无论如何,这一切都是非常复杂的,您的句法条件顺序几乎不可能有所作为。除非您遇到实际的性能问题并且您的数据库供应商使用条件顺序作为提示,否则我不会担心。



 类似资料:
  • 问题内容: 假设我有一个漫长而昂贵的查询,里面挤满了条件,搜索了大量的行。我还有一个特殊条件,例如公司ID,它将限制需要大量搜索的行数,将其范围从数十万缩小到几十。 我这样做是否会对MySQL性能产生任何影响: 或这个: 问题答案: 不,顺序应该不会有太大的不同。当发现哪些行与条件匹配时,将针对每一行检查整个条件(通过布尔逻辑组合的所有子条件)。 一些智能数据库引擎将尝试猜测条件的哪些部分可以更快

  • 我对一个< code>select有一些奇怪的问题。< code>WHERE子句中的顺序可能会影响结果吗? 这是我的选择: 这将重现这个结果:http://dl.dropbox.com/u/4892450/sqlSelectProblem/select1.PNG 当我使用这个条件时: (不同的顺序) 我得到一个不同的结果(参见 列): http://dl.dropbox.com/u/4892450

  • 如果我们有一个由col、col2、col3分区的配置单元表,那么在对该表执行SELECT时,如果我想在WHERE子句中指定这些列,它们是否必须以相同的顺序出现,以便利用分区?我的意思是,逻辑上是的,但无论我以什么顺序指定它们,HIVE编译器都不能理解它们的实际顺序。它应该能够在内部应用正确的顺序,因为HIVE已经知道正确的顺序是col2、col2、col3,所以它以这种方式使用它。HIVE在我们定

  • 我一直在MariaDB中使用大型和扩展数据库的数据库。数据库由PHP应用程序访问。 我将DB字符集和排序规则设置为utf8mb4和utf8mb4\u unicode\u ci 但并非所有的表都有text(varchar、text等)列。有些表只保存关系,因此所有列都是数字类型(int、bigint等)或日期/时间等。此外,不是文本类型。 在表中,我只保留数字,我是否需要保留Unicode多字节字符

  • 问题内容: 我试图用谷歌搜索,但是没有运气。 我的开关很大,有些情况 显然 比其他情况 更常见 。 因此,我想知道订单是否真正保持原状,并且在“下”之前先测试“上”案例,因此可以更快地进行评估。 我想保留订单,但是如果它影响速度,那么重新排序分支将是一个好主意。 例如: 问题答案: 对switch语句重新排序没有任何效果。 查看Java字节码规范,可以将a编译为a 或一条指令,然后打开a 。A 总

  • 问题内容: 在我的公司中,我们有一个包含各种表的遗留数据库,因此包含许多字段。 许多字段似乎都有从未达到的大限制(例如:)。 是否将字段的最大宽度设置为最大宽度或比通常输入的字段大2到3倍会对性能产生负面影响? 一个应如何在性能与字段长度之间取得平衡?有平衡吗? 问题答案: 这个问题有两个部分: 在VARCHAR上使用NVARCHAR是否会损害性能?是的,将数据存储在unicode字段中会使存储需