(一)普通查询
1、查询stu_info表中所有数据
SELECT *
FROM table_source
2、 查询表中指定字段的数据
查询出stu_info表中“depart”(院系)字段的值,即可知道有哪些院系的学生
SELECT depart
FROM stu_info
3、查询stu_info表中,所有学生的姓名、性别和手机的信息。
SELECT sname,sex,telephone
FROM stu_info
4、 查询结果中去除重复信息
去除重复值需要使用DISTINCT关键字。
将例3运行结果中的重复值去掉。
SELECT DISTINCT depart
FROM stu_info
DISTINCT关键字不仅可以去除重复值,也有排序数据的功能。上面查询结果就是按“depart”字段升序排列的。但是,DISTINCT关键字的排序功能是不可靠的,因此,如果需要排序查询结果,则应当使用ORDER BY子句,明确指出排序的根据和方式。
注意:使用DISTINCT关键字会使查询效率下降,因此应尽量避免使用它,在需要去除重复信息时可以使用GROUP BY子句。
使用DISTINCT关键字会使查询效率下降的原因是:在去除重复值之前,首先要对查询结果集进行排序操作,将相同值的记录放在一起分为很多组,然后再删除每组第一条记录以外的其他记录,以此达到去掉重复值的目的。因此,排序操作是降低效率的主要原因。
5、根据现有列值计算新列值
查询每个学生的年龄。
SELECT sname,DATEDIFF(year,birth,GETDATE())
FROM stu_info
GETDATE()用于获取当前系统时间,DATEDIFF函数用于获取两个日期之间的差。
6、命名新列
从图中会看到一个奇怪的现象——第二列没有字段名,这是因为第二列是通过计算得出的新列,而并非是表原有的列,所以没有字段名。没有字段名的列会给用户带来很多不便,例如,无法引用该列等。
通过本例还应该知道,SELECT子句中除了可以放置数据表原有的字段名外,还可以放置表达式,后面还会学习字段列表中放置常量。
例6 查询每个学生的年龄,命名年龄字段
SELECT sname,DATEDIFF(year,birth,GETDATE()) AS 年龄
FROM stu_info
7、查询stu_info表中所有学生的“sname”、“sex”和“depart”三个字段,并将结果集中的“sname”字段改为“学生姓名”,“sex”改为“性别”,“depart”改为“系别”。
SELECT sname AS 姓名,sex AS 性别,depart AS 系别
FROM stu_info
这种设置别名操作不会改变stu_info表中原来的字段名,它只对查询结果集有作用。
设置别名时,需要注意的一点是,如果别名是以数字或者特殊符号开头,例如以等号(=)开头,则应当将别名放入双引号中。
说明:在SQL Server中给字段取别名时可以省略AS关键字,直接用空格代替。例如,下面的SELECT语句运行结果同上例中
SELECT sname 学生姓名, sex 性别, depart 系别
FROM stu_info
8、将查询结果保存为新表
在SELECT子句的后面,FROM子句的前面加了一个“INTO”关键字,关键字的后面紧跟用于保存查询结果的新表的名字。
SELECT *(或字段列表)
INTO 新表名
FROM table_source
……
【例8】从stu_info表中,查询每个学生的年龄,并将查询结果保存为age表。
SELECT sname,DATEDIFF(year,birth,GETDATE()) AS 年龄
INTO age
FROM stu_info
该语句运行后会出现类似下面的提示文字。
(7 行受影响)
这表示查询结果已经被保存到了age表中。使用下面的查询语句查看age表中的内容。
SELECT *
FROM age
注意:将查询结果保存为表时应当考虑到修改、添加和删除等问题。例如,当修改了某学生stu_info表中的“birth”字段的值时,还应当修改age表中的年龄。
9、连接字段
【例9】从stu_info表中,查询所有学生的姓名和系别,并将这两个字段连接为一个字段。
SELECT sname+depart
FROM stu_info
改进:
(1)需要给新字段设置字段名。
SELECT sname+depart AS 姓名及来源地
FROM stu_info
(2)姓名和来源地之间的距离太大,应当缩小距离。
RTRIM函数去除字段值右侧的空格
SELECT RTRIM(sname)+RTRIM(depart) AS 姓名及来源地
FROM stu_info
(3)应当将来源地放进括号内,与姓名隔开。
SELECT RTRIM(sname)+'('+RTRIM(depart)+')' AS 姓名及来源地
FROM stu_info
有些DBMS连接字段或常量时不是使用加号,而是使用两个竖杠(||)。
(二)根据条件查询数值数据(WHERE)
【例10】从course(课程)表中,查询所有4学分的课程信息。
course表属于test数据库,其表结构如图所示。其中,“cno”存放课程编号,“cname”存放课程名称,“ctype”存放课程类型,“credit”存放课程学分。
SELECT *
FROM course
WHERE credit=4
说明:因为“credit”学分字段是数值型字段,因此必须与数值常量比较,所以表达式
credit =4
不能写为:
credit =’4’
或者其他形式。
【例11】从course表中,查询所有学分大于2的课程的课名、课号和学分。
SELECT cname,cno,credit
FROM course
WHERE credit >2
【例12】从stu_info表中,查询年龄大于30岁的学生信息。
SELECT sname,DATEDIFF(year,birth,GETDATE()) AS年龄
FROM stu_info
WHERE DATEDIFF(year,birth,GETDATE())>30
注意:上面WHERE子句中的条件表达式不可以写为如下形式。
年龄>30
因为WHERE子句在SELECT子句之前执行,所以在WHERE子句执行时还没有执行给计算字段(DATEDIFF(year,birth,GETDATE()))取别名的操作。
--
前面介绍了如何查询数值型数据的方法,下面仍旧通过几个示例介绍查询字符型数据的方法。
【例13】从stu_info表中,查询名叫“张三”的学生。
SELECT *
FROM stu_info
WHERE sname='张三'
说明:因为“sname”字段是字符型字段,因此必须与字符常量比较,所以必须用英文单引号('')括住“张三”。
【例14】从stu_info表中,查询非计算机系的所有学生。
SELECT *
FROM stu_info
WHERE depart<>' 计算机系'
【例15】从course表中,查询课号大于“003”的课程信息。
SELECT *
FROM course
WHERE cno > ’003’
字符串比较大小,其实是在比较每个字符的ASCII码值,ASCII码大的字符为大。人们经常使用的字符里数字字符“0”的ASCII码是48,“1”的ASCII码是49,依此类推,向后递增;大写英文字母“A”的ASCII码是65,“B”的ASCII码是66,依此类推,向后递增;小写英文字母“a”的ASCII码是97,“b”的ASCII码是98,依此类推,向后递增。因此,每个排列后面的字符都比前面的要大。汉字比较大小时比较的是拼音,例如,“张”比“王”大,因为“z”大于“w”。
下面看一个汉字比较的例子。
【例16】从stu_info表中,查询姓名按拼音排在“马六”后的所有学生的姓名、性别和所属院系。
SELECT sname 姓名,sex 性别,depart 所属院系
FROM stu_info
WHERE sname > '马六'
--根据条件查询日期数据
使用WHERE子句也能查询日期型数据。但需要注意的是:在不同的DBMS中编写查询日期型数据的条件表达式也不同。在SQL Server中,日期型变量和拥有日期格式的字符串比较即可;而在Access中,日期型变量需要与被井号(#)括住的日期常量相比较。
【例17】从stu_info表中,查询1977年1月20日之后出生的学生信息。
SELECT *
FROM stu_info
WHERE birth > ’ 01/20/1977’
说明:写日期格式的字符串时,应当遵守SQL Server默认的日期格式:月/日/年(month/day/year)。
在此还需要提醒读者一个问题,如果日期字段的数据类型是datetime或smalldatetime,则因为其数据中包含时间,所以需要使用AND运算符处理“等值日期”查询。
例如,如果下面的“出生日期”字段是smalldatetime类型,则下面的语句只能查询1980年1月8日0点0分0秒出生的人。
SELECT *
FROM stu_info
WHERE 出生日期 = '01/08/1980'
而如果想查询1980年1月8日内出生的所有人,则需要使用下面的SELECT语句。
SELECT *
FROM stu_info
WHERE 出生日期 >= '01/08/1980' AND 出生日期 < '01/09/1980'
--按范围查询数据
可以在WHERE子句中使用BETWEEN运算符,该运算符需要两个值,即范围的开始值和结束值。
【例18】从course表中,查询学分在2~4分之间的所有课程的信息。
SELECT *
FROM course
WHERE credit BETWEEN 2 AND 4
说明:BETWEEN运算符包含开始值和结束值。
【例19】从stu_info表中,查询1977年1月1日~1979年12月31日之间出生的学生姓名、出生日期和所属院系。
SELECT sname 姓名,birth 出生日期,depart 所属院系
FROM stu_info
WHERE birth BETWEEN ’ 01/01/1977’ AND ’ 12/31/1979’
--查询NULL值
查询表中的空值或者非空值,可以使用IS NULL(IS NOT NULL)运算符。
【例20】从stu_info表中,查询没有联系方式的所有学生的信息。
SELECT *
FROM stu_info
WHERE telephone IS NULL
注意:查询空值不能写为(字段名=NULL)。
【例21】从stu_info表中,查询拥有联系方式的学生姓名、联系方式和所属院系。
SELECT sname 姓名, telephone 联系方式,depart 所属院系
FROM stu_info
WHERE telephone IS NOT NULL
(三)排序查询数据(ORDER BY)
下面介绍怎样在SELECT语句中使用ORDER BY子句排序查询结果。
-- 按单列排序
【例22】从stu_info表中,查询所有学生的学号、姓名和出生日期,并按出生日期排序结果。
SELECT sno 学号,sname 姓名,birth 出生日期
FROM stu_info
ORDER BY 出生日期
说明:在SELECT语句中的各子句中ORDER BY子句最后执行,因此,ORDER BY子句中可以使用字段别名。
如果排序字段中有NULL值,则NULL值为最小值,当升序排序时它会在最前面,而降序排序时它会在最后面。
ORDER BY子句后的字段名,可以不在SELECT子句的字段名列表中,例如下面的语句。
SELECT sname 姓名,sex 性别
FROM stu_info
ORDER BY birth
该语句将查询结果集按“birth”字段进行了排序,而“birth”字段并不在字段名列表内。
-- 设置排序方向
在ORDER BY子句中使用ASC关键字指定升序,使用DESC关键字指定降序。如果没有使用关键字,则默认排序方式是升序。
【例23】从course表中查询所有内容。要求将查询结果按照学分降序排序。
SELECT *
FROM course
ORDER BY credit DESC
-- 按多列排序
【例24】从course表中查询所有内容。要求将查询结果按照学分降序排序,当学分相同时按照课号升序排序。
SELECT *
FROM course
ORDER BY credit DESC,cno
-- 按字段位置排序
在实际应用中,有时也需要按字段位置排序。因为SELECT关键字后并非都是字段名,也可能是表达式。如果希望按表达式的值排序,而又没有给表达式取别名,则可以按字段位置排序。
【例25】从stu_info表中查询学生的学号、姓名和年龄,并按年龄降序排序记录。
SELECT sno 学号,sname 姓名,DATEDIFF(year, birth, GETDATE( )) 年龄
FROM stu_info
ORDER BY 3 DESC
上面的语句中,因为表达式DATEDIFF(year, 出生日期, GETDATE( ))在字段名列表中的位置是3,所以ORDER BY子句中的3 DESC,表示了使用表达式DATEDIFF(year, 出生日期, GETDATE( ))的值降序排序记录。
技巧:当字段名比较冗长或者拼写比较复杂时,在ORDER BY子句中使用字段位置会节省拼写时间,降低拼写出错的概率。
其实,本例除了使用位置排序以外,在ORDER BY子句后可以直接放置表达式来排序。例如下面的语句所示。
SELECT sno 学号,sname 姓名,DATEDIFF(year, birth, GETDATE( )) 年龄
FROM stu_info
ORDER BY DATEDIFF(year, birth, GETDATE( )) DESC
--查询前5行数据
在SQL Server中,使用关键字TOP可以轻松完成这一任务。TOP关键字可以限制返回到结果集中的记录个数。
【例26】从stu_info表中,查询生日最大的前5名学生的姓名、生日和手机号码。
SELECT TOP 5 sname 姓名,birth 生日,telephone 手机号码
FROM stu_info
ORDER BY birth
TOP关键字除了上述用法以外,还有一种用法:
TOP n PERCENT
其含义为从顶部开始获取结果集的百分之N。
例如,下面的语句查询stu_info表中以出生日期排序后,前30%的学生信息。
SELECT TOP 30 PERCENT sname 姓名,birth 生日,telephone 手机号码
FROM stu_info
ORDER BY birth
(四)WHERE与ORDER BY的结合使用
ORDER BY子句必须放在WHERE子句的后面,作用是 排序满足查询条件的查询结果集。。
【例27】从stu_info表中查询“telephone”字段不空的学生学号、姓名、手机号码和所属院系,并且按学号升序进行排序。
SELECT sno 学号,sname 姓名, telephone 手机号码,depart 所属院系
FROM stu_info
WHERE telephone IS NOT NULL
ORDER BY 学号
(五) 高级条件查询
-- AND运算符
AND运算符只有当两边操作数均为True时,最后结果才为True。
【例28】从stu_info表中查询计算机系的所有女生,并将结果按学号升序排序。
SELECT *
FROM stu_info
WHERE depart = '计算机系'
AND sex = '女'
ORDER BY sno
【例29】从stu_info表中查询1975年之后、1980年之前出生的所有学生,并将结果按出生日期升序排序。
SELECT *
FROM stu_info
WHERE birth >= '01/01/1975'
AND birth < '01/01/1980'
ORDER BY birth
【例30】从stu_info表中,查询1975年之后、1980年之前出生并且没有E-mail的学生,将结果按出生日期升序排序。
SELECT *
FROM stu_info
WHERE 出生日期 >= '01/01/1975'
AND 出生日期 < '01/01/1976'
AND email IS NULL
ORDER BY 出生日期
--OR运算符
OR运算符只有当两边操作数均为False时,最后结果才为False,只要一边是True则最后结果为True。
【例31】从stu_info表中,查询中文系的所有学生和(或者)外语系的所有学生,并将结果按学号升序排序。
分析:本题两个条件的关系其实是“或”,因为满足任何一个条件就可以通过审核。
SELECT *
FROM stu_info
WHERE depart = '中文系'
OR depart = '外语系'
ORDER BY sno
--AND与OR的优先顺序问题
WHERE子句中可以包含任意数量的AND和OR运算符,并且允许两者结合使用。
【例32】从stu_info表中查询中文系和外语系的所有女生。
SELECT *
FROM stu_info
WHERE depart = '中文系'
OR depart = '外语系'
AND sex = '女'
ORDER BY sno
查看运行结果后会发现一个男生进入了查询结果集中。导致这一错误的根源是运算符的优先级问题。在表达式中,如果同时出现了AND和OR两种运算符,则并非从左到右按顺序运算,而是优先执行AND,然后执行OR运算符。
了解了运算符的优先级后,上面错误的原因就很容易地被找到了。因为上面的条件表达式与下面的表达式等价。
所属院系='中文系' OR (所属院系='外语系' AND性别='女')
而该表达式的意思是:中文系的所有学生和外语系的所有女生,因此,查询结果集中出现了中文系的男生。为了让OR运算符优先执行,可以使用括号,下面的SELECT语句是正确的查询语句。
SELECT *
FROM stu_info
WHERE (depart = '中文系' OR depart = '外语系')
AND sex = '女'
ORDER BY sno
--NOT运算符
NOT运算符的作用是对其后的表达式求反。
【例33】查询出生日期不在1978~1980年之间(包含1978和1980)的所有学生。
SELECT *
FROM stu_info
WHERE birth NOT BETWEEN ‘01/01/1978’ AND ‘12/31/1980’
-- IN运算符
IN运算符的运算规则是:当X在集合{Value1, Value2,……ValueN}中时,表达式X IN (Value1, Value2,……ValueN)为True,而X不在集合{Value1, Value2,……ValueN}中时,上面的表达式为False。
例如,13 IN (2,5,8,13),因为13在集合{2,5,8,13}中,所以表达式的值为True,而4 IN (2,5,8,13),因为4不在集合{2,5,8,13}中,所以表达式的值为False。
【例34】从course表中,查询学分为2、3、4的课程的信息,并按学分降序、课号升序排序。
SELECT *
FROM course
WHERE credit IN (2,3,4)
ORDER BY credit DESC,cno
说明:在IN运算符表达式中,集合必须用圆括号括住,并且各元素之间用逗号(,)分隔。
【例35】从stu_info表中,查询中文系、外语系和计算机系的所有学生,并按院系降序排列。
SELECT *
FROM stu_info
WHERE depart IN ('中文系','外语系','计算机系')
ORDER BY depart DESC
IN运算符还有一个反向运算符——NOT IN。
【例36】从stu_info表中,查询除中文系、外语系和计算机系以外的其他系的学生,并按院系降序排列。
SELECT *
FROM stu_info
WHERE depart NOT IN ('中文系','外语系','计算机系')
ORDER BY depart DESC
IN运算符和OR运算符实现的功能是相同的。那为什么使用IN运算符呢?因为IN运算符有如下优点。
● 当条件很多时,使用IN运算符会使语句更加简洁、清楚。例如,如果将例9.36使用OR运算符改写则其语句为:
SELECT *
FROM stu_info
WHERE depart='中文系'
OR depart ='外语系'
OR depart ='计算机系'
ORDER BY depart DESC
很明显,此时,使用IN运算符会比OR运算符更简洁、清楚。
● IN运算符的执行速度要比OR运算符的更快。
● IN运算符最大的优点是:其后条件列表集合中,可以放置其他SELECT语句,即子查询。
--LIKE运算符与“%”通配符
模糊查询,下面主要介绍LIKE运算符和“%”通配符结合使用。
在SQL Server中,“%”通配符代表0个或多个字符。
【例37】从stu_info表中查询所有姓名中包含“三”字的学生信息。
为了更好地体现本例,下面在数据表中插入两条新记录,插入语句如下。
INSERT INTO stu_info(sno,sname,sex,birth) VALUES
('0011', '刘三姐', '男',‘12/20/1981’),
('0012','三胜','男',‘05/15/1983’)
执行查询:
SELECT *
FROM stu_info
WHERE sname LIKE '%三%'
如果将“%三%”中的第一个“%”去掉,则查询结果:
SELECT *
FROM stu_info
WHERE sname LIKE '三%'
这次运行结果中只包含了一条记录,因为字符串“三%”只代表头一个字为“三”的所有字符串。而如果将查询语句改为下面的语句,则只能查询最后一个字为“三”的所有学生。:
SELECT *
FROM stu_info
WHERE RTRIM(sname) LIKE '%三'
说明:RTRIM函数用于将字符串右侧的空格去掉,在本例中是将sname字段值右侧的空格去掉。由于,在表结构的设计中sname字段的类型是nchar,宽度是20,所以其值的宽度不满20个字符时,SQL Server自动用空格填满了剩余位置,因此,本例中需要使用RTRIM函数将右侧的空格去掉。
-- “_”通配符的使用
下画线(_)通配符,它只代表任意一个字符。例如,“刘_”代表以“刘”字开头的,最多由两个汉字组成的字符串。
【例38】从stu_info表中,查询姓“刘”,而且名字最多由两个字组成的学生。
SELECT *
FROM stu_info
WHERE RTRIM(sname) LIKE '刘_'
“_”通配符也可以不与字符组合,而单独使用。
【例39】从stu_info表中,查询名字由两个字组成的所有学生。
SELECT *
FROM stu_info
WHERE RTRIM(sname) LIKE '__'
-- “[ ]”通配符的使用
【例40】从stu_info表中,查询姓张、李或刘的所有学生,并按姓名升序排序。
SELECT *
FROM stu_info
WHERE sname LIKE '[张李刘]%'
ORDER BY sname
如果在方括号内的第一个位置输入符号“^”,则表示取反向值。
【例41】从stu_info表中,查询除姓张、李或刘以外的所有学生,并按姓名升序排序。
SELECT *
FROM stu_info
WHERE sname LIKE '[^张李刘]%'
ORDER BY sname
-- 定义转义字符(ESCAPE关键字)
如果想要查询最后两个字符为百分之五的所有字符串,即将“%5%”中,第二个“%”视为是普通字符,而不是通配符,此时,便应该定义和使用转义字符。在SQL Server中,使用ESCAPE关键字定义转义字符。
例如,要查询最后两个字符为百分之五(5%)的所有字符串,其LIKE语句为:
LIKE '%5#%' ESCAPE '#'
其中,ESCAPE '#'定义了转义字符“#”,它表示紧跟着“#”后的“%”为普通字符,而并非通配符。
注意:只有紧跟在转义字符后面的通配符才被视为转义字符,例如,如果上面的LIKE语句为:
LIKE '%5#%%' ESCAPE '#'
则表示要查询的是包含百分之五(5%)的所有字符串。这里最后一个“%”仍当做通配符来使用,只有紧跟着“#”的“%”(第二个)才被当做普通字符。