SQL研究 - 查询语句被处理的过程

我们向数据库提交了一个查询,假如我们的语句是正确的,我们理所当然的期望的到一个正确的结果。

 

如果要牛角尖一下,怎样才是一个正确的查询呢?

显然首先你查询的对象是存在的,不管是表还是视图,函数或者存储过程。你指定的条件是合法,不管是Jion的条件,Where中的条件,还是Having中的条件。你希望展现的列是存在的,如果一个表里存放的机器的话,你不大可能从里面选出一个性别的字段。

那么我再问你,如果你的查询要1天才能完成,而稍作改动就可以在1分钟内完成,你的查询还是正确的吗?

如果你查询让数据库引擎占用的内存暴涨,让服务器几乎陷于停顿,而这个查询本可以让1000个人同时使用而畅通无阻,你的查询还是正确的吗?

 

可见要写出一个正确的查询,绝不仅仅需要知道SQL的语法,还需要了解这个查询语句是如何被处理,如何从硬盘上那一堆0或1中组合成我们需要的结果的。本文就试着回答这个问题。

 

首先让我们来看一个完整的SQL语句,它包含所有可能的元素:

 

(8)  SELECT (9) DISTINCT (11) <TOP_specification> <select_list>
(1)  FROM <left_table>
(3)    <join_type> JOIN <right_table>
(2)      ON <join_condition>
(4)  WHERE <where_condition>
(5)  GROUP BY <group_by_list>
(6)  WITH {CUBE | ROLLUP}
(7)  HAVING <having_condition>
(10) ORDER BY <order_by_list>

 

请注意那些写在括号中的数字,它们代表的是各个部分被处理的顺序,每一步生成一个虚拟表,该表作为下一步的输入并生成一个新的虚拟表。

做一个简单的说明把:

首先,From后的两个表做笛卡尔积,插入VT1中;

接着,使用On的条件,将VT1中符合的行放入VT2中;

然后,将VT2中符合Where条件的放入VT3中;

随后,Group by 将VT3分组放入VT4中;

With Cube|RollUp进一步产生分组的方法,新的行和VT4加入VT5中;

后面的您就可以明白了。

 

虽然所有的SQL都遵循这一个过程,过程的每一步的代价是不同的, 甚至访问表的顺序也会导致性能的巨大差异。作为数据库引擎中最重要的组件,查询优化器的功能便是为查询语句找到一个最优的执行计划。

查询优化器有两个输入,一个是我们的查询语句,另一个是存放在数据引擎的统计信息,例如表格有多少行,索引树有多深,每个索引也多少键值等。基于这两个输入,它输出的查询计划包括源表被访问的顺序,以及从源表中提取数据的方法,可以是扫描表的所有Page,也可以是查找索引树。

关于执行计划您可以通过Management Studio的查询窗口上右键菜单:Include actual execution plan 来查看。它显示的执行计划包含了SQL被分解成的所有操作,以及各个操作的代价。我们在此不详细讨论这些操作。

 

正如上面所讲的,翻译查询计划并不是一个容易的工作,尤其是在面对复杂的查询的时候,因为要比较众多的可能路径的代价。我们显然不希望这样一个宝贵的执行计划被随便的抹掉,这样复杂的过程不必要的重复。

 

数据库引擎也不愿意,所以它用一块专门的内容缓冲区来存放执行计划。

 

在数据库引擎里,执行计划由QueryPlan和执行上下文组成。QueryPlan都是一个只读的,可重入的数据结构,并且从来不会超过两份,一份是顺序执行,另一份是并行执行。执行上下文存放专用于一次调用的内容,例如参数,用户权限等。

执行计划在缓冲区中并不会一直存在,它有一个参数表示它的生命值。该值在执行计划每次被调用时就会增加,每一个脉冲,生命值减少1.因此产生该计划的代价越大,它留在在缓冲区的时间就越长。

 

当数据库执行一个查询语句,为之生成执行计划之前,它会首先扫描存储过程的缓冲区,以寻找匹配的执行计划。在匹配执行计划时,可能采用全字符匹配,也可能采用简单参数化的方法。如果用户将数据库的Parameterization选项设置成Forced的话,执行计划的源查询语句会被全面参数化,即所有常数会被替换成占位符,这个占位符可以和任何其他常数匹配,这样就会导致更多的查询语句被匹配到该查询计划。

 

如果数据库服务器是多CPU的,数据库引擎还会产生并行执行计划。一类特殊的操作:Exchange负责处理并发的相关操作,成员包括Distribute Streams, Repartition Streams以及Gather Streams。查询计划的并发度受查询语句本身和查询对象属性的影响。通常来说系统闲散线程越多,CPU越多,查询对象行数越多,并发可能性就越大,并发度也越高。另外我们可以配置并发查询的起始值和并发查询的最大并发度。有时候并发度太高就意味着占用更多的资源,着在多用户环境中并不总是友好的。

 

如果查询的数据分布在不同的数据库服务器上,我们需要借助于连接装置进行访问,最简单的连接方式是Linked Server,如下所示:

SELECT Title, HireDate
   FROM DeptSQLSrvr.AdventureWorks.HumanResources.Employee

或者通过OpenRowSetOpenDataSource

SELECT *
FROM OPENROWSET('Microsoft.Jet.OLEDB.4.0',
        'c:/MSOffice/Access/Samples/Northwind.mdb';'Admin';'';
        Employees)

前者适用于较经常的访问,后者适用于不那么频繁的访问。事实上,SQL Server查理查询的两个关键组件是查询解析和存储管理,她们之间使用OLE  DB来连接。当访问远程Server的数据时,本地的解析器将查询分解成具体的操作,通过OLE DB调用远端server的存储管理组件,后者处理这些操作并返回结果。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值