当前位置: 首页 > 科技观察

多次轮回一个数据库SQL查询

时间:2023-03-18 17:36:06 科技观察

,将检测到的数据返回给客户端。但实际上,SQL是在幕后进行了转换和优化,经过多次“麻烦”才取回结果。如上图所示,我们可以看到它从查询处理器通过解析器和优化器进入执行引擎。今天我们先来看查询管理器,然后我们将重点关注查询优化器是如何仔细计算的。查询管理器这部分是数据库功能的体现。在这一部分中,您将编写糟糕的查询转换为快速执行的代码,执行它,并将结果返回给客户端。这个过程会包括多个步骤:首先分析查询是否合法,然后重写查询,去掉无用的运算符,做一些预优化来优化查询提高性能,将查询转化为执行和数据访问计划在编译查询计划执行部分,后面两点我们就不多说了,相对来说不是那么关键。QueryParser每个SQL语句都会经过解析器检查语法是否正确。如果你犯了错误,解析器将拒绝查询。比如你写错了,把SELECT写成了SLECT,就到这里为止了。此外,检查关键字的顺序是否正确。然后,查询SQL中的表名和列名也会被解析,解析器会通过数据库的元数据检查以下内容:表是否存在?表中对应的查询字段是否存在?相应的运算符是否在指定的列上工作?(比如你不能比较一个数字和一个字符串,你不能用substring来比较一个整数。)之后,你会检查你是否有权限在查询中读取或写入相应的表。毕竟,这些访问权限是由DBA分配的。在解析过程中,查询SQL会被转换成数据库的内部表示(通常是树)。如果一切正常,这个转换后的内容将被发送到查询“rewriter”。操作帮助优化器找到最佳计划。重写器对查询执行一组已知的规则。如果查询匹配规则的模式,则应用规则来重写查询。以下是(可选)规则:视图合并:如果在查询中使用视图,视图将与视图的SQL代码一起转换。子查询绑定:带有子查询的查询很难优化,因此重写者会尝试修改查询,甚至删除子查询。例如,SELECTPERSON.*FROMPERSONWHEREPERSON.person_keyIN(SELECTMAILS.person_keyFROMMAILSWHEREMAILS.mailLIKE'christophe%');将被此SQLSELECTPERSON替换。*FROMPERSON,MAILSWHEREPERSON.person_key=MAILS.person_keyandMAILS.mailLIKE'christophe%';去除无用的操作符:如果你使用了DISTINCT,但是你已经有了一个UNIQUE约束来确保数据是唯一的,那么DISTINCT关键字将被删除。消除冗余联接:如果您有两次相同的联接条件,因为其中一个从视图中隐藏,或者由于传递性而导致无用的联接,请将其删除。连续算术求值:如果query是需要计算的东西,rewrite时会计算一次。例如,将WHEREAGE>10+2转换为WHEREAGE>12,然后将TODATE("date")转换为datetime格式的日期(高级)分区修复:如果使用分区表,重写器可以找出要使用的分区.(高级)物化视图重写:如果已经存在与查询子集匹配的物化视图,则重写器检查视图是否是最新的并修改查询以使用物化视图而不是原始表。(高级)自定义规则:如果您为重写查询创建自定义规则,重写器将执行这些规则。(Advanced)OlapTransformations:Analytical/WindowFunctions,StarJoins,Rollups...也将被转换(但这是由重写器还是优化器完成取决于数据库,因为这两个过程是相邻的)。这个重写的查询被发送到查询优化器,有趣的事情来了。统计数据在我们了解数据库如何优化查询之前,我们需要先谈谈统计数据,因为没有统计数据,数据库就是哑巴。如果你不告诉数据库分析它自己的数据,它就不会去做并且会做出错误的假设。数据库需要什么信息?下面说说数据库和操作系统是如何存储数据的。他们使用的最小单位称为页或块(默认为4或8KB)。也就是说,如果你只需要1KB,它也会占用一页。如果页面占用8KB,则浪费了7KB。回到统计,当你要求数据库获取统计信息时,它会计算这些内容:表中的行数或页数表中每一列的单独数据内容数据的长度(最小值,最大值,average)dataIntervalinformation(minimum,maximum,average)Tableindexinformation这些统计信息将帮助优化器更好地估计查询中的磁盘I/O、CPU和内存使用情况。每列的统计数据都很重要。例如,PERSON表需要连接LAST_NAME和FIRST_NAME两列。通过统计,数据库可以知道FIRST_NAME列有多少个不同的值,LAST_NAME有多少个不同的值。所以数据库会使用LAST_NAME,FIRST_NAME来连接,而不是FIRST_NAME,LAST_NAME,因为LAST_NAME不太可能相同,产生的数据会更少。在大多数情况下,要比较LAST_NAME的数据库的前两个或三个字符就足够了。当然,这些都是基本的统计,你也可以让数据库计算更高级的统计,比如直方图。最常使用的值、质量等,通过这些附加信息,可以帮助数据库找到更高效的查询计划,尤其是等值查询和范围查询等。因为数据库已经知道这种情况下有多少条记录。这些统计数据记录在数据库的元数据中。所以持续更新需要时间。这就是为什么它不会在大多数数据库中自动更新的原因。后面的文章会描述查询优化器的一些细节。阅读完这部分之后,扩展阅读:关于基于成本的优化的初步研究论文(1979年):关系数据库管理系统中的访问路径选择。这篇文章只有12页,以计算机科学的平均水平可以理解。这里有一个关于DB29.X如何优化查询的非常好的和深入的演示这里有一个关于PostgreSQL如何优化查询的很好的演示。它是最易访问的文档,因为它更多地是关于“让我们看看PostgreSQL在这些情况下给出的查询计划”的介绍,而不是“让我们看看PostgreSQL使用的算法”。关于优化的官方SQLite文档。它“容易”阅读,因为SQLite使用简单的规则。此外,它是唯一真正解释其工作原理的官方文档。关于SQLServer2005如何优化查询的一个很好的演示,这里是关于Oracl优化的白皮书e12c这里有2门关于查询优化的理论课程,来自“数据库系统概念”一书的作者。一本关注磁盘I/O成本的好书,但需要良好的CS水平。我发现另一门理论课程更容易理解,但它只关注连接运算符和磁盘I/O。