这儿有点需要补充的是,在同样的ORDER BY条件下,可能会得到不一样结果的问题,这个其实和数据结构中排序的概念一样。在某个条件(比如order by日期)下,有多个符合条件的记录时,这几个结果集的顺序是不一定的(已实际访问的物理记录行的顺序为准),属于不稳定排序。常见排序算法中,快速排序、希尔排序、堆排序、直接选择排序不是稳定的排序算法,而基数排序、冒泡排序、直接插入排序、折半插入排序、归并排序是稳定的排序算法。那么有没有稳定的情况呢,那么就需要排序条件中的每一项都是独一无二的,比如是主键列,唯一列,这种属性也称之为排序的决胜属性(tiebreaker)。
此外注意在SQL的关键字和系统函数名使用大写,涉及多表查询时需要给表起别名方便理解。以上是最核心的部分,接下来以列表的形式阐述与基本查询相关的SQL关键字。
关键字
解释与示例
TOP
T-SQL特有功能,用于限制查询返回的行数或行的百分比
获取前5行记录:SELECT TOP 5 userid FROM HR.Employee ORDER BY userid
获取前5%的记录:SELECT TOP 5 PERCENT userid FROM HR.Employee ORDER BY userid
这儿其实隐含了一个问题,就是这儿TOP返回的结果是表结果还是游标,因为之前有提到,使用order by之后返回的游标,这个问问的解释会放到表表达式部分。
OFFSET-FETCH
这是标准SQL的选取行数的语法,并且支持跳过功能,免得我们需要使用开窗函数或者两个TOP取交来实现该功能,等价于C#中 XXX.Skip(m).Take(n)
查询第51到75条记录:SELECT userid FROM HR.Employee ORDER BY userid OFFSET 50 ROWS FETCH NEXT 25 ROWS ONLY
Tip:ROW和ROWS关键字等价,该关键字不支持PERCENT和WITH TIES选项
ROW_NUMBER
是一种开窗函数,理解起来有点困难,其实就是对分组中的数据做更加细粒度的操作,方便实现复杂的查询,尤其是为报告服务(SSAS报表分析服务)。为了之后能更好的理解该知识点,提前拿出来给大家见见面,本文下篇还会具体介绍
开窗函数的定义:对于一个查询中的每一行,按行的窗口组进行运算,并计算一个标量结果值,行的窗口使用OVER字句定义
SELECT userid, ROW_NUMBER() OVER(PARTITION BY userid ORDER BY name) AS rownum
这儿的PARTITION分区其实就是分组条件,ROW_NUMBER函数实际用于对分组后小组内成员标上行号,同时OVER窗中的ORDER BY是组内的排序,规则和正常排序一致
谓词、常见运算符和系统函数
谓词(Predicate,这个委托熟悉不?)主要出现在WHERE、HAVING查询筛选中,包括TRUE、FALSE和UNKNOWN中逻辑结果,这儿一定不能忘记UNKNOWN未知结果这种情况,比如说让两个NULL作比较等,接下来用一个简单的表格展示。
关键字
解释与示例
BETWEEN, IN, LIKE
BETWEEN AND表示属于什么之间;IN表示在枚举出来的几个值中;LIKE可以使用%作为万能替代符,主要注意的LIKE中默认使用的Unicode的字符类型,并且在使用LIKE关键字时一定要谨慎,会造成很大的查询消耗,如果实在需要大量字符串的查询,考虑使用全文检索或选用其他类型数据库等解决方案
NOT, AND, OR
分别表示非、与、或的逻辑,优先级依次递减
%
取余操作符或是之前介绍的万能占位符
CAST(col1 AS NUMERIC(12, 2))
在数值运算时,如果出现两个整型相除,需要修改其类型避免丢失小数点后位数
CASE
CASE是一个标量表达式,返回一个基于条件逻辑的值,需要注意CASE不是语句不能用于控制逻辑(比如IF ELSE),实际中,CASE的使用场景还是很多的,比如行列转换等,才外,ORANGE有一个叫做的decade的简化版操作符。
尤其需要注意的是,CASE具有"简单"和"搜索"两种格式,后者非常的灵活
简单格式: SELECT studentid, CASE score WHEN 59 THEN 'Fail' WHEN 60 THEN 'Alive' FROM dbo.testScore
搜索格式: SELECT studentid, CASE WHEN score < 60 THEN 'Bad' WHEN score > 60 THEN 'Good'ELSE 'UNKNOWN' FROM dbo.testScore
此外,在SQL SERVER2012中还增加几个简化操作符, COALESCE , CHOOSE,ISNULL等,比如ISNULL(col1, '')表示Nullable<T> ?? '',若col1不为空就取其值,为空就是''空字符串,不过均不推荐使用。
NULL
NULL标记的理解在SQL中非常重要,很多细微的SQL错误都来之于此。其根源仍然是之前提到的3值逻辑,NULL标记表示不知道是什么值(在现实生活中,就像登记时缺失了),它与除了IS [NOT] NULL逻辑操作以外的逻辑运算结果均是UNKNOWN。
为了更好的理解应用这个概念,来看看接下来的例子
SELECT COUNT(*) FROM Address WHERE region <> 'Beijing', 假设该表中100条记录,10条的region是Beijing,20条的region为NULL,70为其他城市,那么这个查询的结果将是70,而不是我们想要的90,修改查询如下即可。
SELECT COUNT(*) FROM Address WHERE region <> 'Beijing' OR region IS NULL
同时操作
同时操作(all-at-once operations)表示出现在同一逻辑处理阶段的所有表达式在同一时间点进行逻辑运算,即并行运算,这个概念是之前查询顺序概念的补充,之前讲的纵向的顺序,这儿讲的是横向的并行。是不是觉得很拗口,其实重点就是SQL在同一层中运算的顺序不固定,所以之后运算一定不能依赖于之前的运算,并且不支持C#等常见语言中的短路: if(result != null && result == true),前者失败后者不运算,接下举两个错误的例子。
SELECT orderID, YEAR(orderdate) AS orderyear, orderyear + 1 AS nextyear FROM [order]
SELECT num1, num2 FROM dbo.tableB WHERE num1 <> 0 AND num2/num1 > 5
那么不禁要问,SQL中能提供同层次的逻辑运算的解决方案么?其实是有的,一种技巧性的,一种是通过合理的规划。