SELECT empid, OrderYear=YEAR(orderdate), Numbers=COUNT(*)
FROM Sales.Orders
WHERE custid=71
GROUP BY empid, YEAR(orderdate)
HAVING COUNT(*)>1
ORDER BY empid, OrderYear
SQL逻辑查询语句执行顺序
- FROM:从Sales.Orders表中查询行;
- WHERE:筛选出custid=71的订单;
- GROUP BY:根据empid, YEAR(orderdate)对订单进行分组;
- HAVING:筛选出大于1个订单的组;
- SELECT:返回每组的雇员ID、订单年度、订单数量;
- ORDER BY:按照雇员ID、订单年度排序;
FROM
FROM子句是逻辑化处理的第一个查询子句,此子句指定要查询的表名称和进行多表运算的表运算符。
WHERE
在WHERE子句中,可以指定一个谓词或逻辑表达式来筛选由FROM阶段返回的行。只有逻辑表达式计算结果为TRUE的行,由WHERE阶段返回到后面的逻辑查询处理阶段。
GROUP BY
GROUP BY阶段允许用户把前面逻辑查询处理阶段返回的行排列到组中,组是根据你在GROUP BY子句中指定的元素而确定的。GROUP BY阶段会为WHERE阶段返回的数据,根据指定的元素的每一个唯一组合生成一个组。如果查询涉及分组,那么GROUP BY阶段后的所有阶段(包括HAVING、SELECT和ORDER BY)都必须是对组的操作,而不是对单个行进行操作。
不参与GROUP BY列表中的元素仅允许作为一个聚合函数的输入,如COUNT、SUM、AVG、MIN、MAX等。在GROUP BY子句之后处理的任何子句中,如果试图引入一个未参与到GROUP BY列表中的属性,并且该属性未作为一个聚合函数的输入,会得到一个错误,因为不能保证表达式对每组返回单个值。
注意: 除了COUNT(*)之外,所有聚合函数都会忽略NULL标记。例如一个具有5行的组,数值“30、10、NULL、10、10”在名为qty的列中,表达式COUNT(*)返回5,COUNT(qty)返回4,因为组中有四个已知值。如果只处理已知值中的非重复值,则可以在聚合函数括号内指定DISTINCT关键字。如:COUNT(DISTINCT qty)返回2;SUM(qty)返回60,但SUM(DISTINCT qty)却返回40;表达式AVG(qty)返回15,但AVG(DISTINCT qty)却返回20。
HAVING
在HAVING子句中,可以指定一个谓词来筛选组,而不是筛选单个行(行筛选发生在WHERE阶段)。只有HAVING子句中逻辑表达式计算结果为TRUE的组,才会由HAVING阶段返回到下一个逻辑查询处理阶段。逻辑表达式计算结果为FALSE或者UNKNOWN的组会被筛选掉。由于HAVING子句是在行分组后被处理,所以可以在逻辑表达式中引用集合函数。
SELECT
SELECT 子句是用户指定要返回到查询结果表中属性(列)的地方。用户可以基于所查询表的属性,在 SELECT 列表中建立表达式,属性可以有进一步的操作,也可以没有。T-SQL 可以为表达式分配一个别名(三种:<expression> AS <alias>
、<alias>=<expression>
、<expression> <alias>
)。
SELECT 子句是在 FROM、WHERE、GROUP BY 和 HAVING 子句之后处理的。这意味着 SELECT 子句中分配给表达式的别名,不会存在于之前的SELECT相关子句中。SQL 提供了在 SELECT 语句的结果中确保唯一性的方法,即以 DISTINCT 子句的方式删除重复行(在 SELECT 列表中的表达式是在 DISTINCT 子句之前计算的)。
注: SQL Server 能够识别查询语句中重复使用的相同表达式,此表达式只需被评估或计算一次。
ORDER BY
在逻辑查询处理顺序中,ORDER BY 是被最后处理的子句。ORDER BY 子句允许你对输出行进行排序。ORDER BY 表达式可以指定为升序(ASC)和降序(DESC),默认为ASC。
理解 SQL 的最重要一点是表中没有确定顺序,因为表是被假定为一个集合(或是多重集合,如果有重复数据的话),并且集合是没有顺序的。这意味着在查询表时没有指定 ORDER BY 子句,查询将返回一个表结果,并且 SQL Server 可以按任意顺序自由返回输出行。
如果指定了 ORDER BY 子句,则结果不符合表的要求,因为结果中行的顺序是确定的。标准 SQL 中把具有 ORDER BY 子句的结果称为游标——一个具有确定行顺序的非关系型结果。为什么查询返回表结果还是游标这么重要,因为 SQL 中的一些语言元素和运算需要操作表结果而不是游标。
T-SQL 允许在 ORDER BY 子句中指定未出现在 SELECT 子句中的元素,但是如果指定了 DISTINCT,则 ORDER BY 列表中只能出现在 SELECT 列表中的元素。因为在指定 DISTINCT 后,单个结果行可能会代表多个源行,在 ORDER BY 子句中会有多个可能值可用,这样会导致不清晰。
TOP和OFFSET-FETCH筛选
TOP选项是一个专有的T-SQL功能,用于限制查询返回的行数或行的百分比。它的规范组成依赖于两个元素,一个是要返回行的数目或百分百,另一个是排序。
ORDER BY 子句是在 SELECT 子句所包含的 DISTINCT 选项之后计算的,TOP 要依靠 ORDER BY 为其提供相关的筛选内容,这意味着如果在 SELECT 子句中指定了 DISTINCT,则 TOP 筛选是在删除重复行后计算的。
在指定了 TOP 后,查询中 ORDER BY 子句服务于双重目的:一个目的是为查询结果中的行定义显示顺序,另一个目的是为 TOP 定义要筛选的行。可以为 TOP 选项指定 PERCENT 关键字,这时,SQL Server基于限定行数的百分比计算要返回的行数,向上舍入。
SELECT DISTINCT TOP(1) orderid, orderdate, custid, empid FROM Sales.Orders ORDER BY orderdate DESC
SELECT TOP(1) PERCENT orderid, orderdate, custid, empid FROM Sales.Orders ORDER BY orderdate DESC
如果希望查询结果是确定的,则需要确保 ORDER BY 列出数据是唯一的,换句话说就是要添加一个决胜属性(tiebreaker)。当 ORDER BY 列表不唯一时,可以添加主键作为决胜属性。
除了在 ORDER BY 列表中添加一个决胜属性(tiebreaker),也可以添加 WITH TIES 选项请求返回表中与检索到的最后一行的排序值相同的其他所有行。
SELECT TOP(5) WITH TIES orderid, orderdate, custid, empid FROM Sales.Orders ORDER BY orderdate DESC
SQL Server 2012中 OFFSET-FETCH 筛选被视为 ORDER BY 子句的一部分,通常用于实现按顺序显示效果。OFFSET 子句指定要跳过的行数,FETCH 子句指定在跳过的行数后要筛选的行数。使用 OFFSET-FETCH 的查询必须具有 ORDER BY 子句。FETCH 子句不支持没有 OFFSET 子句,但是 OFFSET 子句可以没有 FETCH 子句。
SELECT orderid, orderdate, custid, empid FROM Sales.Orders ORDER BY orderdate, orderid OFFSET 50 ROWS FETCH NEXT 25 ROWS ONLY;
SELECT orderid, orderdate, custid, empid FROM Sales.Orders ORDER BY orderdate, orderid OFFSET 50 ROWS; -- 跳过1行时可以指定单数行的ROW
SELECT orderid, orderdate, custid, empid FROM Sales.Orders ORDER BY orderdate, orderid OFFSET 0 ROWS FETCH FIRST 25 ROWS ONLY; -- 跳过0行,FIRST比NEXT更合适
OFFSET-FETCH 子句比 TOP 子句更灵活,但 OFFSET-FETCH 不支持 PERCENT 和 WITH TIES 选项。
下面就是以上涉及所有子句的逻辑处理顺序:
- FROM
- WHERE
- GROUP BY
- HAVING
- SELECT
- 表达式
- DISTINCT
- ORDER BY
- TOP/OFFSET-FETCH
其他
三值逻辑
在T-SQL中是使用三值谓词逻辑,逻辑表达式可以计算为TRUE、FALSE、UNKNOWN。对于三值逻辑来说“返回TRUE”并不等于“不返回FALSE”。WHERE阶段返回逻辑表达式计算结果为TRUE的行,并且不返回逻辑表达式计算结果为FALSE或UNKNOWN的行。
分隔标识符名称
只要查询中的标识符符合常规标识符的格式规则,就不需要分割架构、表和列的标识符名称。如果是不规则的标识符,例如:嵌入空格、特殊字符、以数字开头或者是保留的关键字,就必须对它进行分割。标准SQL格式是使用双引号:SELECT * FROM "Sales"."OrderDetails"
。SQL Server特定格式是使用方括号:SELECT * FROM [Sales].[OrderDetails]
。使用符合常规标识符格式规则的标识符,分隔是可选的。