Select语句的元素
select语句的目的: 对表进行查询、应用一定的逻辑处理,并返回结果。
select处理的顺序:
1.from
2.where
3.group by
4.having
5.select
6.order by
USE TSQLFundamentals2008;
SELECT
empid,YEAR(orderdate) AS orderyear,COUNT(*)AS numorders
FROM Sales.Orders
WHERE custid =71
GROUP BY empid,YEAR(orderdate)
HAVING COUNT(*)>1
ORDER BY empid,YEAR(orderdate)
上面的语句是select书写的顺序,并不是SQL的执行顺序。我们可以根据上面的公式还原一下,上面SQL语句的顺序
1. FROM Sales.Orders
2. WHERE custid =71
3. GROUP BY empid,YEAR(orderdate)
4. HAVING COUNT(*)>1
5. SELECT empid,YEAR(orderdate) AS orderyear,COUNT(*)AS numorders
6. ORDER BY empid,YEAR(orderdate)
From语句
from子句是在逻辑处理阶段第一个要处理的查询子句
FROM Sales.Orders 表示要对Order表进行查询(Sales是架构的名称,SQLServer默认是dbo,推荐大家在写SQL的时候加上架构名,因为即便你没有显式指定架构名称,SQL Server也一定会隐式地解析它。这样就产生了一些额外的代价,而且如果完全让SQL Server决定使用哪个对象,也容易产生歧义。)
如果标识符是非常规的(例如:名称中嵌入了空格或其他特殊字符、以数字开头、或是SQL Server的保留字),就必须分隔这样的标题符
ANSI SQL的标准格式使用的是双引号(例如,"Order Details")
SQL Server独有的特殊格式是使用方括号(例如,[Order Details])
Where子句
在where子句中,可以指定一个谓词或谓词逻辑表达式,从而过滤由From阶段返回的行。只有能让逻辑表达式结果为True的行,才能由Where阶段返回给后续的逻辑查询处理阶段。
WHERE custid =71
表示 要查询客户Id为71的
Where子句对查询性能有重要影响。在过滤表达式的基础上,SQL Server会计算使用什么索引来访问请求的数据。
与扫描整个表相比,通过使用索引,有时可以大幅减少SQL Server在获取请求的数据时付出的代价。
要时刻记住T-SQL使用的是三值谓词逻辑,所以逻辑表达式的结果可以为True、False或者UNKNOWN(NULL)
Group By子句
group by阶段可以将前面逻辑查询处理阶段返回的行按"组"进行组合。
每个组由在group by子句中指定的各元素决定。
GROUP BY empid,YEAR(orderdate)
上面语句表达的意思是:对于Where阶段返回的数据中出现的每个empid和YEAR(orderdate)的唯一组合,在Group By阶段将会为其生成一个组
重点:如果查询涉及到分组,那么Group By阶段之后的所有阶段(包括Having、Select以及Order By)的操作对象将是组,而不是单独的行。
每个组最终也表示为查询结果集中的一行,这意味着在Group By阶段之后处理的子句中的所有表达式务必保证为每个组只返回一个标量(单值)。
YEAR(orderdate)
因为聚合函数只为每个组返回一个值,所以一个元素如果不在Group By列表中出现,就只能作为聚合函数(Count、Sum、Avg、Min、以及Max)的输入
SELECT
empid,
YEAR(orderdate) AS orderyear,
freight
COUNT(*) AS numorders
FROM Sales.Orders
WHERE custid=71
GROUP BY empid,YEAR(orderdate)
如果试图引用不在Group By列表中出现的属性(freight),而且也没有将其作为Group By子句之后处理的任何子句中聚合函数的输入,SQL Server引擎就会报错(这种情况下,无法保证表达式为每个组返回的值是唯一的)
重点:所有的聚合函数都会忽略NULL值,只有一个例外(Count(*))
Having子句
Having子句用于指定对组进行过滤的谓词或逻辑表达式,这与Where阶段对单独的行进行过滤相对应。
只有能让Having子句中的逻辑表达式为True的组,Having阶段才会把这些组返回到下一个逻辑查询处理阶段。
所以Having是和Group By一起出现的,是对Group By分组以后数据进行过滤。
因为Having子句是在对行进行分组后处理的,所以可以在逻辑表达式中引用聚合函数。
SELECT
empid,YEAR(orderdate) AS orderyear,COUNT(*) AS num
FROM Sales.Orders
WHERE custid=71
GROUP BY empid,YEAR(orderdate)
HAVING COUNT(*)>1
COUNT(*)>1
意味着Having阶段过滤器只保留包含多行的组(empid和Year(orderdate))
Select子句
select子句用于指定需要在查询返回的结果集中包含的属性(列)。select列表中的表达式可以直接基于正在查询的表的各个列,也可以在此基础上做进一步的处理。
给列起别名的方式:
SELECT
YEAR(orderdate) AS orderyear1,
YEAR(orderdate) orderyear2,
orderyear3 = YEAR(orderdate)
FROM Sales.Orders
1.列名 AS 别名 (推荐)
2.列名 别名
3.别名 = 列名
我们知道在SQL中用";"来结束的,而在Select查询列的时候是用","来分隔不同列的,假如你在列与列之间少加",",SQL会将下列的列名当做上一列的别名来处理。(这里容易产生一些很难发现的Bug)
重点:select子句是在From、Where、Group By以及Having子句后处理的。这意味着对于select子句之前处理的那些子句,Select子句中为表达式分配的别名并不存在。
SELECT
empid,YEAR(orderdate) AS orderyear,COUNT(*)AS numorders
FROM Sales.Orders
WHERE custid =71
GROUP BY empid,orderyear
HAVING COUNT(*)>1
ORDER BY empid,orderyear
现在我标红的两个位置,原来是YEAR(orderdate) 现在全部替换为orderyear,但是会报错。
原因:
1.查看上面我给出的查询顺序便可知道了,select执行是在from、where、group by、having之后执行的,所以我在select操作期间声明的别名在group by不能使用,而order by操作是在select之后,所以order by中的orderyear可以使用,只要将group by的orderyear用Year(orderdate)替换就可以了。
在SQL中,因select查询并不保证返回一个真正的集合(即,由唯一行组成的无序集合)。
待续...
摘自:SQL Server2008技术内幕(T-SQL语言基础)