• 第四章 高级查询


    4.1 简单子查询

      究竟什么是子查询?子查询有什么用?带着这样的疑问,我们不妨先来解决本章第一个问题。

      学员信息表(stuInfo)和学员成绩表(stuMarks)的数据。

    stuName stuNo stuSex stuAge stuSeat stuAddress
    张秋丽 s25301 18 1 北京海淀
    李思文 s25303 22 2 河南洛阳
    李文才 s25302 85 3 地址不详
    欧阳俊雄 s25304 28 4 新疆
    梅超风 s25318 23 5 地址不详
    ExamNo stuNo writtenExam labExam
    s271811 s25303 90 58
    s271813 s25302 60 90
    s271816 s25301 87 82

      问题:查看年龄比“李思文”大的学员,要求显示这些学员的信息。

      实现方法一:采用T-SQL变量实现,SQL语句如示例1所示。

      示例1

      Declare @age int    ---定义变量,用于存放“李思文”的年龄

      Select @age = stuAge From stuInfo where stuName = '李思文'   ---求出“李思文”的年龄

      Select * from stuInfo where stuAge > @age   ---筛选比“李思文”年龄大的学员

      GO

      实现方法二:采用子查询实现,SQL语句如示例2所示。

      我们可以合并上述两步。

      将语句  Select * from stuInfo where stuAge > @age

      替换为如示例2所示的语句

      示例2

      Select * from stuInfo Where stuAge > ( select stuAge from stuInfo where stuName > '李思文')

      Go

      你一定明白了,上述查询中的“( select stuAge from stuInfo where stuName > '李思文')”部分,就是子查询,因为它嵌入到查询中作为Where条件的一部分。

      所以它在Where语句中的一般用法如下。

      Select ··· from 表1 Where 字段1 > (子查询)

      除了“>” 号外,还可以使用其他运算符号,习惯上,外面的查询称为父查询,括号中嵌入的查询称为子查询。SQL Server 执行时,先执行子查询部分,求出子查询部分的值,然后再执行整个父查询。它的执行效率比采用SQL变量实现的方案要高,所以推荐采用子查询。因为子查询作为Where条件的一部分,所以还可以和Update、Insert、Delete一起使用,语法类似于Select语句。

      注: 将子查询和比较运算符联合使用,必须保证子查询返回的值不能多于一个。

      上述子查询将多个结果集合并在一起,除此之外,还可以将多表间的数据组合在一起,从而替换连接(Join)查询。

      问题:查询笔试刚好通过60分的学员名单。

      实现方法一: 采用表连接。

      示例3

      Select stuName from stuInfo inner join stuMarks

        On stuInfo.stuNo = stuMarks.stuNo where writtenExam = 60

      GO

      除了采用表连接以外,还可以采用子查询替换上述连接。

      实现方法二:采用子查询

      示例4

      Select stuName From stuInfo where stuNo = (select stuNo from stuMarks where writtenExam = 60)  GO

      注:一般来说,表连接都可以用子查询替换,但反过来说却不一定,有的子查询不能用表连接来替换。子查询比较灵活、方便,形式多样,适合于作为查询的筛选条件。而表连接更适合于查看多表的数据。

    4.2 IN 和 Not In 子查询

      使用“=”、“>” 等比较运算符时,要求子查询只能返回一条或空的记录。在SQL Server中,当子查询跟随在 = 、!= 、<、<=、>、>=之后,不允许子查询返回多条记录。例如上例查询笔试刚好及格的学员信息,成绩表中刚好只有一条记录满足条件:李文才(s25302)的笔试成绩刚好60分。如果有多条记录满足条件,既有多个学员的笔试成绩为60分,采用上述子查询将出现编译错误。

      示例5

      /*-- 采用in子查询查询参加考试的学员名单 --*/

      Select stuName from stuInfo where stuNo in (select stuNo from stuMarks) GO

      同理,如果希望查询未参加考试的学员名单呢?您一定想到了,加上否定的Not 即可。

      /*-- 采用Not in 子查询,查看未参加考试的学员名单 --*/

      Select stuName from stuInfo where stuNo Not in (select stuNo from stuMarks) GO

    4.3 Exists 和 Not Exists 子查询

      Exists 语句我们并不陌生,在学习创建库和创建表的语句时曾提前用过,它是一个存在检测的子查询语句。例如,如果存在数据库stuDB,则先删除它,然后重新创建。

      IF Exists (select * from sysDatabases where name = 'stuDB')

        Drop DataBase stuDB

      Create DataBase stuDB

      ··· 创建的库代码略

      从理论上讲,Exists 也可以作为Where语句的子查询,但一般用于IF语句的存在检测。其基本语法如下:

      IF Exists(子查询)

        语句

      如果子查询的结果非空,则Exists(子查询)将返回真(true),否则返回假(false)。

      问题:检查本次考试,本班如果有人笔试成绩达到80分以上,则每人提2分,否则,每人允许提5分。

      示例8

      /*-- 采用Exists子查询,进行酌情加分 --*/

      IF Exists (select * from stuMarks where writtenExam > 80)

        Begin

        print '本班有人笔试成绩高于80分,每人只加2分,加分后的成绩为:'

        Update stuMarks set writtenExam = writtenExam + 2

        Select * from stuMarks

        End

      Else

        Begin

        print '本班无人笔试成绩高于80分,每人可以加5分,加分后的成绩为:'

        Update stuMarks set writtenExam = writtenExam + 5

        Select * from stuMarks

        End

      Go

      Exists 和 in 一样,同样允许添加Not取反,表示不存在。

      问题,检查本次考试,本班如果没有一人通过考试(笔试和机试成绩都>60分),则试题偏难,每人加3分,否则,每人只加1分。

      示例9

      /*--- 采用Not Exists子查询,根据试题难度加分 ---*/

      If Not Exists ( select * from stuMarks where writtenExam > 60 and LabExam > 60)

        Begin 

          Print '本班无人通过考试,试题骗难,每人加3分,加分后成绩为:'

          Update stuMarks set writtenExam = writtenExam + 3 , labExam = labExam + 3

          Select * from stuMarks

        End

      Else

        Begin

          Print '本班考试成绩一般,每人只加1分,加分后的成绩为:'

          Update stuMarks set writtenExam = writtenExam + 1, labExam = labExam + 1

          Select * from stuMarks

        End

      Go

    4.4 T-SQL 语句的综合应用

      假定目前本次考试学员信息表(stuInfo)和学员成绩表(stuMarks)的原始数据如下:

    stuName stuNo stuSex stuAge stuSeat stuAddress
    张秋丽 s25301 18 1 北京海淀
    李思文 s25303 22 2 河南洛阳
    李文才 s25302 85 3 地址不详
    欧阳俊雄 s25304 28 4 新疆
    梅超风 s25318 23 5 地址不详
    ExamNo stuNo writtenExam LabExam
    s271811 s25303 93 59
    s271813 s25302 63 91
    s271816 s25301 90 83
    s271817 s25318 63 53

      问题:

      (1)统计本次考试的缺考情况,结果如图第一个记录集所示。

    应到人数 实到人数 缺考人数
    5 4 1
    姓名 学号 笔试成绩 机试成绩 是否通过
    张秋丽 s25301 90 89
    李思文 s25303 93 65
    李文才 s25302 63 97
    欧阳俊雄 s25304 缺考 缺考
    梅超风 s25318 63 59
    总人数 通过人数 通过率
    5 3 60%

      (2)提取学员的成绩信息并保存结果,包括学员姓名、学号、笔试成绩、机试成绩、是否通过。

      (3)比较笔试平均分和机试平均分,较低者进行循环提分,但提分后最高分不能超过97分。

      (4)提分后,统计学员的成绩和通过情况,如上图第二个记录集所示。

      (5)提分后统计学员的通过率情况,如上图第三个记录集所示。

      示例10

      /*--- 本次考试的原始数据 ---*/

      select * from stuInfo

      select * from stuMarks

      /*--- 统计考试缺考情况 ---*/

      select 应到人数=(select Count(*) from stuInfo),

          实到人数=(select count(*) from stuMarks),

          缺考人数=((select count(*) from stuInfo) - (select count(*) from stuMarks))

      /*--- 统计考试通过情况,并将统计结果存放在新表newTable中 ---*/

      IF Exists (select * from sysobjects where name = 'newTable')

        Drop table newTable

      Select stuName, stuInfo.stuNo, writtenExam, labExam,

        isPass = Case

            When writtenExam >= 60 and labExam >=60 Then 1

            Else 0

            End

        Info newTable from stuInfo Left Join stuMarks

          On stuInfo.stuNo = stuMarks.stuNo

      /*--- 酌情加分:比较笔试和机试平均分,哪科偏低,就给哪科提分 ---*/

      Declare @avgWritten numeric (4,1), @avgLab numeric(4,1) ---定义变量存放笔试和机试平均分

      Select @avgWritten = Avg(writtenExam) from newTable where writtenExam IS Not null

      Select @avgLab = Avg(labExam) from newTable where labExam IS Not null

      IF @avgWritten<@avgLab ---比较笔试和机试平均分,看哪科偏低

        While (1=1) ---循环给笔试加分,最高分不能超过97分

          Begin

            Update newTable set writtenExam = writtenExam + 1

            If(select Max(writtenExam) From newTable ) >= 97

              Break

          End

      Else

        While(1=1)  ---循环给机试加分,最高分不能超过97分

          Begin

            Update newTable set labExam = labExam + 1

            If(select Max(labExam) from newTable) >= 97

              Break

          End

      --- 因为提分,所以需要更新isPass(是否通过)列的数据

      Update newTable

        Set isPass = Case

          When writtenExam >= 60 and labExam >= 60 Then 1

          Else 0

        End

      /*--- 显示考试最终通过情况 ---*/

      Select 姓名=stuName, 学号=stuNo

        笔试成绩=Case

          when writtenExam IS null then '缺考'

          Else convert(varchar(5), writtenExam)

        End

        机试成绩=Case

          When labExam IS null then '缺考'

          Else convert(varchar(5),labExam)

        End

        是否通过=Case

          When isPass = 1 then '是'

          Else '否'

        End

      From newTable

      /*--- 显示通过率及通过人数 ---*/

      Select 总人数 = Count(*) , 通过人数 = Sum(isPass),

        通过率 = (convert (varchar(5), avg(isPass*100)) + '%' ) From newTable

      Go

  • 相关阅读:
    一种简洁明了的权限管理系统
    css小技巧(1)
    多功能旋转木马轮播实例
    jquery双向列表选择器select版
    jquery双向列表选择器DIV模拟版
    单击页面任何地方关闭隐藏层
    用户登录体验之密码框设计
    扁平化设计的美感
    分析网站的用户行为
    app的架构和导航设计
  • 原文地址:https://www.cnblogs.com/jiazengtao/p/3159882.html
Copyright © 2020-2023  润新知