SQL Server(三)-查询数据(1)

(一)普通查询
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)。

在此还需要提醒读者一个问题,如果日期字段的数据类型是datetimesmalldatetime,则因为其数据中包含时间,所以需要使用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%)的所有字符串。这里最后一个“%”仍当做通配符来使用,只有紧跟着“#”的“%”(第二个)才被当做普通字符。


 

猜你喜欢

转载自blog.csdn.net/qq_37503890/article/details/88559804