5.1 模糊查询
模糊查询提取的数据不一定的确切的,查询者对查询条件也是模糊的、大概的、不特别明确的。例如,只查询张姓学员的信息、只查询分数在60-83分之间的考试成绩、或者只查询北京上海广州地区的学员,这种查询不是指定某个人的姓名、一个具体的分数或者某个固定的地区,因此属于模糊查询。
模糊查询可以使用我们前面已经介绍过的Like、通配符来进行。上一章讲述过的Is Null查询严格说也是一种模糊查询,模糊查询还有基于范围内的查询和在某些列举值内的查询。
5.1.1 使用Like进行模糊查询
在前面我们已经介绍过使用Like来编写约束,Like运算符用于匹配字符串或字符串的一部分(称为子串)。由于该运算符只用于字符串,所以仅与char或varchar数据类型联合使用。
在数据更新、删除或者查询的时候,依然可以使用Like关键字来进行匹配查找,例如,查找姓张的学员:
Select * from Students Where Sname like '张%'
或者查询不是八月份发行的A卡或者C卡:
Select * from Card where ID Like '00[^8]%[A,C]%'
5.1.2 使用BetWeen在某个范围内进行查询
使用关键字Between可以查找那些介于两个已知值之间的一组未知值。要实现这种查找,必须知道开始查找的初值和终值,这个最大值和最小值用单词And分开,例如:
Select * from Score where Score Berween 60 and 80
此外,Between 查询在日期范围的时候使用得比较多。例如,查询不在1992年8月1号到1993年8月1号之间订购的读书列表:
Select * from Sales Where ord_date not Between '1992-8-1' and '1993-8-1'
5.1.3 使用In在列举值内进行查询
查询的值是指定的某些值之一,可以使用带列举值的In关键字来进行查询。将列举值放在圆括号里,用逗号分开,例如查询北京、广州或者上海的学员姓名:
Select Sname as 学员姓名 from Students Where Saddress in ('北京','广州','上海') Order by Saddress
还可以把In关键字和Not关键字合起来使用,这样可以得到所有不匹配列举值的行。
5.2 SQL Server 中的聚合函数
在查询中还会经常碰到的要求是取某些列的最大值、最小值、平均值等信息,有时候还需要计算出究竟查询到多少行数据项。这个时候,查询的“统计数据”是用户比较关心的,SQL Server 提供了“聚合函数”,聚合函数能够基于列进行计算,并返回单个值。
SQL Server 提供了以下几种聚合函数。
1. Sum
Sum返回表达式中所有数值的总和,Sum只能用于数字类型的列,不能够汇总字符、日期等其他数据类型。要得到商务付款的总数,可执行以下查询:
Select Sum(ytd_sales) From titles where type = 'business'
注意这种查询只返回一个数值,因此,不能够直接与可能返回多行的列一起使用来进行查询,例如:
Select Sum(ytd_sales) , Price from titles where type = 'business'
将报告错误信息。但是,在一个查询中可以同时使用多个聚合函数。
2. Avg
Avg函数返回表达式中所有数值的平均值,Avg函数也只能用于数字类型的列。例如,成绩表中存在的数据项。
要查询及格线以上的学员的平均成绩,语句如下:
Select Avg(Score) as 平均成绩 from Score Where Score >= 60
3. Max 和 Min
Max返回表达式中的最大值,Min返回表达式中的最小值,它们都可以用于数字型、字符型以及日期/时间类型的列。
例如,查询平均成绩、最高分、最低分的语句如下:
Select Avg(Score) as 平均成绩 , Max(Score) as 最高分, Min(Score) as 最低分 from Score Where Score >= 60
4. Count
Count 返回提供的表达式中非空值的计数,Count可以用于数字和字符类型的列。
另外,也可以使用星号(*)作为Count的表达式,使用星号可以不必指定特定的列而计算所有的行数。
例如,查询及格人数的语句如下:
Select Count(*) as 及格人数 From Score where Score >= 60
5.3 分组查询
5.3.1 使用 Group By 进行分组查询
我们来看一下学员成绩表,成绩表中存储了所有课程的成绩。在这种情况下,可能就需要统计不同课程的平均成绩,也就是说,需要对不同的成绩首先按照课程来进行分组,分组以后再进行聚合计算,得到累计信息。
这种情况应用很普遍,例如一个早餐店,早晨销售包子、油条、米粉等,中午的时候,就需要分类统计包子的销售金额、油条的销售金额、米粉的销售金额,这个时候就需要首先分类,然后在这个类别的基础上分别进行汇总和统计输出。
再具体点,假设学员成绩表中有以下数据。
此时,要统计不同课程的平均分数。首先把相同的CourseID都分为一组,然后把这些相同组对应的分数值再使用前面的聚合函数取平均值(最大值、最小值或者参考人数统计)。
在编写SQL语句之前,先想想我们想要的输出结果是什么?
我们想要的输出结果应该首先是不同的课程,并且课程不能重复存在--如过重复存在就说明不符合“分组”的原则。其次,才是这个不重复的课程的平均分。
以上这种类型的查询,在SQL Server 中叫做分组查询,分组查询采用 Group By 子句来实现。
采用分组查询实现的SQL语句如下:
Select CourseID , Avg(Score) as 课程平均成绩 from Score Group by CourseID
查询结果如下:
分组查询有时候可能还要按照多个列来进行分组。例如,成绩表中记录了每次内部测试的成绩(CourseID 表示内部测试编号)。内部测试如果不及格则需要补考,补考学员最后的成绩取第一次成绩和补考成绩的平均值。
如果要统计内部测试的成绩表,则学员的编号、内部测试的编号不能存在重复,两项都重复的行的分数值要取平均值。因此,这是一个既按照学员编号、又按照内部测试编号进行分组的查询。
Select StudentID AS 学员编号, CourseID AS 内部测试, Avg(Score) AS 内部测试平均成绩 From Score Group by StudentID, CourseID
不难理解,在使用Group by 关键字时,在Select 列表中可以指定的项目是有限的,Select 语句中仅允许以下几项。
--- 被分组的列。
--- 为每个分组返回一个值的表达式,例如用一个列名作为参数的聚合函数。
5.3.2 使用 Having 子句进行分组筛选
继续考虑上面的分组查询,如果查询内部测试的成绩,只显示“补考过的学员”的成绩怎么处理?
这个时候,牵涉到分组统计后的条件限制。限制条件为Count(Score) > 1 , 也就是相同的学员编号列和内部测试成绩列,至少出现过两次。这时候使用Where子句是不能满足查询要求的,因为Where子句只能对没有分组统计前的数据行进行筛选(例如成绩必须大于60分),对分组后的条件的筛选必须使用Having子句。
以上要求的SQL如下:
Select StudentID As 学员编号, CourseID As 内部测试, Avg(Score) As 内部测试平均成绩 from Score Group by StudentID, CourseID Having Count(Score) > 1
Having 和 Where 子句可以在同一个Select语句中一起使用,使用的顺序应按照如图所示的次序。
例如,在按照部门分类的员工表中,要查询“有多个员工的工资不低于2000的部门编号”,则SQL语句如下:
Select 部门编号 , Count (*) From 员工信息表 Where 工资 >= 2000 Group by 部门编号 Having Count(*) > 1
5.4 多表联接查询
前面我们讲述过的所有查询,都是基于单个数据库表的查询,本节介绍牵涉到多个表的数据查询。
5.4.1 多表联接查询的分类
在上面介绍的学员内部测试成绩查询中,我们每次显示的都是学员的编号信息,因为该表中只存储了学员的编号,实际上最好显示学员的姓名,而姓名却存储在学员信息表中,像这种需要从多个表中选择或者比较数据项的情况,就需要使用到多表联接查询。
多表联接查询实际上是通过各个表之间共同列的关联性来查询数据的,它是关系数据库查询最主要的特征。
联接可分为以下内联接、外联接和交叉联接等类型。
1. 内联接
内联接是最典型、最常用的联接查询,它根据表中共同的列来进行匹配,特别是两个表存在主外键关系时通常会使用到内联接查询。
内联接查询通常会使用像“=”或“<>”之类的比较运算符来判断两列数据项是否相等,上面所说的根据学员编号信息来判断出学员姓名的联接就是一种内链接。
内联接使用 Inner Join 关键字来进行表之间的关联。
2. 外联接
外联接可以是左外联接或完整外联接。
(1)左外联接:Left Join 或 Left Outer Join。
左外联接的结果集包括Left Join 子句中指定的左表的所有行,而不仅仅是联接列所匹配的行。如果左表的某行在右表中没有匹配行,则在相关联接的结果集行中右表的所有选择列均为空值。
(2)右外联接:Right Join 或 Right Outer Join。
右外联接是左外联接的反向联接,将返回右表的所有行。如果右表的某行在左表中没有匹配行,则将为左表返回空值。
(3)完整外联接:Full Join 或 Full Outer Join。
完整外联接返回左表和右表中的所有行,当某行在另一个表中没有匹配行时,则另一个表的选择列包含空值,如果表之间有匹配行,则整个结果集行包含基表的数据值。
3. 交叉联接
交叉联接返回左表中的所有行,左表中的每一行于右表中的所有行再一一组合,相当于两个表“相乘”。
5.4.2 内联接查询
内联接查询可以通过两种方式实现。
1. 在Where子句中指定联接条件
例如查询学员姓名和成绩的SQL:
Select Students.Sname, Score.CourseID, Score.Score From Students, Score Where Students.Scode = Score.StudentID
上面这种形式的查询,相当于From后面紧跟了两个表名,然后在字段列表中用“表名.列名”来区分列,再在Where条件子句中加以判断,要求学员编号信息相等。
2. 在From子句中使用Join···On
上面的查询也可以通过以下的 Join···On 子句来实现:
Select S.Sname, C.CourseID, C.Score from Students AS S Inner join Score AS C on (S.Scode = C.StudentID)
在NorthWind数据库中,以下的SQL语句:
Select ProductID, Suppliers.SupplierID, CompanyName From Suppliers Inner Join Products On (Suppliers.SupplierID = Products.SupplierID) Where UnitPrice > $10 And CompanyName Like 'F%'
查询将返回某公司所提供的一组产品和供应商信息,该公司名以F字母开头,并且产品价格在10美元以上。
内联接查询通常不仅仅联接两个表,有时候还会牵涉到三个表或者更多的表。例如除了学员信息表、学员成绩表之外,还存在课程名称表,上面的查询不仅仅要显示学员姓名、分数,而且要通过课程编号来显示课程名称表中对应课程的名称,可以使用以下的三表联接查询的SQL语句来实现:
Select S.Sname AS 学员姓名, CS.CourseName AS 课程名称, C.Score AS 考试成绩 From Students AS S Inner Join Score AS C On (S.Scode = C.StudentID) Inner Join Course AS CS On (CS.CourseID = C.CourseID)
注:以上查询语句查询三个不同的数据表。
5.4.3 外联接查询
通过上面的例子可以看出:内联接的结果是从两个或两个以上的表的组合中挑选出符合联接条件的数据。如果数据无法满足联接条件则将其丢弃。在内部联接中,参与联接的表的地位是平等的。
于内部联接相对的方式称为外部联接。在外部联接中参与联接的表有主从之分,以主表的每行数据去匹配从表的数据列,符合联接条件的数据将直接返回到结果集中;对那些不符合联接条件的列,将被填上Null值(空值)后再返回到结果集中。
1. 左外联接查询
例如要统计所有学员的考试情况,要求显示所有参加考试学员的每次考试分数,没有参加考试的学员也要显示出来。这时候,以学员信息表为主表、学员成绩表为从表的左外联接查询:
Select S.Sname , C.CourseID , C.Score From Students AS s Left Outer Join Score AS C On S.Scode = C.StudentID
查询的结果可能有部分学员没有出现在成绩表上,对应的科目和成绩以Null(空值)填充。
2. 右外联接查询
右外联接查询与左外联接查询类似,只不过要包含右表中所有匹配的行。如果右表中有的项在左表中没有对应的项,则以Null值来填充。例如,在Pubs数据库中,在Titles和Publishers表之间的右向外联接将包括所有的出版商,在Titles表中没有书名的(很久没出书了)也被列出。
Select Titles.Title_id, Titles.Title, Publishers.Pub_Name From titles Right Outer Join Publishers On Titles.Pub_id = Publishers.Pub_id