SQL进阶教程学习笔记

在学习《SQL进阶教程学习》的记录笔记,现学现用效率真的很高,带着问题学习,记忆会深很多很多。


  • CASE表达式

CASE表达式有简单CASE表达式(simple caseexpression)和搜索CASE表达式(searched case expression)两种写法。

    --简单CASE表达式
    CASE sex
      WHEN '1' THEN ’男’
      WHEN '2' THEN ’女’
    ELSE ’其他’ END


    --搜索CASE表达式
    CASE WHEN sex ='1'THEN’男’
        WHEN sex ='2'THEN’女’
    ELSE ’其他’ END

tips:

  1. 因为GROUP BY子句比SELECT语句先执行,所以在GROUP BY子句中引用在SELECT子句里定义的别称是不被允许的。
  • 用一条SQL语句进行不同条件的统计:

进行不同条件的统计是CASE表达式的著名用法之一。

  • 新手用WHERE子句进行条件分支,高手用SELECT子句进行条件分支:
    -- 男性人口
    SELECT pref_name,
          SUM(population)
      FROM PopTbl2
     WHERE sex ='1'
     GROUP BY pref_name;


    -- 女性人口
    SELECT pref_name,
          SUM(population)
      FROM PopTbl2
     WHERE sex ='2'
     GROUP BY pref_name;
    SELECT pref_name,
          --男性人口
          SUM( CASE WHEN sex ='1'THEN population ELSE 0 END) AS cnt_m,
          --女性人口
          SUM( CASE WHEN sex ='2'THEN population ELSE 0 END) AS cnt_f
      FROM  PopTbl2
     GROUP BY pref_name;
  • 用CHECK约束定义多个列的条件关系

  • 在UPDATE语句里进行条件分支
    --条件1
    UPDATE Salaries
      SET salary = salary * 0.9
     WHERE salary >= 300000;


    --条件2
    UPDATE Salaries
      SET salary = salary * 1.2
     WHERE salary >= 250000 AND salary < 280000;

两个UPDATE函数依次执行的话,可能会反复操作同一个数据,因此会造成错误。因此使用UPDATE与CASE函数结合,而且因为只需执行一次,所以速度也更快:

    --用CASE表达式写正确的更新操作
    UPDATE Salaries
      SET salary = CASE WHEN salary >= 300000
                        THEN salary * 0.9
                        WHEN salary >= 250000 AND salary < 280000
                        THEN salary * 1.2
                        ELSE salary END;
  • 主键值调换:
    --1.将a转换为中间值d
    UPDATE SomeTable
      SET p_key ='d'
     WHERE p_key ='a';


    --2.将b调换为a
    UPDATE SomeTable
      SET p_key ='a'

      WHERE p_key ='b';


     --3.将d调换为b
     UPDATE SomeTable
        SET p_key ='b'
      WHERE p_key ='d';

​​​​​​​没有必要执行3次UPDATE操作,因此可以用UPDATE结合CASE:

    --用CASE表达式调换主键值
    UPDATE SomeTable
      SET p_key = CASE WHEN p_key ='a'
                        THEN 'b'
                        WHEN p_key ='b'
                        THEN 'a'
                        ELSE p_key END
     WHERE p_key IN ('a', 'b');
  • 表之间的数据匹配
    --表的匹配:使用IN谓词
    SELECT course_name,
          CASE WHEN course_id IN
                        (SELECT course_id FROM OpenCourses
                          WHERE month = 200706) THEN'○'
                ELSE'×'END AS "6月",
          CASE WHEN course_id IN
                        (SELECT course_id FROM OpenCourses
                          WHERE month = 200707) THEN'○'
                ELSE'×'END AS "7月",
          CASE WHEN course_id IN
                        (SELECT course_id FROM OpenCourses
                          WHERE month = 200708) THEN'○'
                ELSE'×'END  AS "8月"
      FROM CourseMaster;


    --表的匹配:使用EXISTS谓词
    SELECT CM.course_name,
          CASE WHEN EXISTS
                        (SELECT course_id FROM OpenCourses OC
                          WHERE month = 200706

                              AND OC.course_id = CM.course_id) THEN'○'
                  ELSE'×'END AS "6月",
              CASE WHEN EXISTS
                          (SELECT course_id FROM OpenCourses OC
                            WHERE month = 200707
                              AND OC.course_id = CM.course_id) THEN'○'
                  ELSE'×'END AS "7月",
              CASE WHEN EXISTS
                          (SELECT course_id FROM OpenCourses OC
                            WHERE month = 200708
                              AND OC.course_id = CM.course_id) THEN'○'
                  ELSE'×'END  AS "8月"
        FROM CourseMaster CM;

tips:

使用EXISTS时还要把两个表格的相同列表示出来。

  • 自连接
  • 排序

方法一、使用窗口函数:

    --排序:使用窗口函数
    SELECT name, price,
          RANK() OVER (ORDER BY price DESC) AS rank_1,
          DENSE_RANK() OVER (ORDER BY price DESC) AS rank_2
      FROM Products;

​​​​​​​tips:

  1. rank_1跳过了之后的位次,rank_2没有跳过,而是连续排序。

方法二、使用自连接:

    --排序从1开始。如果已出现相同位次,则跳过之后的位次
    SELECT P1.name,
          P1.price,
          (SELECT COUNT(P2.price)
              FROM Products P2
            WHERE P2.price > P1.price) + 1 AS rank_1
      FROM Products P1
      ORDER BY rank_1;

​​​​​​​

  • 三值逻辑和NULL:

二值逻辑:true、false

三值逻辑(three-valued logic):true、false、unknown

两种NULL分别指的是“未知”(unknown)和“不适用”(not applicable,inapplicable)。“未知”指的是“虽然现在不知道,但加上某些条件后就可以知道”;而“不适用”指的是“无论怎么努力都无法知道”。

三个真值之间的优先级顺序:

  1. AND的情况: false > unknown > true
  2. OR的情况: true > unknown > false

  • HAVING子句的力量:

SQL是一种基于“面向集合”思想设计的语言,同样具备这种设计思想的语言很少;最开始学习过了某种理念的语言后,心理上会形成思维定式,从而妨碍我们理解另一种理念的语言。

  • 寻找缺失的编号
    -- 如果有查询结果,说明存在缺失的编号
    SELECT’存在缺失的编号’AS gap
      FROM SeqTbl
    HAVING COUNT(*) <> MAX(seq);

​​​​​​​

如果这个查询结果有1行,说明存在缺失的编号;如果1行都没有,说明不存在缺失的编号。

    -- 查询缺失编号的最小值
    SELECT MIN(seq + 1) AS gap
      FROM SeqTbl
     WHERE (seq+ 1) NOT IN ( SELECT seq FROM SeqTbl);

tips:

  1. HAVING子句是可以单独使用的
  • 用HAVING子句进行子查询:求众数

众数(mode):指的是在群体中出现次数最多的值.

    --求众数的SQL语句(1):使用谓词
    SELECT income, COUNT(*) AS cnt
      FROM Graduates
     GROUP BY income
    HAVING COUNT(*) >= ALL ( SELECT COUNT(*)
                              FROM Graduates
                              GROUP BY income);
    --求众数的SQL语句(2):使用极值函数
    SELECT income, COUNT(*) AS cnt
      FROM Graduates
     GROUP BY income
    HAVING COUNT(*) >=  ( SELECT MAX(cnt)
                            FROM ( SELECT COUNT(*) AS cnt
                                    FROM Graduates
                                  GROUP BY income) TMP ) ;

​​​​​​​

  • 用HAVING子句进行自连接:求中位数

中位数(median):指的是将集合中的元素按升序排列后恰好位于正中间的元素。

    --求中位数的SQL语句:在HAVING子句中使用非等值自连接
    SELECT AVG(DISTINCT income)
      FROM (SELECT T1.income
              FROM Graduates T1, Graduates T2
            GROUP BY T1.income

            --S1的条件
      HAVING SUM(CASE WHEN T2.income >= T1.income THEN 1 ELSE 0 END)
                >= COUNT(*) / 2
            --S2的条件
        AND SUM(CASE WHEN T2.income <= T1.income THEN 1 ELSE 0 END)
                >= COUNT(*) / 2 ) TMP;
  • 查询不包含NULL的集合

COUNT函数的使用方法有COUNT(*)和COUNT(列名)两种,它们的区别有两个:

  1. 第一个是性能上的区别;
  2. 第二个是COUNT(*)可以用于NULL,而COUNT(列名)与其他聚合函数一样,要先排除掉NULL的行再进行统计。第二个区别也可以这么理解:COUNT(*)查询的是所有行的数目,而COUNT(列名)查询的则不一定是。
    --查询“提交日期”列内不包含NULL的学院(1):使用COUNT函数
    SELECT dpt
      FROM Students
     GROUP BY dpt
    HAVING COUNT(*) = COUNT(sbmt_date);
    --查询“提交日期”列内不包含NULL的学院(2):使用CASE表达式
    SELECT dpt
      FROM Students
     GROUP BY dpt
    HAVING COUNT(*) = SUM(CASE WHEN sbmt_date IS NOT NULL
                              THEN 1
                              ELSE 0 END);

tips:

  1. 表不是文件,记录也没有顺序,所以SQL不进行排序。
  2. SQL不是面向过程语言,没有循环、条件分支、赋值操作。(这个思维模式真的很难改变,我的第一反应就是循环、条件分支)
  3. SQL通过不断生成子集来求得目标集合。SQL不像面向过程语言那样通过画流程图来思考问题,而是通过画集合的关系图来思考。SQL是集合语言
  •  外连接的用法:

  • ​​​​​​​EXISTS:

  • 让SQL飞起来:

参数是子查询时,使用EXISTS代替IN

tips:

  1. 如果IN的参数是“1, 2, 3”这样的数值列表,一般还不需要特别注意。但是如果参数是子查询,那么就需要注意了。
  2. 但是从代码的可读性上来看,IN要比EXISTS好。使用IN时的代码看起来更加一目了然,易于理解。因此,如果确信使用IN也能快速获取结果,就没有必要非得改成EXISTS了。
  3. IN的参数是子查询时,数据库首先会执行子查询,然后将结果存储在一张临时的工作表里(内联视图),然后扫描整个视图。很多情况下这种做法都非常耗费资源。使用EXISTS的话,数据库不会生成临时的工作表

使用EXISTS时更快的原因有以下两个:

  1. 如果连接列(id)上建立了索引,那么查询Class_B时不用查实际的表,只需查索引就可以了。
  2. 如果使用EXISTS,那么只要查到一行数据满足条件就会终止查询,不用像使用IN时一样扫描全表。在这一点上NOT EXISTS也一样。

tips:

  1. 极值函数在统计时会把为NULL的数据排除掉。

(不定期更新。。。)

你可能感兴趣的:(学习笔记,学习,笔记)