在当今这个多种不同数据库混用,各种不同语言不同框架融合的年代(一切为了降低成本并高效的提供服务),知识点多如牛毛。虽然大部分SQL脚本可以使用标准SQL来写,但在实际中,效率就是一切,因而每种不同厂商的SQL新特性有时还是会用到,这部分内容更是让人抓瞎,常常会由于一些很简单的问题花很久来搜索准确答案。赶脚俺弱小的智力已经完全无法记清楚常见的命令了,即使是用的最熟悉的T-SQL(SQL Server)。因此将最常见的T-SQL操作做个简单的总结,包括一些容易忽视的知识点和常见的开发样例。实话实说,现在开发中较少直接写SQL了,但有时需要给测试团队提供一些便利还是需要的。
本系列包含上中下三篇,内容比较驳杂,望大家耐心阅读:
那些年我们写过的T-SQL(上):上篇介绍查询的基础,包括基本查询的逻辑顺序、联接和子查询
那些年我们写过的T-SQL(中):中篇介绍表表达式、集合运算符和开窗函数(编辑中)
那些年我们写过的T-SQL(下):下篇介绍数据修改、事务&并发和可编程对象(编辑中)
预祝大家新年快乐,万事如意! I believe: 万丈高楼平地起,大海无边百川融。
这部分中重要的概念就是要弄清楚SQL语句具体的执行顺序,记得在南京做一个短期培训讲师期间,就发现这部分是一个很容易被忽视的基础,一旦弄清这部分内容,基本的标准SQL的编写基本上就没有很大问题了。此外关于SQL的一个非常关键的概念是,尽可能的使SQL语句进行的是整体的集合操作,而不是类似游标的循环迭代操作,这一点也是SQL优化的一个核心概念。
语句 执行顺序
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, orderyear [ASC, DESC]
不知道这儿的执行顺序和你心中的是否相同,记得了解到这部分知识时,自己也花了很久去理解, 不过从形式上可以看到实际的执行顺序很像LINQ,有木有?SQL只所以语句顺序和实际执行顺序不同是因为SQL设计师将该高级语言作为声明式语言来定义的,"可以按照类似英语的方法提供自己的请求"。之所以说这部分重要,不知道大家遇到过自己给字段起的别名在where中不能使用的情况没有,那是因而where执行时,select还未执行,那么select中给字段其的别名还不存在好,但在order by字句中就可以正常使用。接下来,补充说明一下以上六个字句中的相关知识。
FROM字句:在From字句中的对象中需要附加上schema架构限定,如dbo.Sales, hr.Employee等。
WHERE字句:该字句中字段的选择对于查询性能影响很大,如果符合索引(包括组合索引,需要正确的顺序)条件,那么查询就会通过索引而不是全表扫描。例如建立的组合索引为(name, time),那么如果查询中使用where time =xx and name = xx会造成索引不起作用,而造成全表扫描,当然由于内置查询优化器的存在,实际的查询可能与教科书上说的不同, 一定要实际查看执行计划,一起以事实说话。在实际项目中,数据库的设计需要保证基本不犯明显的错误即可,其他的到出现性能问题时通过查询计划和查询统计信息才去优化,不用过度设计,因为数据量没变化一个量级可能调优的方式就会出现不同。才外,需要记住,在TSQL中使用三值谓词逻辑,逻辑表达式可以计算为TRUE、FALSE和UNKNOWN,而如果数据字段为空,需要使用IS [NOT] NULL判断。
GROUP BY字句:当涉及分组时,其后续的所有操作都是对组的操作而不是对单个行的操作,每组均是一个单个行,这些操作中表达式需要保证返回一个标量。不参与到group by中的字段仅允许作为一个聚合函数的输入,如COUNT、SUM等。注意,除了Count(*)外,所有的聚合函数忽略NULL标记,DISTINCT可以包含在聚合函数中,针对不重复且有值的项。
HAVING字句:可以指定一个谓词来筛选组而不是单个的行,比如使用集合函数count(*)>1表示筛选组成员大于1的组。
SELECT字句:指定返回到查询结果表中列的地方,可以包含表达式,推荐给表达式创建一个易懂的别名,比如Year(orderdate) AS OrderYear,尤其是新增一些与列无关的表达式,如current_timestamp()函数,使关系模型变得完善。这人再次提及SELECT字句中别名的使用范围,只能是SELECT字句执行之后的部分,也就是Order by字句。此外,有一点曾经困扰了我很久,就是如果我在where字句中使用YEAR(orderdate),还在select中使用YEAR(orderdate),那样不是重复计算了?其实,SQL SERVER能够识别查询中重复使用的相同表达式,也就是说在一个查询,出现多次相同的表达式,实际上只会运算一次,简直赞赞哒。不过在同层中使用别名还是没有被支持的,例如,
SELECT YEAR(orderdata)AS orderYear, orderYear + 1 AS nextyear是不正确的。常见的,我们在一般的查询中,比如检验数据等,是推荐使用SELECT *,包括加上top 1000的,但在项目代码中,是严禁这样的操作的。
补充一点关系代数的知识,我们知道在关系模型中,所有操作均基于关系代数,并且操作结果是一个关系集合,但实际上我们返回的结果集还是会出现重复行的情况,不过可以通过DISTINCT关键字删除重复行。
ORDER BY字句:按序输出行,需要理解的是,在SQL中,表中没有确定的顺序,表假定为一个集合,集合是没有顺序(这个观念如果是半路出家,需要很久才能真正理会的到)。因此,Order by之后的有序结果,其实失去表资格,一般将这种结果称之为游标,"一个具有确定行顺序的非关系型结果",这部分概念在之后的还会有介绍。此外,该字句中可以使用不在SELECT列表中的字段排序,但如果使用了DISTINCT关键字,则必须使用SELECT列表中的列,否则由于单个结果行可能代表多个原行,造成排序的不清晰。