最近,我们需要对提高sql查询的性能做一些研究,所以我们研究了sql语句的解析过程。这篇文章是我手机看了各种资料总结出来的。它会一步一步的详细讲述一条sql语句的每个关键字的解析过程。欢迎大家互相学习。
最近,我们需要对提高sql查询的性能做一些研究,所以我们研究了sql语句的求解过程。在园里看完,大家写了很多相关的文章,侧重点不同。这篇文章是我手机看了各种资料总结出来的。它会一步一步的详细讲述一条sql语句的每个关键字的解析过程。欢迎大家互相学习。SQL语句的解析顺序
简单来说,一条sql语句按以下顺序解析:1. FROM FROM后面的表标识了这条语句要查询的数据源。以及一些子句,例如,(1-J1)笛卡尔积,(1-J2)关于滤波,(1-JBOY3乐队)添加外部列,要应用的对象。在FROM过程之后,将生成一个虚拟表VT1。(1-J1)笛卡尔积 此步骤将计算两个相关表的笛卡尔积(交叉连接),并生成虚拟表VT1-J1。(1-J2)ON过滤 这一步基于虚拟表VT1-J1过滤掉所有满足ON谓词条件的列,生成虚拟表VT1-J2。(1-J3)添加外部行 如果使用外部连接,保留表中不满足ON条件的列也将被添加到VT1-J2,并且作为外部行,将生成虚拟表VT1-JBOY3乐队。2. WHERE 过滤VT1过程中生成的临时表,将满足where子句的列插入VT2表中。3. GROUP BY 该子句将根据GROUP BY中的列对VT2中生成的表进行分组。生成VT3表。4. HAVING该子句过滤VT3表中的不同组,满足HAVING条件的子句被添加到VT4表中。5. SELECT此子句处理SELECT子句中的元素,以生成VT5表。(5-1)计算表达式 计算SELECT子句中的表达式,生成VT5-1(5-2)Distinct。在VT5-1中查找重复的列,并删除它们,生成VT5-2(5-3)TOP。从ORDER BY子句中定义的结果中,选择符合条件的列。从VT5-3中的表生成VT5-3表ORDER BY,根据ORDER BY子句的条件对结果进行排序,生成VC6表。
客户,订单的查询例子
首先,创建一个客户表,并插入以下数据:
customerid
城市
FISSA
马德里
FRNDO
马德里
克尔洛斯
马德里
MRPHS
耶路撒冷
创建一个订单表并插入以下数据:
订单id
customerid
一个
FRNDO
2
FRNDO
三
克尔洛斯
四
克尔洛斯
五
克尔洛斯
六
MRPHS
七
空
假设我们想查询订单编号小于3的马德里客户,并显示他们的订单编号。结果按照从小到大的顺序号排序。复制代码如下:从dbo中选择C.customerid,COUNT(O.orderid)作为numorders。客户为c左外联接dbo。orders as o on c . customerid=o . customerid where c . city=' Madrid ' group by c . customerid having count(o . orderid)3 order by numorders查询结果为:
customerid
numorders
FISSA
0
FRNDO
2
我们将详细描述sql如何计算这个结果:FROM子句。
FROM子句标识要查询的表。如果指定了表操作,将从左到右进行处理。基于一个或两个表的每个表操作都将返回一个输出表。左表的输出结果将作为下一次表操作的输入结果。例如,与交叉表相关的操作有(1-J1)笛卡尔积、(1-J2)过滤、(1-JBOY3乐队)添加外部列。FROM语句生成一个虚拟表VT1。Step 1-J1:执行笛卡尔积(CROSS JOIN)
笛卡尔乘积将列出左表和右表的每一行的所有可能组合,以生成表VT1-J1。如果左表有M列,右表有N列,那么笛卡尔积后生成的VT1-J1表将有mn列。
Step 1-J1这个步骤等价于执行:从客户中选择*交叉连接订单O
执行结果为: (共47列)
c .客户id
c .城市
订单编号
o .客户id
FISSA
马德里
一个
FRNDO
FISSA
马德里
2
FRNDO
FISSA
马德里
三
克尔洛斯
FISSA
马德里
四
克尔洛斯
FISSA
马德里
五
克尔洛斯
FISSA
马德里
六
MRPHS
FISSA
马德里
七
空
FRNDO
马德里
一个
FRNDO
FRNDO
马德里
2
FRNDO
FRNDO
马德里
三
克尔洛斯
FRNDO
马德里
四
克尔洛斯
FRNDO
马德里
五
克尔洛斯
FRNDO
马德里
六
MRPHS
FRNDO
马德里
七
空
克尔洛斯
马德里
一个
FRNDO
克尔洛斯
马德里
2
FRNDO
克尔洛斯
马德里
三
克尔洛斯
克尔洛斯
马德里
四
克尔洛斯
克尔洛斯
马德里
五
克尔洛斯
克尔洛斯
马德里
六
MRPHS
克尔洛斯
马德里
七
空
MRPHS
耶路撒冷
一个
FRNDO
MRPHS
耶路撒冷
2
FRNDO
MRPHS
耶路撒冷
三
克尔洛斯
MRPHS
耶路撒冷
四
克尔洛斯
MRPHS
耶路撒冷
五
克尔洛斯
MRPHS
耶路撒冷
六
MRPHS
MRPHS
耶路撒冷
七
空值Step 1-J2:应用ON过滤,(JOIN 条件)
on筛选条件是sql的三个筛选条件(ON、WHERE、HAVING)中的第一个。对上一步中生成的虚拟表(VT1-J1)应用on筛选条件,并将满足ON筛选条件的行添加到虚拟表VT1-J2中。应用ON滤波后,生成的VT1-J2表如下:
c .客户id
c .城市
订单编号
o .客户id
FRNDO
马德里
一个
FRNDO
FRNDO
马德里
2
FRNDO
克尔洛斯
马德里
三
克尔洛斯
克尔洛斯
马德里
四
克尔洛斯
克尔洛斯
马德里
五
克尔洛斯
MRPHS
耶路撒冷
六
MRPHSStep 1-J3:添加外部列
只有使用外部连接时,才会出现这一步。对于外部连接(LEFT、RIGHT或FULL),可以将一个或两个表标记为保留表。作为保留表意味着您希望返回该表中的所有列,即使其中的数据不符合ON子句的筛选条件。LEFT OUTER JOIN将左边的表标记为保留表,RIGHTOUTER JOIN将右边的表标记为保留表,FULL OUTER JOIN将两个表都标记为保留表。步骤1-JBOY3乐队根据VT1-J2中的虚拟表,在保留表中添加不满足ON条件的列,而在未保留表中没有对应的列,因此标记为NULL。这个过程生成虚拟表VT1-JBOY3乐队。
c .客户id
c .城市
订单编号
o .客户id
FISSA
马德里
空
空
FRNDO
马德里
一个
FRNDO
FRNDO
马德里
2
FRNDO
克尔洛斯
马德里
三
克尔洛斯
克尔洛斯
马德里
四
克尔洛斯
克尔洛斯
马德里
五
克尔洛斯
MRPHS
耶路撒冷
六
MRPHS
如果FROM子句中有多个表操作,则从左到右处理sql,左边生成的临时表结果作为右边表的输入表。Step 2 WHERE 子句
其中过滤被应用于在先前步骤中产生的临时表,并且临时表VT2根据过滤条件产生。
注意:因为数据还没有分组,所以现在不能使用聚合运算——比如不能使用WHERE orderdate=MAX(orderdate)这样的句子。此外,不能使用在SELECT子句中创建的变量alias,因为SELECT子句还没有被处理——例如,不能写出这样的句子:SELECT YEAR(ORDER DATE)AS ORDER YEAR.订购2008年。
在C.city='Madrid '处应用此过滤器
此时生成的临时表VT2的内容如下:
c .客户id
c .城市
订单编号
o .客户id
FISSA
马德里
空
空
FRNDO
马德里
一个
FRNDO
FRNDO
马德里
2
FRNDO
克尔洛斯
马德里
三
克尔洛斯
克尔洛斯
马德里
四
克尔洛斯
克尔洛斯
马德里
五
克尔洛斯
在本例中,您需要在ON子句中使用ON c.customerid=o.customerid筛选。没有订单的客户在步骤1-J2中被过滤掉,但在步骤1-JBOY3乐队中作为外部列被添加回来。但是,由于您只想返回来自马德里的客户,因此需要在WHERE子句中过滤城市(其中c.city=' Madrid ')。如果您将其放入ON过滤器,不属于马德里的客户将被添加回Add External列。
这里需要解释一下ON和WHERE的区别。ON和WHERE的主要区别在于,ON在添加外部列之前进行筛选,而WHERE在添加外部列之后进行筛选。在已过滤的列上将被添加回1-JBOY3乐队。如果不需要添加外部列,那么这两个过滤器是一样的。Step 3 GROUP BY子句
该子句将对上一步中生成的临时表中的数据进行分组,每一行将被划分为仅一个组,以生成虚拟表VT3。VT3表包含VT2表中的所有数据和数据包标识符。
这是生成的临时表VT3。内容如下:
c .客户id组
c .客户id
c .城市
订单编号
o .客户id
FISSA
FISSA
马德里
空
空
FRNDO
FRNDO
马德里
一个
FRNDO
FRNDO
马德里
2
FRNDO
克尔洛斯
马德里
三
克尔洛斯
克尔洛斯
克尔洛斯
马德里
四
克尔洛斯
克尔洛斯
马德里
五
克尔洛斯
在sql返回的最终结果中,每个组必须只返回一行(除非被过滤掉)。因此,当在sql语句中使用GROUP BY时,在GROUP BY之后处理的子句(如SELECT和HAVING子句)只能使用出现在GROUP BY和聚合函数(如MAX、MIN、COUNT、AVG等)之后的列。)必须用于不出现在GROUP BY之后的列,以确保每个组只返回一行。Step 4 HAVING子句
HAVING子句用于过滤上一步生成的临时表,只作用于分组后的数据。满足HAVING条件的组被添加到虚拟表VT4。
当应用这个过滤器时:复制代码如下:具有COUNT(O.orderid) 3,生成的VT4表的内容如下:
c .客户id组
c .客户id
c .城市
订单编号
o .客户id
FISSA
FISSA
马德里
空
空
FRNDO
FRNDO
马德里
一个
FRNDO
FRNDO
马德里
2
FRNDO
值得注意的是,在这个查询中使用了COUNT(O.orderid)而不是COUNT(*)。当一个外部列被添加到该查询中时,COUNT方法将忽略空列,从而产生您不需要的结果。Step 5 SELECT 子句
虽然出现在sql语句的前面,但是SELECT是在第五步处理的,SELECT子句返回的表最终会返回给调用者。该子句包含三个子阶段:(5-1)评估表达式,(5-2)处理DISTINCT,以及(5-3)应用顶部过滤。
Step 5-1 计算表达式
SELECT子句中的表达式可以返回或操作上一个表中返回的基本列。如果此sql语句是一个聚合查询,在步骤3之后,您只能使用GROUP BY中的列,并且必须对不属于组集的列使用聚合操作。不属于FROM表中基本列的内容必须有一个别名,例如YEAR(orderdate)作为orderyear。
注意:在SELECT子句中创建的别名不能用于上一步,即使是在SELECT子句中。原因是sql的很多操作都是一次性操作,这里不介绍什么是一次性操作。因此,在SELECT子句中创建的别名只能在后面的子句中使用,如ORDER BY。示例:选择year (orderdate)作为订单年度.按订单年份排序。
在本例中:复制代码如下:选择c.customerid,count (o.orderid)作为num orders。结果,您将得到一个虚拟表VT5-1:
c .客户id
numorders
FIFSSA
0
FRNDO
2
Step 5-2:应用DISTINCT子句
如果在sql语句中使用DISTINCT,sql将删除重复的列并生成虚拟表VT5-2。
Step 5-3:应用TOP选项
TOP option是T-SQL提供的一个函数,用于指示显示多少行。根据ORDER BY子句定义的顺序,将查询指定数量的列。这个过程产生虚拟表VT5-3。
如上所述,这一步取决于ORDER BY定义的顺序来决定哪些列应该显示在前面。如果不按结果的顺序指定顺序,并且不使用WITH TIES子句,则每次返回的结果都可能不一致。
在我们的例子中,步骤5-3被省略了,因为我们没有使用TOP关键字。Step 6:ORDER BY子句
在上一步中返回的虚拟表在这一步中进行排序,游标VC6根据ORDER BY子句中指定的顺序返回。order子句也是唯一可以使用SELECT子句创建别名的地方。
注意:这一步和上一步的区别在于,这一步返回的结果是游标,而不是表格。Sql是基于集合论的。集合不定义其行的顺序,它只是成员的逻辑集合,因此成员的顺序并不重要。带有ORDER BY子句的Sql返回一个对象,该对象以特定的顺序组织每一行。ANSI将这种对象称为光标。理解这一点对你理解sql很重要。以上步骤如图所示:
本书主要内容参考《微软sql Server 2008内部:T-SQL查询》中的内容。如果你想了解更多关于SQL查询的知识,可以找这本书。我这里有英文原版pdf,有需要可以找我要。
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。