SQL(2)--复杂查询

介绍多表查询等复杂SQL语句。

关系数据库的查询结果都是一个结果表(也是关系)

集聚函数

基本语法

  • 统计元组个数
    • COUNT(*)
  • 统计一列中值的个数
    • COUNT([DISTINCT|ALL]<列名>)
  • 计算一列值的总和(此列必须为数值型)
    • SUM([DISTINCT|ALL]<列名>)
  • 计算一列值的平均值(此列必须为数值型)
    • AVG([DISTINCT|ALL]<列名>)
  • 求一列中的最大值和最小值
    • MAX([DISTINCT|ALL]<列名>)
    • MIN([DISTINCT|ALL]<列名>)

例子

  • 查询选修1号课程的学生最高分数

    • SELECTMAX(Grade)
         FROM SC
         WHERE Cno='1';
      
  • 查询学生201215012选修课程的总学分数

    • SELECT SUM(Ccredit)
          FROM  SC,Course
          WHERE Sno='201215012'                
          AND SC.Cno=Course.Cno; 
      

GROUP BY 子句

细化聚集函数的作用对象

  • 如果未对查询结果分组,聚集函数将作用于整个查询结果
  • 对查询结果分组后,聚集函数将分别作用于每个组
  • 按指定的一列或多列值分组,值相等的为一组

HAVING短语与WHERE子句的区别:

  • 作用对象不同
  • WHERE子句作用于基表或视图,从中选择满足条件的元组
  • HAVING短语作用于组,从中选择满足条件的组
  • WHERE子句不能使用聚合函数!

例子

  1. 求各个课程号及相应的选课人数

       SELECT Cno, COUNT(Sno)
       FROM    SC
       GROUP BY Cno; 
    
  2. 查询选修了3门以上课程的学生学号

     SELECT Sno
         FROM  SC
         GROUP BY Sno
         HAVING  COUNT(*) >3;      
    
  3. 查询平均成绩大于等于90分的学生学号和平均成绩

        SELECT  Sno, AVG(Grade)
        FROM  SC
        GROUP BY Sno
        HAVING AVG(Grade)>=90;
    

    这里只能使用HAVING,不能使用WHERE

ORDER BY子句

  • 可以按一个或多个属性列排序
    • 优先级逐渐降低
  • 升序:ASC;
  • 降序:DESC;
  • 缺省值为升序
  • 对于空值,排序时显示的次序由具体系统实现来决定

例子

  1. 查询选修了3号课程的学生的学号及其成绩,查询结果按分数降序排列

        SELECT Sno, Grade
        FROM    SC
        WHERE  Cno= ' 3 '
        ORDER BY Grade DESC;
    
  2. 查询全体学生情况,查询结果按所在系的系号升序排列,同一系中的学生按年龄降序排列

        SELECT  *
        FROM  Student
        ORDER BY Sdept, Sage DESC;  
    

连接查询

  • 连接查询:同时涉及两个以上的表的查询

  • 连接条件或连接谓词:用来连接两个表的条件

  • 一般格式:

    [<表名1>.]<列名1> <比较运算符> [<表名2>.]<列名2>

    [<表名1>.]<列名1>BETWEEN [<表名2>.]<列名2>AND[<表名2>.]<列名3>

  • 连接字段:连接谓词中的列名称

    • 连接条件中的各连接字段类型必须是可比的,但名字不必相同

(非)等值连接查询

等值连接:连接运算符为=,这里与Join操作等价。

例子

  1. 查询每个学生及其选修课程的情况

        SELECT  Student.*, SC.*
        FROM     Student, SC
        WHERE  Student.Sno = SC.Sno;
    
  2. 一条SQL语句可以同时完成选择和连接查询,这时WHERE子句是由连接谓词和选择谓词组成的复合条件。

    查询选修2号课程且成绩在90分以上的所有学生的学号和姓名

        SELECT  Student.Sno, Sname
        FROM     Student, SC
        WHERE  Student.Sno=SC.Sno  AND                          
               SC.Cno=' 2 ' AND SC.Grade>90;
    

执行过程

嵌套循环法(NESTED-LOOP)

  • 首先在表1中找到第一个元组,然后从头开始扫描表2,逐一查找满足连接件的元组,找到后就将表1中的第一个元组与该元组拼接起来,形成结果表中一个元组。
  • 表2全部查找完后,再找表1中第二个元组,然后再从头开始扫描表2,逐一查找满足连接条件的元组,找到后就将表1中的第二个元组与该元组拼接起来,形成结果表中一个元组。
  • 重复上述操作,直到表1中的全部元组都处理完毕

可以发现,等值连接的复杂度很高,为O(m* n)。

自身连接

  • 自身连接:一个表与其自己进行连接
  • 需要给表起别名以示区别
  • 由于所有属性名都是同名属性,因此必须使用别名前缀

例子

  1. 查询每一门课的间接先修课(即先修课的先修课)

     SELECT  FIRST.Cno, SECOND.Cpno
         FROM  Course  FIRST, Course  SECOND
         WHERE FIRST.Cpno = SECOND.Cno;
    

外连接

外连接与普通连接的区别

  • 普通连接操作只输出满足连接条件的元组
  • 外连接操作以指定表为连接主体,将主体表中不满足连接条件的元组一并输出
  • 左外连接
    • 列出左边关系中所有的元组
  • 右外连接
    • 列出右边关系中所有的元组

例子

    SELECT Student.Sno, Sname, Ssex, Sage, Sdept, Cno, Grade
    FROM  Student  LEFT OUT JOIN SC ON    
                 (Student.Sno=SC.Sno); 
53960143076

多表连接

两个以上的表进行连接。

MongoDB不提供这种操作:

  • JOIN很慢
  • 多级扩展能力差,代价太高

例子

  1. 查询每个学生的学号、姓名、选修的课程名及成绩

      SELECT Student.Sno, Sname, Cname, Grade
      FROM   Student, SC, Course    /*多表连接*/
      WHERE Student.Sno = SC.Sno 
                   AND SC.Cno = Course.Cno;
    

嵌套查询

  • 一个SELECT-FROM-WHERE语句称为一个查询块

  • 将一个查询块嵌套在另一个查询块的WHERE子句或HAVING短语的条件中的查询称为嵌套查询

  • 上层的查询块称为外层查询或父查询

  • 下层查询块称为内层查询或子查询

  • SQL语言允许多层嵌套查询

    • 即一个子查询中还可以嵌套其他子查询
  • 子查询的限制

    • 不能使用ORDERBY子句
    • 因为ORDER BY 结果为有序的,不满足关系的定义,只能作为最后的生成结果

带有IN谓词的子查询

  1. 查询与“刘晨”在同一个系学习的学声

    SELECT Sno, Sname, Sdept
             FROM Student
             WHERE Sdept  IN
                      (SELECT Sdept
                       FROM Student
                       WHERE Sname= ' 刘晨 ');
    /*用自身连接表示*/
     SELECT  S1.Sno, S1.Sname,S1.Sdept
          FROM     Student S1,Student S2
          WHERE  S1.Sdept = S2.Sdept  AND
               S2.Sname = '刘晨';
    
  2. 查询选修了课程名为“信息系统”的学生学号和姓名

    SELECT Sno,Sname                
         FROM    Student                         
         WHERE Sno  IN
                 (SELECT Sno                    
                  FROM    SC                         
                  WHERE  Cno IN
                         (SELECT Cno             
                           FROM Course           
                           WHERE Cname= '信息系统' )
                  );
    /*用连接查询表示*/
    SELECT Sno,Sname
          FROM    Student,SC,Course
          WHERE   Student.Sno = SC.Sno  AND
                  SC.Cno = Course.Cno AND
                  Course.Cname='信息系统';            
    

带有比较运算符的子查询

  • 当能确切知道内层查询返回单值时,可用比较运算符(>,<,=,>=,<=,!=或< >)

  • 由于一个学生只可能在一个系学习,则可以用 = 代替IN :

        SELECT Sno,Sname,Sdept
        FROM    Student
        WHERE Sdept   =
                       (SELECT Sdept
                        FROM    Student
                        WHERE Sname= '刘晨');
    
  • 注意,用比较运算符取嵌套,只能SELECT一个属性,且为数值类型。

  • 不相关子查询

    • 子查询的查询条件不依赖于父查询
    • 由里向外逐层处理。即每个子查询在上一级查询处理之前求解,子查询的结果用于建立其父查询的查找条件。
  • 相关子查询

    • 子查询的查询条件依赖于父查询
    • 首先取外层查询中表的第一个元组,根据它与内层查询相关的属性值处理内层查询,若WHERE子句返回值为真,则取此元组放入结果表
    • 然后再取外层表的下一个元组
    • 重复这一过程,直至外层表全部检查完为止

例子

  1. 找出每个学生超过他选修课程平均成绩的课程号

    SELECT Sno, Cno
        FROM    SC x
        WHERE Grade >= ( SELECT AVG(Grade) 
                         FROM  SC y
                         WHERE y.Sno = x.Sno );
    /*用连接查询表示*/
    SELECT First.Sno, First.Cno
     FROM SC First JOIN (
         SELECT Sno, AVG(Grade) as A_Grade
            FROM SC
            GROUP BY Sno) SA
            ON First.Sno = SA.Sno
         WHERE First.Grade > SA.A_Grade
    

带有ANY(SOME)或ALL谓词的子查询

使用ANY或ALL谓词时必须同时使用比较运算

若子查询中不是唯一的,使用ANY/ALL可以使用比较运算符

语义为:

> ANY 大于子查询结果中的某个值

>ALL 大于子查询结果中的所有值

>=ANY 大于等于子查询结果中的某个值

<=ANY 小于等于子查询结果中的某个值

=ANY 等于子查询结果中的某个值

!=(或<>)ALL 不等于子查询结果中的任何一个值

例子

  1. 查询非计算机科学系中比计算机科学系任意一个学生年龄小的学生姓名和年龄

    SELECT Sname,Sage
        FROM    Student
        WHERE Sage < ANY ( SELECT  Sage
                           FROM    Student
                           WHERE Sdept= ' CS ')
         AND Sdept <> ‘CS ' ;           /*父查询块中的条件 */
    
    /*用聚集函数实现*/
    
    SELECT Sname,Sage
         FROM   Student
         WHERE Sage < 
                      ( SELECT MAX(Sage)
                         FROM Student
                         WHERE Sdept= 'CS ')
          AND Sdept <> ' CS ';
    
  2. 查询非计算机科学系中比计算机科学系所有学生年龄都小的学生姓名及年龄

    SELECT Sname,Sage
        FROM Student
        WHERE Sage < ALL
                     (SELECT Sage
                       FROM Student
                       WHERE Sdept= ' CS ')
          AND Sdept <> ' CS ’;
    /*用聚集函数实现*/
    SELECT Sname,Sage
        FROM Student
        WHERE Sage < 
                   (SELECT MIN(Sage)
                    FROM Student
                    WHERE Sdept= ' CS ')
        AND Sdept <>' CS ';
    

带有EXISTS谓词的子查询

EXISTS谓词

  • 存在量词
  • 带有EXISTS谓词的子查询不返回任何数据,只产生逻辑真值“true”或逻辑假值“false”。
  • 若内层查询结果非空,则外层的WHERE子句返回真值
  • 若内层查询结果为空,则外层的WHERE子句返回假值
  • 由EXISTS引出的子查询,其目标列表达式通常都用* ,因为带EXISTS的子查询只返回真值或假值,给出列名无实际意义。

例子

  1. 查询所有选修了1号课程的学生姓名。

    思路

    • 本查询涉及StudentSC关系
    • Student中依次取每个元组的Sno值,用此值去检查SC
    • SC中存在这样的元组,其Sno值等于此Student.Sno值,并且其Cno=‘1’,则取此Student.Sname送入结果表
    SELECT Sname
         FROM Student
         WHERE EXISTS
                       (SELECT *
                        FROM SC
                        WHERE Sno=Student.Sno AND Cno= ' 1 ');
    
  2. 查询没有选修1号课程的学生姓名。

    SELECT Sname
         FROM     Student
         WHERE NOT EXISTS
                       (SELECT *
                        FROM SC
                        WHERE Sno = Student.Sno AND    Cno='1');
    

难点

  • 不同形式的查询间的替换

    • 一些带EXISTS或NOT EXISTS谓词的子查询不能被其他形式的子查询等价替换

    • 所有带IN谓词、比较运算符、ANY和ALL谓词的子查询都能用带EXISTS谓词的子查询等价替换

    • 查询与“刘晨”在同一个系学习的学生

      • 可以用带EXISTS谓词的子查询替换

      • SELECT Sno,Sname,Sdept
             FROM Student S1
              WHERE EXISTS
                         (SELECT *
                             FROM Student S2
                             WHERE S2.Sdept = S1.Sdept AND
                                   S2.Sname = '刘晨');
        
  • 用EXISTS/NOT EXISTS实现全称量词(难点)

    • 查询选修了全部课程的学生姓名

      • 不存在一门课,这个学生没有选

      • SELECT Sname
                FROM Student
                WHERE NOT EXISTS
                              (SELECT *
                                FROM Course
                                WHERE NOT EXISTS
                                              (SELECT *
                                               FROM SC
                                               WHERE Sno= Student.Sno
                                                     AND Cno= Course.Cno
                                              )
                               );
        
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,636评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,890评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,680评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,766评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,665评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,045评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,515评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,182评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,334评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,274评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,319评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,002评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,599评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,675评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,917评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,309评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,885评论 2 341

推荐阅读更多精彩内容

  • 这会儿闲着, 所以写几笔; 说到2017, 先定个小目标。 比如换个发型, 然而也从来没有过发型; 百斤左右的体重...
    XX的Xixi阅读 104评论 0 0
  • 总是自信自己人缘好,跟谁都能聊得来,可是每当自己遇到问题,或者心情不好时,翻遍手机,脑海里都想不出一个可以倾述的对...
    6fd7152783bc阅读 380评论 0 0
  • 感恩祺予提醒我,我们想做某件事家人反对,并不是说你要做的那件事有错,而是我们过去也种子过不了解他人的种子开花结果了...
    骞卉阅读 218评论 0 0
  • 梅子不落阅读 183评论 0 1