尚硅谷MySQL基础学习笔记

数据库MySQL学习笔记

写在前面

学习链接:数据库 MySQL 视频教程全集

MySQL引入

数据库的好处

  1. 持久化数据到本地
  2. 可以实现结构化查询,方便管理

数据库的相关概念

  • DB:数据库(database):存储数据的“仓库”,它保存了一系列有组织的数据。
  • DBMS:数据库管理系统(Database Management System)。数据库是通过DBMS创建和操作的容器。
  • SQL:结构化查询语言(Structure Query Language),专门用来与数据库通信的语言。
    • SQL优点:
    • 不是某个特定数据库供应商专有的语是言,几乎所有DBMS都支持SQL
    • 简单易学
    • 实际上强有力的语言,灵活使用可以进行非常复杂和高级的数据库操作

数据库存储数据的特点

  • 将数据放到表中,表再放到库中
  • 一个数据库中可以有多个表,每个表都有一个的名字,用来标识自己。表名具有唯一性。
  • 表具有一些特性,这些特性定义了数据在表中如何存储,类似java中 “类”的设计。
  • 表由列组成,我们也称为字段。所有表都是由一个或多个列
    组成的,每一列类似java 中的“属性” 。
  • 表中的数据是按行存储的,每一行类似于java中的“对象”。
  • DBMS分为两类:
    • 基于共享文件系统的DBMS(ACCESS)
    • 基于客户机——服务器的DBMS(MySQL、Oracle、SqlServer)

MySQL服务的启动和停止

  • 停止服务:net stop mysql
  • 开启服务:net start mysql

MySQL服务端的登录和退出

  • 登录:mysql 【-h localhost -P 3306】(本机可省略) -u root -p(可以直接写密码,不能有空格)
    • -h:主机名
    • -P:端口号
    • -u:用户名
    • -p:密码
  • 退出:exit
  • 查看mysql数据库的版本:
    • select version();(mysql命令)
    • mysql –version(dos命令)

MySQL的常用命令

  • 查看当前所有的数据库:show databases;

  • 打开指定的库:use 库名

  • 查看当前的所有表:show tables;

  • 查看其他库的所有表:show tables from 库名;

  • 创建表:

    create table 表名(

    ​ 列名 列类型,

    ​ 列名 列类型,

    ​ …

    );

  • 查看表结构:desc 表名;

MySQL语法规范

  1. 不区分大小写,建议关键字大写,表名、列名小写
  2. 每句话用;或\g结尾
  3. 每条命令根据需要,各子句一般分行写,关键字不能缩写也不能分行
  4. 注释
    • 单行注释:#注释文字
    • 单行注释:-- 注释文字(要有空格)
    • 多行注释:/* 注释文字 */

DQL(Data Query Language)数据查询语言

1. 基础查询

  • 语法:

    select 查询列表

    from 表名;

  • 特点:

    • 查询列表可以是:表中的字段、常量、表达式、函数
    • 查询的结果是一个虚拟的表格
  • 注意:在进行查询操作之前要指定所有的库:use myemployees;

  • 查询表中的单个字段:select last_name from employees;

  • 查询表中的多个字段:select last_name, salary, email from employees;

  • 查询表中的所有字段:select * from employees;

  • 按F12进行格式化

  • 着重号`用来区分是否是关键字或者字段

  • 选中语句进行执行或F9

  • 查询常量值:

    select 100;

    select ‘john’;

  • 查询表达式:select 100*98;

  • 查询函数:select version();

  • 起别名:

    • 便于理解
    • 如果查询的字段有重名的情况,使用别名可以区分开来

    方式1:

    select 100%98 as 结果;

    select last_name as 姓, first_name as 名 from employees;

    方式2:

    select last_name 姓, first_name 名 from employees;

    如果别名有特殊符号要加双引号:

    select salary as "out put" from employees;

  • 去重:

    查询员工表中涉及到的所有部门编号:select distinct department_id from employees;

  • +号的作用:

    • 两个操作数为数值型,则做加法运算
    • 只要其中一方为字符型,试图将字符型数值转换成数值型,如果转换成功,则继续做加法运算;如果转换失败,则将字符型数值转换成0
    • 只要其中一方为null,则结果肯定为null
  • 使用concat连接字符串:

    查询员工的名和姓连接成一个字段,并显示为姓名:select concat(last_name,first_name) as 姓名 from employees;

  • ifnull函数检测是否为null,如果为null,则返回指定的值,否则返回原本的值:

    select ifnull(commission_pct, 0) as 奖金率, commission_pct from employees;
    
  • isnull函数判断某字段或表达式是否为null,如果是,则返回1,否则返回0

2. 条件查询

  • 语法:select 查询列表 from 表明 where 筛选条件;

  • 分类:

    • 按条件表达式筛选:

      • 条件运算符:> < = != <> >= <=
    • 按逻辑表达式筛选:

      • 主要作用:用于连接条件表达式
      • 逻辑运算符:&& || ! and or not
    • 模糊查询

      like

      between and

      in

      is null

  • 按条件表达式筛选:

    • 查询工资>12000的员工信息:select * from employees where salary>12000;
    • 查询部门编号不等于90号的员工名和部门编号:select last_name, department_id from employees where department_id != 90;
  • 按逻辑表达式筛选:

    • 查询工资在10000到20000之间的员工名、工资以及奖金:select last_name, salary, commission_pct from employees where salary >= 10000 and salary <= 20000;
    • 查询部门编号不是在90到110之间,或者工资高于15000的员工信息:select * from employees where department_id < 90 or department_id > 110 or salary > 15000;
  • 模糊查询

    • like

      • 一般和通配符搭配使用,可以判断字符型数值或数值型

      • 通配符:

        • % 任意多个字符,包含0个字符
        • _ 任意单个字符
      • 查询员工名中包含字符a的员工信息:

        SELECT * FROM employees WHERE last_name LIKE '%a%';
        
      • 查询员工名中第三个字符为e,第五个字符为a的员工名和工资:

        SELECT last_name, salary FROM employees WHERE last_name LIKE '__n_l%';
        
      • 查询员工名中第二个字符为_的员工名:

        SELECT last_name FROM employees WHERE last_name LIKE '_\_ %';
        
      • 指定转义字符:

        SELECT last_name FROM employees WHERE last_name LIKE '_$_%' ESCAPE '$';
        
    • between and

      • 使用between and可以提高语句的简洁度;

      • 包含临界值;

      • 两个临界值不能替换顺序;

      • 查询员工编号在100到120之间的员工信息:

        SELECT * FROM employees WHERE employee_id >= 100 AND employee_id <= 120;
        
        SELECT * FROM employees WHERE employee_id BETWEEN 100 AND 120;
        
    • in

      • 含义:判断某字段的值是否属于in列表中的某一项

      • 使用in提高语句简洁度

      • in列表的值类型必须一致或兼容

      • in相当于等于,所以不支持通配符(like才支持)

      • 查询员工的工种编号是 IT_PROG、AD_VP、AD_PRES中的一个员工名和工种编号:

        SELECT last_name, job_id FROM employees WHERE job_id = 'IT_PROG' OR job_id = 'AD_VP' OR job_id = 'AD_PRES';
        
        SELECT last_name, job_id FROM employees WHERE job_id IN ('IT_PROG', 'AD_VP', 'AD_PRES');
        
    • is null

      • 用于判断null值

      • =或者<>不能用于判断null值

      • 查询没有奖金的员工名和奖金率:

        SELECT
        	last_name,
        	commission_pct
        FROM
        	employees
        WHERE
        	commission_pct IS NULL;
        
      • 查询有奖金的:

        SELECT
        	last_name,
        	commission_pct
        FROM
        	employees
        WHERE
        	commission_pct IS NOT NULL;
        
    • 安全等于 <=>

      • is null:仅仅可以判断null值,可读性较高
      • <=>:既可以判断null值,又可以判断普通的数值,可读性较低
  • 测试题

    • 查询没有奖金,且工资小于18000的salary, last_name:

      SELECT 
        salary,
        last_name 
      FROM
        employees 
      WHERE commission_pct IS NULL 
        AND salary < 18000;
      
    • 查询employees表中,job_id不为‘IT’或者工资为12000的员工信息:

      SELECT 
        * 
      FROM
        employees 
      WHERE job_id <> 'IT' 
        OR salary = 12000 ;
      
    • 查看部门表的结构:

      DESC departments;
      
    • 查询部门表中涉及到了哪些位置编号:

      SELECT DISTINCT 
        location_id 
      FROM
        departments ;
      
    • 经典面试题:select * from employees;select * from employees where commission_pct like ‘%%’ and last_name like ‘%%’; 结果是否一样?并说明原因:不一样!如果判断的字段中有null值,如果查询是select * from employees where commission_pct like ‘%%’ or last_name like ‘%%’ or ...;把所有字段都or写齐了就一样了。

3. 排序查询

  • 语法:

    select 查询列表

    from 表

    【where 筛选条件】

    order by 排序列表 【asc|desc】

  • asc代表的是升序,desc代表的是降序,如果不写,默认是升序

  • order by子句中可以支持单个字段、多个字段、表达式、函数、别名

  • order by子句一般是放在查询语句的最后面,但limit子句除外

  • 查询员工的信息,要求工资从高到低排序:

    SELECT 
      * 
    FROM
      employees 
    ORDER BY salary DESC ;
    

    从低到高是ASC(默认是ASC)

  • 查询部门编号>=90的员工信息,按入职时间的先后进行排序:

    SELECT 
      * 
    FROM
      employees 
    WHERE department_id >= 90 
    ORDER BY hiredate ASC ;
    
  • 按年薪的高低显示员工的信息和年薪【按表达式(别名)排序】

    SELECT 
      *,
      salary * 12 * (1+ IFNULL(commission_pct, 0)) AS 年薪 
    FROM
      employees 
    ORDER BY 年薪 DESC ;
    
  • 按姓名的长度显示员工的姓名和工资【按函数排序】

    SELECT 
      LENGTH(last_name) AS 字节长度,
      last_name,
      salary 
    FROM
      employees 
    ORDER BY 字节长度 DESC;
    
  • 查询员工信息,要求先按工资排序,再按员工编号排序

    SELECT 
      * 
    FROM
      employees 
    ORDER BY salary ASC,
      employee_id DESC ;
    
  • 测试题

    • 查询员工的姓名和部门号和年薪,按年薪降序,按姓名升序

      SELECT 
        last_name,
        department_id,
        salary * 12 * (1+ IFNULL(commission_pct, 0)) AS 年薪 
      FROM
        employees 
      ORDER BY 年薪 DESC,
        last_name ASC ;
      
    • 选择工资不在8000到17000的员工的姓名和工资,按工资降序

      SELECT 
        last_name,
        salary 
      FROM
        employees 
      WHERE salary NOT BETWEEN 8000 
        AND 17000 
      ORDER BY salary DESC ;
      
    • 查询邮箱中包含e的员工信息,并先按邮箱的字节数降序,再按部门号升序

      SELECT 
        * 
      FROM
        employees 
      WHERE email LIKE '%e%' 
      ORDER BY LENGTH(email) DESC,
        department_id ASC ;
      

4. 常见函数

  • 功能:类似于java中的方法,将一组逻辑语句

  • 好处:

    • 隐藏了实现细节
    • 提高代码的重用性
  • 调用:select 函数名(实参列表) 【from 表】;

  • 特点:

    • 叫什么(函数名)
    • 干什么(函数功能)
  • 分类:

    • 单行函数:如concat、length、ifnull等
    • 分组函数:做统计使用,又称为统计函数、聚合函数、组函数

单行函数

  • 字符函数

    • length:获取参数值的字节个数

    • concat:拼接字符串

    • upper/lower:将字符串变成大写/小写

      • 将姓变成大写,名变成小写,然后拼接:

        SELECT 
          CONCAT(UPPER(last_name), LOWER(first_name)) AS 姓名 
        FROM
          employees ;
        
    • substr/substring:截取字符串

      • 注意:索引从1开始

      • 截取从指定索引处后面所有字符

        SELECT 
          SUBSTR(
            '李莫愁爱上了陆展元',
            6
          ) AS output ;
        
      • 截取从指定索引处指定字符长度的字符

        SELECT 
          SUBSTR(
            '李莫愁爱上了陆展元',
            1,
            3
          ) output ;
        
    • 案例:姓名中首字母大写,其他字符小写,然后用_拼接,显示出来:

      SELECT 
        CONCAT(
          UPPER(SUBSTR(last_name, 1, 1)),
          '_',
          LOWER(SUBSTR(last_name, 2))
        ) AS output 
      FROM
        employees ;
      
    • instr:返回子串第一次出现的索引,如果找不到返回0

      SELECT 
        INSTR(
          '杨不悔爱上了殷六侠',
          '殷六侠'
        ) AS output ;
      
    • trim:去掉字符串前后的空格或子串

      SELECT 
        LENGTH(TRIM('   张翠山   ')) AS output ;
      
      SELECT 
        TRIM('a' FROM 'aaa张a翠aa山aaaaa') AS output ;
      
    • lpad:用指定的字符实现左填充指定长度

    • rpad:用指定的字符实现右填充指定长度

    • replace:替换,替换所有的子串

  • 数学函数

    • round:四舍五入
    • ceil:向上取整,返回>=该参数的最小整数
    • floor:向下取整,返回<=该参数的最大整数
    • truncate:截断,小数点后截断到几位
    • mod:取余,被除数为正,则为正;被除数为负,则为负
    • rand:获取随机数,返回0-1之间的小数
  • 日期函数

    • now:返回当前系统日期+时间

    • curdate:返回当前系统日期,不包含时间

    • curtime:返回当前时间,不包含日期

    • 可以获取指定的部分,年、月、日、小时、分钟、秒

      SELECT 
        YEAR(hiredate) 年 
      FROM
        employees ;
      
    • str_to_date:将日期格式的字符转换成指定格式的日期

      SELECT 
        STR_TO_DATE('1998-3-2', '%Y-%c-%d') AS output ;
      
      • 查询入职日期为1992-4-3的员工信息

        SELECT 
          * 
        FROM
          employees 
        WHERE hiredate = STR_TO_DATE('4-3 1992', '%c-%d %Y') ;
        
    • date_format:将日期转换成字符串

      SELECT 
        DATE_FORMAT(NOW(), '%y年%m月%d日)') AS output ;
      
      • 查询有奖金的员工名和入职日期(xx月/xx日 xx年)

        SELECT 
          last_name,
          DATE_FORMAT(hiredate, '%m月/%d日 %y年') AS 入职日期 
        FROM
          employees 
        WHERE commission_pct IS NOT NULL ;
        
    • datediff:返回两个日期相差的天数

    • monthname:以英文形式返回月

  • 其他函数

    SELECT VERSION();	当前数据库服务器的版本
    SELECT DATABASE();	当前打开的数据库
    SELECT USER();		当前用户
    password('字符');		返回该字符的密码形式
    md5('字符');			也是加密的一种形式(MD5)
    
  • 流程控制函数

    • if函数:if else的效果

      SELECT 
        last_name,
        commission_pct,
        IF(
          commission_pct IS NULL,
          '没奖金,呵呵',
          '有奖金,嘻嘻'
        ) 备注 
      FROM
        employees ;
      
    • case函数的使用1:switch case的效果

      • 语法:

        case 要判断的字段或表达式
        when 常量1 then 要显示的值1或语句1;
        when 常量2 then 要显示的值2或语句2;
        ...
        else 要显示的值n或语句n;
        end
        
      • 查询员工的工资,要求:

        部门号=30,显示的工资为1.1倍

        部门号=40,显示的工资为1.2倍

        部门号=50,显示的工资为1.3倍

        其他部门,显示的工资为原工资

        SELECT 
          salary AS 原始工资,
          department_id,
          CASE
            department_id 
            WHEN 30 
            THEN salary * 1.1 
            WHEN 40 
            THEN salary * 1.2 
            WHEN 50 
            THEN salary * 1.3 
            ELSE salary 
          END AS 新工资 
        FROM
          employees ;
        
    • case函数的使用2:类似于多重if

      case
      when 条件1 then 要显示的值1或语句1
      when 条件2 then 要显示的值2或语句2
      ...
      else 要显示的值n或语句n
      end
      
      • 查询员工的工资情况

        如果工资>20000,显示A级别

        如果工资>15000,显示B级别

        如果工资>10000,显示C级别

        否则,显示D级别

        SELECT 
          salary,
          CASE
            WHEN salary > 20000 
            THEN 'A' 
            WHEN salary > 15000 
            THEN 'B' 
            WHEN salary > 10000 
            THEN 'C' 
            ELSE 'D' 
          END AS 工资级别 
        FROM
          employees ;
        
  • 测试题

    • 显示系统时间(日期+时间)

      SELECT NOW();
      
    • 查询员工号,姓名,工资,以及工资提高20%后的结果(new salary)

      SELECT 
        employee_id,
        last_name,
        salary,
        salary * 1.2 AS "new salary" 
      FROM
        employees ;
      
    • 将员工的姓名按首字母排序,并写出姓名的长度(length)

      SELECT 
        last_name,
        LENGTH(last_name) 
      FROM
        employees 
      ORDER BY SUBSTR(last_name, 1, 1) ;
      
    • 做一个查询

      SELECT 
        CONCAT(
          last_name,
          ' earns ',
          salary,
          ' monthly but wants ',
          salary * 3
        ) AS "Dream Salary" 
      FROM
        employees ;
      
    • case-when训练

      SELECT 
        last_name,
        job_id AS job,
        CASE
          job_id 
          WHEN 'AD_PRES' 
          THEN 'A' 
          WHEN 'ST_MAN' 
          THEN 'B' 
          WHEN 'IT_PROG' 
          THEN 'C' 
          WHEN 'SA_PRE' 
          THEN 'D' 
          WHEN 'ST_CLERK' 
          THEN 'E' 
        END AS Grade 
      FROM
        employees 
      WHERE job_id = 'AD_PRES' ;
      

分组函数

  • 功能:用作统计使用,又称为聚合函数或统计函数或组函数

  • 分类:sum 求和、avg 平均值、max 最大值、min 最小值、count 计数(非空)

    SELECT SUM(salary) FROM employees;
    
  • 特点

    • sum、avg一般用于处理数值型数据
    • max、min、count可以处理任何类型数据
    • 以上分组函数都忽略null值
  • 可以和distinct搭配实现去重的运算

    SELECT 
      SUM(DISTINCT salary),
      SUM(salary) 
    FROM
      employees ;
    
    SELECT 
      COUNT(DISTINCT salary),
      COUNT(salary) 
    FROM
      employees ;
    
  • count函数的单独介绍

    • 效率

      • MYISAM存储引擎下,count(*)的效率高
      • INNODB存储引擎下,count(*)和count(1)效率差不多,比count(字段)要高一些
    • 使用count(*) 统计一共有多少行

      SELECT COUNT(salary) FROM employees;
      SELECT COUNT(*) FROM employees;
      SELECT COUNT(1) FROM employees;
      
  • 和分组函数一同查询的字段有限制,要求是group by后的字段

  • 训练题

    • 查询公司员工工资的最大值,最小值,平均值,总和

      SELECT 
        MAX(salary),
        MIN(salary),
        AVG(salary),
        SUM(salary) 
      FROM
        employees ;
      
    • 查询员工表中的最大入职时间和最小入职时间的相差天数(difference)

      SELECT 
        DATEDIFF(MAX(hiredate), MIN(hiredate)) DIFFERENCE 
      FROM
        employees ;
      
    • 查询部门编号为90的员工个数

      SELECT 
        COUNT(*) 
      FROM
        employees 
      WHERE department_id = 90 ;
      

5. 分组查询

  • 语法:

    select 分组函数,列(要求出现在group by的后面)

    from 表

    【where 筛选条件】

    group by 分组的列表

    【having 分组后的筛选】

    【order by 子句】

  • 注意:查询列表比较特殊,要求是分组函数和group by后出现的字段

  • 特点:

    • 分组查询中的筛选条件分为两类:

      ​ 数据源 位置 关键字

      分组前筛选 原始表 group by子句的前面 where

      分组后筛选 分组后的结果集 group by子句的后面 having

    • 分组函数做条件肯定是放在having子句中

    • 能用分组前筛选的,就优先考虑使用分组前筛选

    • group by子句支持单个字段分组,多个字段分组(多个字段之间用逗号隔开没有顺序要求),表达式或函数(用得较少)

    • 也可以添加排序(排序放在整个分组查询最后位置)

  • 查询每个工种的最高工资

    SELECT 
      MAX(salary),
      job_id 
    FROM
      employees 
    GROUP BY job_id ;
    
  • 查询每个位置上的部门个数

    SELECT 
      COUNT(*),
      location_id 
    FROM
      departments
    GROUP BY location_id ;
    
  • 查询邮箱中包含a字符的,每个部门的平均工资

    SELECT 
      AVG(salary),
      department_id 
    FROM
      employees 
    WHERE email LIKE '%a%' 
    GROUP BY department_id ;
    
  • 查询有奖金的每个领导手下员工的最高工资

    SELECT 
      MAX(salary),
      manager_id 
    FROM
      employees 
    WHERE commission_pct IS NOT NULL 
    GROUP BY manager_id ;
    
  • 查询那个部门的员工个数>2

    • 查询每个部门的员工个数

      SELECT 
        COUNT(*) AS 员工个数,
        department_id 
      FROM
        employees 
      GROUP BY department_id ;
      
    • 根据上面的结果进行筛选,查询哪个部门的员工个数>2

      SELECT 
        COUNT(*) AS 员工个数,
        department_id 
      FROM
        employees 
      GROUP BY department_id 
      HAVING 员工个数 > 2 ;
      
  • 添加分组后的筛选用having,分组前的用where

  • 查询每个工种有奖金的员工的最高工资>12000的工种编号和最高工资

    • 查询每个工种有奖金的员工的最高工资

      SELECT 
        MAX(salary),
        job_id 
      FROM
        employees 
      WHERE commission_pct IS NOT NULL
      GROUP BY job_id ;
      
    • 根据上面的结果继续筛选,最高工资>12000

      SELECT 
        MAX(salary) AS 最高工资,
        job_id 
      FROM
        employees 
      WHERE commission_pct IS NOT NULL 
      GROUP BY job_id 
      HAVING 最高工资 > 12000 ;
      
    • 查询领导编号>102的每个领导手下的最低工资>5000的领导编号是哪个,以及其最低工资

      SELECT 
        MIN(salary) AS 最低工资,
        manager_id 
      FROM
        employees 
      WHERE manager_id > 102 
      GROUP BY manager_id 
      HAVING 最低工资 > 5000 ;
      
    • 按表达式或函数分组

      • 按员工姓名的长度分组,查询每一组的员工个数,筛选员工个数>5的有哪些

        • 查询每个长度的员工个数

          SELECT 
            COUNT(*) 员工个数,
            LENGTH(last_name) 姓名长度 
          FROM
            employees 
          GROUP BY 姓名长度 ;
          
        • 添加筛选条件

          SELECT 
            COUNT(*) 员工个数,
            LENGTH(last_name) 姓名长度 
          FROM
            employees 
          GROUP BY 姓名长度 
          HAVING 员工个数 > 5 ;
          
    • 按多个字段分组

      • 查询每个部门每个工种的员工的平均工资

        SELECT 
          AVG(salary),
          department_id,
          job_id 
        FROM
          employees 
        GROUP BY department_id,
          job_id ;
        
    • 添加排序

      • 查询每个部门每个工种的员工的平均工资,并按平均工资的高低显示

        SELECT 
          AVG(salary) AS 平均工资,
          department_id,
          job_id 
        FROM
          employees 
        GROUP BY department_id,
          job_id 
        ORDER BY 平均工资 DESC ;
        
  • 练习题

    • 查询各job_id的员工工资的最大值、最小值、平均值,总和,并按job_id升序

      SELECT 
        MAX(salary),
        MIN(salary),
        AVG(salary),
        SUM(salary),
        job_id 
      FROM
        employees 
      GROUP BY job_id 
      ORDER BY job_id ;
      
    • 查询员工最高工资和最低工资的差距(DIFFERENCE)

      SELECT 
        MAX(salary) - MIN(salary) AS DIFFERENCE 
      FROM
        employees ;
      
    • 查询各个管理者手下员工的最低工资,其中最低工资不能低于6000,没有管理者的员工不计算在内

      SELECT 
        MIN(salary) AS 最低工资 
      FROM
        employees 
      WHERE manager_id IS NOT NULL 
      GROUP BY manager_id 
      HAVING 最低工资 >= 6000 ;
      
    • 查询所有部门的编号,员工数量和工资平均值,并按平均工资降序

      SELECT 
        department_id,
        COUNT(*) AS 员工数量,
        AVG(salary) AS 工资平均值 
      FROM
        employees 
      GROUP BY department_id 
      ORDER BY 工资平均值 DESC ;
      
    • 查询具有各个job_id的员工人数

      SELECT 
        COUNT(*),
        job_id 
      FROM
        employees 
      GROUP BY job_id ;
      

6. 连接查询

  • 含义:又称多表查询,当查询的字段来自于多个表时,就会用到连接查询

  • 笛卡尔乘积现象:表1有m行,表2有n行,结果=m*n

    • 发生原因:没有有效的连接条件
    • 如何避免:添加有效的连接条件
  • 分类:

    • 按年代分类:
      • sql92标准:仅仅支持内连接
      • sql99标准【推荐】:支持内连接+外连接(左外和右外)+交叉连接
    • 按功能分类:
      • 内连接
        • 等值连接
        • 非等值连接
        • 自连接
      • 外连接
        • 左外连接
        • 右外连接
        • 全外连接(mysql不支持)
      • 交叉连接
  • sql92标准

    • 等值连接

      • 多表等值连接的结果为多表的交集部分

      • n表连接,至少需要n-1个连接条件

      • 多表的顺序没有要求

      • 一般需要为表起别名

      • 可以搭配前面介绍的所有子句使用,比如排序、分组、筛选

      • 查询女神名和对应的男神名:

        SELECT 
          NAME,
          boyname 
        FROM
          boys,
          beauty 
        WHERE beauty.boyfriend_id = boys.id ;
        
      • 查询员工名和对应的部门名

        SELECT 
          last_name,
          department_name 
        FROM
          employees,
          departments 
        WHERE employees.`department_id` = departments.`department_id` ;
        
    • 为表起别名

      • 提高语句的简洁度

      • 区分多个重名的字段

      • 注意:如果为表起了别名,则查询 的字段就不能使用原始的表明去限定

      • 查询员工名、工种号、工种名

        SELECT 
          last_name,
          e.`job_id`,
          job_title 
        FROM
          employees e,
          jobs j 
        WHERE e.`job_id` = j.`job_id` ;
        
    • 两个表的顺序是否可以调换

      • 查询员工名、工种号、工种名

        SELECT 
          last_name,
          e.`job_id`,
          job_title 
        FROM
          jobs j ,
          employees e
        WHERE e.`job_id` = j.`job_id` ;
        
    • 可以加筛选

      • 查询有奖金的员工名、部门名

        SELECT 
          last_name,
          department_name 
        FROM
          employees AS e,
          departments AS d 
        WHERE e.`department_id` = d.`department_id` 
          AND e.`commission_pct` IS NOT NULL ;
        
      • 查询城市名中第二个字符为o的部门名和城市名

        SELECT 
          department_name,
          city 
        FROM
          departments d,
          locations l 
        WHERE d.`location_id` = l.`location_id` 
          AND city LIKE '_o%' ;
        
    • 可以加分组

      • 查询每个城市的部门个数

        SELECT 
          COUNT(*) 个数,
          city 
        FROM
          departments d,
          locations l 
        WHERE d.`location_id` = l.`location_id` 
        GROUP BY city ;
        
      • 查询有将近的每个部门的部门名和部门的领导编号和该部门的最低工资

        SELECT 
          department_name,
          d.manager_id,
          MIN(salary) 
        FROM
          departments d,
          employees e 
        WHERE d.`department_id` = e.`department_id` 
          AND commission_pct IS NOT NULL 
        GROUP BY department_name,
          d.manager_id ;
        
    • 可以加排序

      • 查询每个工种的工种名和员工的个数,并且按员工个数降序

        SELECT 
          job_title,
          COUNT(*) AS 个数 
        FROM
          employees e,
          jobs j 
        WHERE e.`job_id` = j.`job_id` 
        GROUP BY job_title 
        ORDER BY 个数 DESC ;
        
    • 可是实现三表连接:

      • 查询员工名、部门名和所在的城市

        SELECT 
          last_name,
          department_name,
          city 
        FROM
          employees e,
          departments d,
          locations l 
        WHERE e.`department_id` = d.`department_id` 
          AND d.`location_id` = l.`location_id` ;
        
    • 非等值连接

      • 查询员工的工资和工资级别

        SELECT 
          salary,
          grade_level 
        FROM
          employees e,
          job_grades g 
        WHERE salary BETWEEN g.lowest_sal 
          AND g.highest_sal ;
        
    • 自连接

      • 查询 员工名和上级的名称

        SELECT 
          e.employee_id,
          e.last_name,
          m.employee_id,
          m.last_name 
        FROM
          employees e,
          employees m 
        WHERE e.`manager_id` = m.`employee_id` ;
        
    • 测试题:

      • 显示员工表的最大工资,工资平均值

        SELECT 
          MAX(salary),
          AVG(salary) 
        FROM
          employees ;
        
      • 查询员工表的employee_id,job_id,last_name,按department_id降序,salary升序

        SELECT 
          employee_id,
          job_id,
          last_name 
        FROM
          employees 
        ORDER BY department_id DESC,
          salary ASC ;
        
      • 查询员工表的job_id中包含a和e的,并且a在e的前面

        SELECT 
          job_id 
        FROM
          employees 
        WHERE job_id LIKE '%a%e%' ;
        
      • 显示当前日期,以及去前后空格,截取子字符串的函数

        select now();
        select trim();
        select substr(str, startIndex, [length])
        
  • sql99语法

    • 语法:

      select 查询列表

      from 表1 别名 【连接类型】

      join 表2 别名

      on 连接条件

      【where 筛选条件】

      【group by 分组】

      【having 筛选条件】

      【order by 排序列表】

    • 内连接(同上):连接类型是inner

    • 外连接

      • 左外:left 【outer】
      • 右外:right【outer】
      • 全外:full 【outer】
    • 交叉连接:cross

    • 内连接:

      • 语法:

        select 查询列表

        from 表1 别名

        inner join 表2 别名

        on 连接条件

      • 分类:

        等值连接

        非等值连接

        自连接

      • 特点:

        • 添加排序、分组、筛选
        • inner可以省略
        • 筛选条件放在where后面,连接条件放在on后面,提高分离性,便于阅读
        • inner join连接和sql92语法中的等值连接效果是一样的,都是查询多表的交集
      • 等值连接:

        • 查询员工名、部门名

          SELECT 
            last_name,
            department_name 
          FROM
            employees e 
            INNER JOIN departments d 
              ON e.`department_id` = d.`department_id` ;
          
        • 查询名字中包含e的给员工名和工种名

          SELECT 
            last_name,
            job_title 
          FROM
            employees e 
            INNER JOIN jobs j 
              ON e.`job_id` = j.`job_id` 
          WHERE last_name LIKE "%e%" ;
          
        • 查询部门个数>3的城市名和部门个数

          SELECT 
            city,
            COUNT(*) 部门个数 
          FROM
            departments d 
            INNER JOIN locations l 
              ON d.`location_id` = l.`location_id` 
          GROUP BY city 
          HAVING 部门个数 > 3 ;
          
        • 查询哪个部门的部门员工个数>3的部门名和员工个数,并按个数降序排序

        SELECT 
          department_name,
          COUNT(*) 员工个数 
        FROM
          departments d 
          INNER JOIN employees e 
            ON d.`department_id` = e.`department_id` 
        GROUP BY d.`department_id` 
        HAVING 员工个数 > 3 
        ORDER BY 员工个数 DESC ;
        
        • 查询员工名、部门名、工种名,并按部门名降序

          SELECT 
            last_name,
            department_name,
            job_title 
          FROM
            employees e 
            INNER JOIN departments d 
              ON e.`department_id` = d.`department_id` 
            INNER JOIN jobs j 
              ON e.`job_id` = j.`job_id` 
          ORDER BY d.`department_id` DESC ;
          
      • 非等值连接

        • 查询员工的工资级别

          SELECT 
            salary,
            grade_level 
          FROM
            employees e 
            INNER JOIN job_grades g 
              ON e.`salary` BETWEEN g.`lowest_sal` 
              AND g.`highest_sal` ;
          
        • 查询每个工资级别>20的个数,并且按工资级别降序

          SELECT 
            COUNT(*),
            grade_level 
          FROM
            employees e 
            INNER JOIN job_grades g 
              ON e.`salary` BETWEEN g.`lowest_sal` 
              AND g.`highest_sal` 
          GROUP BY grade_level 
          HAVING COUNT(*) > 20 
          ORDER BY grade_level DESC ;
          
      • 自连接

        • 查询员工的名字、上级的名字

          SELECT 
            e.last_name,
            m.last_name 
          FROM
            employees e 
            INNER JOIN employees m 
              ON e.`manager_id` = m.`employee_id` ;
          
        • 查询姓名中包含字符k的员工的名字、上级的名字

          SELECT 
            e.last_name,
            m.last_name 
          FROM
            employees e 
            INNER JOIN employees m 
              ON e.`manager_id` = m.`employee_id` 
          WHERE e.`last_name` LIKE "%k%" ;
          
    • 外连接

      • 应用场景:用于查询一个表中有,另一个表没有的记录

      • 特点:

        • 外连接的查询结果为主表中的所有记录,如果从表中有和它匹配的,则显示匹配的值,如果从表中没有和它匹配的,则显示null
        • 外连接查询结果=内连接结果+主表中有而从表中没有的记录
        • 左外连接:left join左边的是主表
        • 右外连接:right join右边的是主表
        • 左外和右外交换两个表的顺序,可以实现同样的效果
        • 圈外链接=内连接的结果+表1中有但表2中没有的+表2中有但表1中没有的
      • 查询没有男朋友的女神名

        SELECT 
          b.name,
          bo.* 
        FROM
          beauty b 
          LEFT JOIN boys bo 
            ON b.boyfriend_id = bo.id 
        WHERE bo.`id` IS NULL ;
        
      • 查询哪个部门没有员工

      • 左外:

        SELECT 
          d.*,
          e.employee_id 
        FROM
          departments d 
          LEFT OUTER JOIN employees e 
            ON d.`department_id` = e.`department_id` 
        WHERE e.`employee_id` IS NULL ;
        
      • 右外:

        SELECT 
          d.*,
          e.employee_id 
        FROM
          employees e 
          RIGHT OUTER JOIN departments d 
            ON d.`department_id` = e.`department_id` 
        WHERE e.`employee_id` IS NULL ;
        
      • 全外连接

        • mysql不支持

        • 案例:

          SELECT 
            b.*,
            bo.* 
          FROM
            beauty b FULL 
            OUTER JOIN boys bo 
              ON b.`boyfriend_id` = bo.id ;
          
      • 交叉连接(也就是笛卡尔乘积)

        • 案例:

          SELECT 
            b.*,
            bo.* 
          FROM
            beauty b 
            CROSS JOIN boys bo ;
          
  • sql92 和 sql99 pk

    • 功能:sql99支持的较多
    • 可读性:sql99实现连接条件和筛选条件的分离,可读性较高
  • 练习:

    • 查询编号>3的女神的男朋友信息,如果有则列出详细信息,如果没有,则用null填充

      SELECT 
        a.id,
        a.name,
        b.* 
      FROM
        beauty a 
        LEFT JOIN boys b 
          ON a.`boyfriend_id` = b.`id` 
      WHERE a.`id` > 3 ;
      
    • 查询哪个城市没有部门

      SELECT 
        city,
        d.* 
      FROM
        departments d 
        RIGHT JOIN locations l 
          ON d.location_id = l.location_id 
      WHERE d.department_id IS NULL ;
      
    • 查询部门名为SAL或IT的员工信息

      SELECT 
        d.`department_name`,
        e.* 
      FROM
        departments d 
        LEFT JOIN employees e 
          ON d.`department_id` = e.`department_id` 
      WHERE d.`department_name` = 'SAL' 
        OR d.`department_name` = 'IT' ;
      

7. 子查询

  • 含义:出现在其他语句中的select语句,称为子查询或内查询;外部的查询语句,称为主查询或外查询

  • 嵌套在其他语句内部的select语句成为子查询或内查询

  • 外面的语句可以是insert、update、delete、select等,一般select作为外面语句较多

  • 外面如果为select语句,则此语句称为外查询或主查询

  • 分类:

    • 按子查询出现的位置:
      • select后面:仅仅支持标量子查询
      • from后面:支持表子查询
      • where或having后面:支持标量子查询,列子查询,行子查询(较少)
      • exists后面(相关子查询):支持表子查询
    • 按功能、结果集的行列数不同:
      • 标量子查询(结果集只有一行一列)
      • 列子查询(结果集只有一列多行)
      • 行子查询(结果集有一行多列)
      • 表子查询(结果集一般为多行多列)

where或having后面

  • 标量子查询(单行子查询)

  • 列子查询(多行子查询)

  • 行子查询(多列多行)

  • 特点:

    • 子查询放在小括号内
    • 子查询一般放在条件的右侧,where,having
    • 标量子查询,一般搭配着单行操作符使用(> < >= <= = <>)
    • 列子查询,一般搭配着多行操作符使用(IN、ANY/SOME、ALL)
    • 子查询的执行优选与主查询执行,主查询的条件用到了子查询的结果
标量子查询
  • 案例1:谁的工资比Abel高?

    SELECT 
      salary 
    FROM
      employees 
    WHERE last_name = 'Abel' ;
    
  • 案例2:返回job_id与141号员工相同,salary比143员工多的员工,姓名,job_id,工资

    SELECT 
      last_name,
      job_id,
      salary 
    FROM
      employees 
    WHERE job_id = 
      (SELECT 
        job_id 
      FROM
        employees 
      WHERE employee_id = 141) 
      AND salary > 
      (SELECT 
        salary 
      FROM
        employees 
      WHERE employee_id = 143) ;
    
  • 案例3:返回公司工资最少的员工的last_name, job_id和salary

    SELECT 
      last_name,
      job_id,
      salary 
    FROM
      employees 
    WHERE salary = 
      (SELECT 
        MIN(salary) 
      FROM
        employees) ;
    
  • 案例4:查询最低工资大于50号部门的最低工资的部门id和其最低工资

    SELECT 
      MIN(salary),
      e.`department_id` 
    FROM
      employees e 
    GROUP BY e.`department_id` 
    HAVING MIN(salary) > 
      (SELECT 
        MIN(salary) 
      FROM
        employees 
      WHERE department_id = 50) ;
    
列子查询
  • (多行子查询)

  • 多行比较操作符:

    IN/NOT IN:等于列表中的任意一个

    ANY|SOME:和子查询返回的某一个值比较,用的较少

    ALL:和子查询返回的所有值比较

    • 案例1:返回location_id是1400或1700的部门中的所有员工姓名

      SELECT 
        last_name 
      FROM
        employees 
      WHERE department_id IN 
        (SELECT DISTINCT 
          department_id 
        FROM
          departments 
        WHERE location_id IN (1400, 1700)) ;
      
    • 案例2:返回其他工种中比job_id为‘IT_PROG’工种任一工资低的员工的员工号、姓名、job_id以及salary

      SELECT 
        employee_id,
        last_name,
        job_id,
        salary 
      FROM
        employees 
      WHERE salary < ANY 
        (SELECT DISTINCT 
          salary 
        FROM
          employees 
        WHERE job_id = 'IT_PROG') 
        AND job_id <> 'IT_PROG' ;
      

      或者用max代替any

      SELECT 
        employee_id,
        last_name,
        job_id,
        salary 
      FROM
        employees 
      WHERE salary < 
        (SELECT 
          MAX(salary) 
        FROM
          employees 
        WHERE job_id = 'IT_PROG') 
        AND job_id <> 'IT_PROG' ;
      
    • 案例3:返回其他工种中比job_id为‘IT_PROG’工种所有工资都低的员工的员工号、姓名、job_id以及salary

      SELECT 
        employee_id,
        last_name,
        job_id,
        salary 
      FROM
        employees 
      WHERE salary < ALL 
        (SELECT DISTINCT 
          salary 
        FROM
          employees 
        WHERE job_id = 'IT_PROG') 
        AND job_id <> 'IT_PROG' ;
      

      或者用min代替all

      SELECT 
        employee_id,
        last_name,
        job_id,
        salary 
      FROM
        employees 
      WHERE salary < 
        (SELECT 
          MIN(salary) 
        FROM
          employees 
        WHERE job_id = 'IT_PROG') 
        AND job_id <> 'IT_PROG' ;
      
行子查询
  • 结果集一行多列或多行多列

  • 案例1:查询员工编号最少并且工资最高的员工信息

    SELECT 
      * 
    FROM
      employees 
    WHERE (employee_id, salary) = 
      (SELECT 
        MIN(employee_id),
        MAX(salary) 
      FROM
        employees) ;
    

select后面

  • 仅仅支持标量子查询

  • 案例1:查询每个部门的员工个数

    SELECT 
      d.*,
      (SELECT 
        COUNT(*) 
      FROM
        employees e 
      WHERE e.department_id = d.department_Id) 个数 
    FROM
      departments d ;
    
  • 案例2:查询员工号=102的部门名

    SELECT 
      (SELECT 
        department_name 
      FROM
        departments d 
        INNER JOIN employees e 
          ON d.department_id = e.department_id 
      WHERE e.employee_id = 102) 部门名 ;
    

from后面

  • 将子查询结果充当一张表,要求必须起别名

  • 案例1:查询每个部门的平均工资的工资等级

    SELECT 
      ag_dep.*,
      g.`grade_level` 
    FROM
      (SELECT 
        AVG(salary) ag,
        department_id 
      FROM
        employees 
      GROUP BY department_id) ag_dep 
      INNER JOIN job_grades g 
        ON ag_dep.ag BETWEEN g.`lowest_sal` 
        AND g.`highest_sal` ;
    

exists后面

  • 相关子查询

  • 语法:exists(完整的查询语句)

  • 结果:1或0

  • 案例1:查询有员工的部门名

    SELECT 
      department_name 
    FROM
      departments d 
    WHERE EXISTS 
      (SELECT 
        * 
      FROM
        employees e 
      WHERE d.`department_id` = e.`department_id`) ;
    

    用in更简单

    SELECT 
      department_name 
    FROM
      departments d 
    WHERE d.`department_id` IN 
      (SELECT 
        department_id 
      FROM
        employees e) ;
    
  • 习题集

    1. 查询和zlotkey相同部门的员工姓名和工资

      SELECT 
        last_name,
        salary 
      FROM
        employees 
      WHERE department_id = 
        (SELECT 
          department_id 
        FROM
          employees e 
        WHERE e.`last_name` = 'Zlotkey') ;
      
    2. 查询工资比公司平均工资高的员工的员工号,姓名和工资

      SELECT 
        employee_id,
        last_name,
        salary 
      FROM
        employees e 
      WHERE e.`salary` > 
        (SELECT 
          AVG(salary) 
        FROM
          employees) ;
      
    3. 查询各部门中工资比本部门平均工资高的员工的员工号,姓名和工资

      SELECT 
        employee_id,
        last_name,
        salary 
      FROM
        employees e 
        INNER JOIN 
          (SELECT 
            AVG(salary) ag,
            department_id 
          FROM
            employees 
          GROUP BY department_id) nt 
          ON nt.department_id = e.department_id 
      WHERE salary > ag ;
      
    4. 查询和姓名中包含字母u的员工在相同部门的员工的员工号和姓名

      SELECT 
        employee_id,
        last_name 
      FROM
        employees 
      WHERE department_id IN 
        (SELECT DISTINCT 
          department_id 
        FROM
          employees 
        WHERE last_name LIKE '%u%') ;
      
    5. 查询在部门的location_id为1700的部门工作的员工的员工号

      SELECT 
        employee_id 
      FROM
        employees 
      WHERE department_id IN 
        (SELECT DISTINCT 
          department_id 
        FROM
          departments 
        WHERE location_id = 1700) ;
      
    6. 查询管理者是King的员工姓名和工资

      SELECT 
        last_name,
        salary 
      FROM
        employees 
      WHERE manager_id IN 
        (SELECT 
          employee_id 
        FROM
          employees 
        WHERE last_name = 'K_ing') ;
      
    7. 查询工资最高的员工的姓名,要求first_name和last_name显示为一列,列名为 姓.名

      SELECT 
        CONCAT(nt.first_name, nt.last_name) "姓.名" 
      FROM
        (SELECT 
          first_name,
          last_name 
        FROM
          employees 
        WHERE salary = 
          (SELECT 
            MAX(salary) 
          FROM
            employees)) nt ;
      

8. 分页查询

  • 应用场景:当要显示的数据,一页显示不全,需要分页提交sql请求

  • 语法:

    select 查询列表

    from 表

    【join type】 join 表2

    on 连接条件

    where 筛选条件

    group by 分组字段

    having 分组后的筛选

    order by 排序的字段】

    limit offset,size;

    offset:要显示条目的起始索引(从0开始)

    size:要显示的条目个数

  • 特点:

    • limit语句放在查询语句的最后

    • 公式:

      要显示的页数page,每页的条目数size

      select 查询列表

      from 表

      limit (page - 1)* size, size;

  • 案例1:查询前5条员工信息

    SELECT * FROM employees LIMIT 0, 5;
    或者
    SELECT * FROM employees LIMIT 5;
    
  • 案例2:查询第11条-第25条

    SELECT * FROM employees LIMIT 10, 15;
    
  • 案例3:有奖金的员工信息,并且工资较高的前10名显示出来

    SELECT 
      * 
    FROM
      employees 
    WHERE commission_pct IS NOT NULL 
    ORDER BY salary DESC 
    LIMIT 10 ;
    
  • 经典案例1:

    1. 查询工资最低的员工信息:last_name, salary

      SELECT 
        last_name,
        salary 
      FROM
        employees 
      WHERE salary = 
        (SELECT 
          MIN(salary) 
        FROM
          employees) ;
      
    2. 查询平均工资最低的部门信息

      SELECT 
        * 
      FROM
        departments 
      WHERE department_id = 
        (SELECT 
          department_id 
        FROM
          employees 
        GROUP BY department_id 
        ORDER BY AVG(salary) ASC 
        LIMIT 1) ;
      
    3. 查询平均工资最低的部门信息和该部门的平均工资

      SELECT 
        d.*,
        dd.ag 
      FROM
        departments d 
        INNER JOIN 
          (SELECT 
            AVG(salary) ag,
            department_id 
          FROM
            employees 
          GROUP BY department_id 
          ORDER BY ag 
          LIMIT 1) dd 
          ON d.`department_id` = dd.department_id ;
      
    4. 查询平均工资最高的job信息

      SELECT 
        * 
      FROM
        jobs j 
      WHERE j.`job_id` = 
        (SELECT 
          job_id 
        FROM
          employees 
        GROUP BY job_id 
        ORDER BY AVG(salary) DESC 
        LIMIT 1) ;
      
    5. 查询平均工资高于公司平均工资的部门有哪些

      SELECT 
        AVG(salary) ag,
        department_id 
      FROM
        employees 
      GROUP BY department_id 
      HAVING ag > 
        (SELECT 
          AVG(salary) 
        FROM
          employees) ;
      
    6. 查询出公司中所有manager的详细信息

      SELECT 
        * 
      FROM
        employees 
      WHERE employee_id IN 
        (SELECT DISTINCT 
          manager_id 
        FROM
          employees 
        WHERE manager_id IS NOT NULL) ;
      
    7. 各个部门中,最高工资中,最低的那个部门的最低工资是多少

      SELECT 
        MIN(salary) 
      FROM
        employees 
      WHERE department_id = 
        (SELECT 
          department_id 
        FROM
          employees 
        GROUP BY department_id 
        ORDER BY MAX(salary) ASC 
        LIMIT 1) ;
      
    8. 查询平均工资最高的部门的manager的详细信息

      SELECT 
        last_name,
        department_id,
        email,
        salary 
      FROM
        employees 
      WHERE employee_id = 
        (SELECT DISTINCT 
          manager_id 
        FROM
          employees 
        WHERE department_id = 
          (SELECT 
            department_id 
          FROM
            employees 
          GROUP BY department_id 
          ORDER BY AVG(salary) DESC 
          LIMIT 1) 
          AND manager_id IS NOT NULL) ;
      

9. 联合查询

  • union:联合,合并,将多条查询语句的结果合并成一个结果

  • 引入案例:查询部门编号>90或邮箱包含a的员工信息

    SELECT 
      * 
    FROM
      employees 
    WHERE email LIKE "%a%" 
      OR department_id > 90 ;
    

    用联合查询为:

    SELECT 
      * 
    FROM
      employees 
    WHERE email LIKE "%a%" 
    UNION
    SELECT 
      * 
    FROM
      employees 
    WHERE department_id > 90;
    
  • 语法:

    查询语句1

    union 【ALL】

    查询语句2

    union 【ALL】

  • 应用场景:要查询的结果来自于多个表,且多个表没有直接的连接关系,但查询的信息一致

  • 特点:

    • 要求多条查询语句的查询列数是一致的
    • 要求多条查询语句的查询的每一列的类型和顺序最好是一致的
    • union关键字默认去重,如果使用union all可以包含重复项

10. 查询总结

  • 语法:

    select 查询列表 7

    from 表1 别名 1

    连接类型 join 表2 2

    on 连接条件 3

    where 筛选 4

    group by 分组列表 5

    having 筛选 6

    order by 排序列表 8

    limit 排序列表 9

DML(Data Manipulation Language)数据操作语言

  • 涉及到数据的
    • 插入:insert
    • 修改:update
    • 删除:delete

1. 插入语句

  • 方式1:

    • 语法:insert into 表名(列名,…) values(值1,…)

    • 示例1:插入的值的类型要与列的类型一致或兼容

      INSERT INTO beauty (
        id,
        NAME,
        sex,
        borndate,
        phone,
        photo,
        boyfriend_id
      ) 
      VALUES
        (
          13,
          '唐艺昕',
          '女',
          '1990-4-23',
          '18934531234',
          NULL,
          2
        );
      
    • 示例2:不可以为null的列必须插入值。可以为null的列如何插入值?

      方式1:字段的值写null
      方式2:不写该字段
      
    • 示例3:列的顺序是否可以调换

      INSERT INTO beauty(NAME, sex, id, phone)
      VALUES('蒋欣', '女', 16, '110');
      
    • 示例4:列数和值的个数必须一致

    • 示例5:可以省略列名,默认所有列,而且列的顺序和表中列的顺序一致

      INSERT INTO beauty
      VALUES(18, '李易峰', '男', NULL, '19', NULL, NULL);
      
  • 方式2:

    • 语法:insert into 表名 set 列名=值,列名=值,…

      INSERT INTO beauty SET id = 19,
      NAME = '刘涛',
      phone = '999' ;
      
  • 两种方式大pk

    • 方式1支持插入多行,但是方式2不支持

      INSERT INTO beauty
      VALUES
      (20, '李易峰', '男', NULL, '19', NULL, NULL),
      (21, '李易峰', '男', NULL, '19', NULL, NULL),
      (22, '李易峰', '男', NULL, '19', NULL, NULL);
      
    • 方式1支持子查询,方式2不支持

      INSERT INTO beauty(id, NAME, phone)
      SELECT 26, '送钱', '12341234';
      

2. 修改语句

  • 语法:

    • 修改单表的记录

      update 表名

      set 列=新值,列=新值…

      where 筛选条件;

    • 修改多表的记录

    • sql92语法

      update 表1 别名,表2 别名

      set 列=值…

      where 筛选条件

      and 筛选条件;

    • sql99语法:

      update 表1 别名

      inner | left | right join 表2 别名

      on 连接条件

      set 列=值,…

      where 筛选条件;

修改单表的记录

  • 案例1:修改beauty表中姓唐的女神电话为109090909

    UPDATE 
      beauty 
    SET
      phone = '109090909' 
    WHERE NAME LIKE '唐%' ;
    
  • 案例2:修改boys表中id号位2的名称为张飞,魅力值为10

    UPDATE 
      boys 
    SET
      boyname = '张飞',
      usercp = 10 
    WHERE id = 2 ;
    

修改多表的记录

  • 案例1:修改张无忌的女朋友的手机号为114

    UPDATE
      boys b 
      INNER JOIN beauty be 
        ON b.`id` = be.`boyfriend_id` SET be.`phone` = '114' 
    WHERE b.`boyName` = '张无忌' ;
    
  • 案例2:修改没有男朋友的女神的男朋友编号都为 2号

    UPDATE 
      boys b 
      RIGHT JOIN beauty be 
        ON b.`id` = be.`boyfriend_id` SET be.`boyfriend_id` = 2 
    WHERE be.`boyfriend_id` IS NULL ;
    

3. 删除语句

  • 方式1:delete

    • 语法

      • 单表的删除

        delete from 表名 where 筛选条件

      • 多表的删除

        • sql92语法

          delete 别名(要删哪个表就写哪个表的别名,都删就都写)

          from 表1 别名,表2 别名

          where 连接条件

          and 筛选条件

          limit 条目数;

        • sql99语法

          delete 别名(要删哪个表就写哪个表的别名,都删就都写)

          from 表1 别名

          inner | left | right join 表2 别名 on 连接条件

          where 筛选条件

          limit 条目数;

    • 案例1:删除手机号以9结尾的女神信息

      DELETE 
      FROM
        beauty 
      WHERE phone LIKE '%9' ;
      
    • 案例2:删除张无忌的女朋友的信息

      DELETE 
        be 
      FROM
        beauty be 
        INNER JOIN boys b 
          ON b.`id` = be.`boyfriend_id` 
      WHERE b.`boyName` = '张无忌' ;
      
    • 案例3:删除黄晓明的信息以及他女朋友的信息

      DELETE 
        b,
        be 
      FROM
        beauty be 
        INNER JOIN boys b 
          ON b.`id` = be.`boyfriend_id` 
      WHERE b.`boyName` = '黄晓明' ;
      
  • 方式2:truncate

    • 语法

      truncate table 表名;

    • truncate语句中不许加where

    • 一删全删

      TRUNCATE TABLE boyes ;
      
  • delete pk truncate

    • delete可以加where条件,truncate不可以
    • truncate删除效率高一些
    • 假如要删除的表中有自增长列,如果用delete删除后,再插入数据,自增长列的值从断点开始,而truncate删除后,再插入数据,自增长列的值从1开始。
    • truncate删除没有返回值,delete删除有返回值
    • truncate删除不能回滚,delete删除可以回滚

DDL(Data Definition Language)数据定义语言

  • 库和表的管理
    • 库的管理:创建、修改、删除
    • 标的管理:创建、修改、删除
    • 创建:create
    • 修改:alter
    • 删除:frop

1. 库的管理

  • 库的创建

    • 语法:create database [if not exists] 库名 [character set 字符集名];

    • 案例:创建库book

      CREATE DATABASE IF NOT EXISTS books;
      
  • 库的修改

    • 修改库名的语句【已停用】

      RENAME DATABASE books TO new_books;
      
    • 更改库的字符集

      ALTER DATABASE books CHARACTER SET gbk;
      
  • 库的删除

    DROP DATABASE IF EXISTS books;
    

2. 表的管理

  • 表的创建

    • 语法:

    create table 【if not exists】 表名(

    ​ 列名 列的类型【(长度) 约束】,

    ​ 列名 列的类型【(长度) 约束】,

    ​ …

    )

    • 案例1:创建表 book

      CREATE TABLE book (
        id INT,
        bname VARCHAR (20),
        price DOUBLE,
        authorid INT,
        publishdate DATETIME
      ) ;
      
    • 案例2:创建表author

      CREATE TABLE author (
        id INT,
        au_name VARCHAR (20),
        nation VARCHAR (10)
      ) ;
      
    • 案例3:查看创建的表

      DESC author;
      
  • 表的修改

    • 语法:alter table 表名 add | drop | modify | change column 列名 【列类型 约束】;

    • 添加列:alter table 表名 add column 列名 类型 【first | after 字段名】;

    • 修改列的类型或约束:alter table 表名 modify column 列名 新类型 【新约束】;

    • 修改列名:alter table 表名 change column 旧列名 新列名 类型;

    • 删除列:alter table 表名 drop column 列名;

    • 修改表名:alter table 表名 rename 【to】 新表名;

    • 修改列名

      ALTER TABLE book 
        CHANGE COLUMN publishdate pubdate DATETIME ;
      
    • 修改列的类型或约束

      ALTER TABLE book 
        MODIFY COLUMN pubdate TIMESTAMP ;
      
    • 添加新列

      ALTER TABLE author 
        ADD COLUMN annual DOUBLE ;
      
    • 删除列

      ALTER TABLE author 
        DROP COLUMN annual ;
      
    • 修改表名

      ALTER TABLE author 
        RENAME TO book_author ;
      
  • 表的删除

    • 语法:drop table if exists 表名;

    • 查看有哪些表:show tables;

    • if exists 只能在库,表的创建和删除的时候使用,列的操作不能使用。

    • 通用的写法:

      DROP DATABASE IF EXISTS 旧库名;
      CREATE DATABASE 新库名;
      
      DROP TABLE IF EXISTS 旧表名;
      CREATE TABLE 表名();
      
  • 表的复制

    • 仅仅复制表的结构

      CREATE TABLE copy LIKE book_author ;
      
    • 复制表的结构+数据

      CREATE TABLE copy2 
      SELECT 
        * 
      FROM
        book_author ;
      
    • 只复制部分数据

      CREATE TABLE copy3 
      SELECT 
        id,
        au_name 
      FROM
        book_author 
      WHERE nation = '中国' ;
      
    • 仅仅复制某些字段(部分结构):设置where不满足,那么就没有数据

      CREATE TABLE copy4 
      SELECT 
        id,
        au_name 
      FROM
        book_author 
      WHERE 0 ;
      
  • 习题集

    • 创建表dept1

      USE myemployees;
      CREATE TABLE dept1 (id INT (7), NAME VARCHAR (25)) ;
      
    • 将表departments中的数据插入新表dept2中

      CREATE TABLE dept2 
      SELECT 
        department_id,
        department_name 
      FROM
        departments ;
      
    • 创建表emp5

      CREATE TABLE emp5 (
        id INT (7),
        first_name VARCHAR (25),
        last_name VARCHAR (25),
        dept_id INT (7)
      ) ;
      
    • last_name的长度修改为50

      ALTER TABLE emp5 MODIFY COLUMN last_name VARCHAR(50);
      
    • 根据表employees创建employee2

      CREATE TABLE employee2 LIKE employees ;
      
    • 删除表emp5

      DROP TABLE IF EXISTS emp5;
      
    • 将表empoyees2重命名为emp5

      ALTER TABLE employee2 
        RENAME TO emp5 ;
      
    • 在表dept和emp5中添加新列test_column,并检查所做的操作

      ALTER TABLE emp5 
        ADD COLUMN test_column INT ;
      DESC emp5;
      
    • 直接删除表emp5中的列dept_id

      ALTER TABLE emp5 
        DROP COLUMN dept_id ;
      

3. 常见的数据类型

  • 数值型:
    • 整型
    • 浮点型
      • 定点数
      • 浮点数
  • 字符型:
    • 较短的文本:char、varchar
    • 较长的文本:text、blob(较长的二进制数据)
  • 日期型:

整型

  • 分类:Tinyint、Smallint、Mediumint、int/integer、bigint
  • 特点
    • 如果不设置无符号还是有符号,默认是有符号,如果想设置无符号,需要添加unsigned关键字
    • 如果插入的数值超出了整型的范围,会报out of range异常,并且插入临界值
    • 如果不设置长度,会有默认的长度,长度代表了显示的最大宽度,如果不够会用0在左边填充,但必须搭配zerofill使用,并且默认变为无符号的整型!

img

小数

  • 分类
    • 浮点型
      • float(M,D)
      • double(M,D)
    • 定点型
      • dec(M,D)
      • decimal(M,D)
  • 特点
    • M:整数部位+小数部位
    • D:小数部位
    • 如果超过范围,则插入临界值
    • M和D都可以省略,如果是decimal,则M默认为10,D默认为0
    • 如果是float和double,则会根据插入的数值的精度来决定精度
    • 定点型的精确度较高,如果要求插入数值的精度较高如货币运算等则考虑使用定点型
  • 原则:所选择的类型越简单越好,能保存数值的类型越小越好

img

字符型

  • 较短的文本:char、varchar
  • 较长的文本:text,blob
  • 特点:
    • 写法 M的意思 特点 空间耗费 效率
    • char char(M) 最大的字符数,可以省略,默认为1 固定长度的字符 比较耗费 高
    • varchar varchar(M) 最大的字符数,不可以省略 可变长度的字符 比较节省 低

img

  • 其他类型
    • binary和varbinary用于保存较短的二进制
    • enum:枚举,eg. enum('男', '女')
    • set:集合,eg. set('a', 'b', 'c', 'd')

日期型

  • 分类:
    • date:只保存日期
    • time:值保存时间
    • year:值保存年
    • datetime:保存日期+时间
    • timestamp:保存日期+时间,更能反映真实时间
  • 特点:
    • ​ 字节 范围 时区等的影响
    • datetime 8 1000-9999 不受
    • timestamp 4 1970-2038 受

img

4. 常见约束

  • 含义:一种限制,用于限制表中的数据,为了保证表中的数据的准确和可靠性

  • 分类:六大约束

    • not null:非空,用于保证该字段的值不能为空。比如姓名、学号等。
    • default:默认,用于保证该字段有默认值。比如性别。
    • primary key:主键,用于保证该字段的值具有唯一性,并且非空。比如学号、员工编号等。
    • unique:唯一,用于保证该字段的值具有唯一性,可以为空。比如座位号。
    • check:检查约束【mysql中不支持】。不日年龄、性别。
    • foreign key:外键,用于限制两个表的关系,用于保证该字段的值必须来自于主表的关联列的值。在从表添加外键约束,用于应用主表中某列的值。比如学生表的专业编号,员工表的部门编号,员工表的工种编号。
  • 添加约束的时机:

    • 创建表时
    • 修改表时
  • 约束添加的分类:

    • 列级约束:六大约束语法上都支持,但外键约束没有效果

    • 表级约束:除了非空、默认,其他的都支持

      位置 支持的约束类型 是否可以起约束名
      列级约束 列的后面 语法都支持,但外键没有效果 不可以
      表级约束 所有列的下面 默认和非空不支持,其他支持 可以(主键没有效果)

创建表时添加约束

  • 添加列级约束

    CREATE DATABASE students;
    USE students;
    
    CREATE TABLE stuinfo (
      id INT PRIMARY KEY,
      stuname VARCHAR (20) NOT NULL,	# 非空
      gender CHAR(1) CHECK (gender = '男' 
        OR gender = '女'),
      seat INT UNIQUE,	# 唯一
      age INT DEFAULT 18,	# 默认
      majorID INT REFERENCES major (id)
    ) ;
    
    CREATE TABLE major (
      id INT PRIMARY KEY,
      majorName VARCHAR (20)
    ) ;
    
    DESC stuinfo;
    SHOW INDEX FROM stuinfo;
    
    • 语法:直接在字段名和类型后面追加约束类型即可。
    • 只支持:默认、非空、主键、唯一(除了外键都支持)
  • 添加表级约束

    DROP TABLE IF EXISTS stuinfo ;
    
    CREATE TABLE stuinfo(
    id INT,
    stuname VARCHAR(20),
    gender CHAR(1),
    seat INT,
    age INT,
    majorid INT,
    
    CONSTRAINT pk PRIMARY KEY(id),
    CONSTRAINT uq UNIQUE(seat),
    CONSTRAINT ck CHECK(gender='男' OR gender='女'),
    CONSTRAINT fk_stuinfo_major FOREIGN KEY(majorid) REFERENCES major(id)
    );
    
    SHOW INDEX FROM stuinfo;
    
    • 语法:在各个字段的最下面

      【constraint 约束名】 约束类型(字段名)

    • 除了非空、默认,其他的都支持

  • 通用的写法:

    CREATE TABLE IF NOT EXISTS stuinfo (
      id INT PRIMARY KEY,
      stuname VARCHAR (20) NOT NULL,
      gender CHAR(1),
      seat INT UNIQUE,
      age INT DEFAULT 18,
      majorID INT,
      CONSTRAINT fk_stuinfo_major FOREIGN KEY (majorid) REFERENCES major (id)
    ) ;
    
  • 表级约束pk列级约束

    支持类型 可以起约束名与否
    列级约束 除了外键 不可以
    表级约束 除了非空和默认 可以,但对主键无效
  • 列级约束可以在一个字段上追加多个,中间用空格隔开,没有顺序要求

  • 主键和唯一的大对比

    •        保证唯一性    是否允许为空    一个表中可以有多少个    是否允许组合
      
    • 主键 √ × 至多有1个 √,但不推荐
    • 唯一 √ √ 可以有多个 √,但不推荐
  • 外键特点

    • 要求在从表设置外键关系

    • 从表的外键列的类型和主表的关联列的类型要求一致或兼容,名称无要求

    • 主表的关联列必须是一个key(一般是主键或唯一)

    • 插入数据时,先插入主表,再插入从表

    • 删除数据时,先删除从表,再删除主表

      • 方式1:级联删除

        ALTER TABLE stuinfo ad CONSTRAINT fk_stu_major FOREIGN KEY(majorid) REFERENCES major(id) ON DELETE CASCADE;
        

        删除的时候,主表和从表对应的行都删了。

      • 方式2:级联置空

        ALTER TABLE stuinfo ad CONSTRAINT fk_stu_major FOREIGN KEY(majorid) REFERENCES major(id) ON DELETE SET NULL;
        

        删除的时候,主表对应的行被删除了,从表引入的地方变为空值null。

修改表时添加约束

  • 添加列级约束:alter table 表名 modify column 字段名 字段类型 新约束;

  • 添加表级约束:alter table 表名 add 【constraint 约束名】 约束类型(字段名)【外键的引用】;

  • 添加非空约束

    ALTER TABLE stuinfo MODIFY COLUMN stuname VARCHAR(20) NOT NULL;
    
  • 添加默认约束

    ALTER TABLE stuinfo MODIFY COLUMN age INT DEFAULT 18;
    
  • 添加主键

    # 列级约束的写法
    ALTER TABLE stuinfo MODIFY COLUMN id INT PRIMARY KEY;
    # 表级约束的写法
    ALTER TABLE stuinfo ADD PRIMARY KEY(id);
    
  • 添加唯一

    # 列级约束的写法
    ALTER TABLE stuinfo MODIFY COLUMN seat INT UNIQUE;
    # 表级约束的写法
    ALTER TABLE stuinfo ADD UNIQUE(seat);
    
  • 添加外键

    ALTER TABLE (CONSTRAINT fk_stuinfo_major) stuinfo ADD FOREIGN KEY(majorid) REFERENCES major(id);
    

修改表时删除约束

  • 删除非空约束

    ALTER TABLE stuinfo MODIFY COLUMN stuname VARCHAR(20) NULL;
    
  • 删除默认约束

    ALTER TABLE stuinfo MODIFY COLUMN age INT;
    
  • 删除主键

    ALTER TABLE stuinfo DROP PRIMARY KEY;
    
  • 删除唯一

    ALTER TABLE stuinfo DROP INDEX seat;
    
  • 删除外键

    ALTER TABLE stuinfo DROP FOREIGN KEY fk_stuinfo_major;
    
  • 总结

    1. 非空

      添加非空:alter table 表名 modify column 字段名 字段类型 not null;

      删除非空:alter table 表名 modify column 字段名 字段类型;

    2. 默认

      添加默认:alter table 表名 modify column 字段名 字段类型 default;

      删除默认:alter table 表名 modify column 字段名 字段类型;

    3. 主键

      添加主键:alter table 表名 add 【constraint 约束名】 primary key(字段名);

      删除主键:alter table 表名 drop primary key;

    4. 唯一

      添加唯一:alter table 表名 add 【constraint 约束名】unique(字段名);

      删除唯一:alter table 表名 drop index 索引名;

    5. 外键

      添加外键:alter table 表名 add 【constraint 约束名】foreign key(字段名) references 主表(被引用列);

      删除唯一:alter table 表名 drop foreign key 约束名;

5. 标识列

  • 又称为自增长列

  • 含义:可以不用手动的插入值,系统提供默认的序列值

  • 特点:

    • 标识列必须和主键搭配吗?不一定,但要求是一个key。
    • 一个表可以有几个标识列?至多一个。
    • 标识列的类型?只能是数值型(int(一般是int),float,double)
    • 标识列可以通过SET auto_increment_increment = 1;设置步长;可以通过手动插入值设置起始值。
  • 创建表时设置表时列

    create table 表(

    ​ 字段名 字段类型 约束 auto_increment

    CREATE TABLE tab_identity (
      id INT PRIMARY KEY AUTO_INCREMENT,
      NAME varcahr (20)
    ) ;
    
  • 设置表时列的步长

    SHOW VARIABLES LIKE '%auto_increment%';
    SET auto_increment_increment = 3;
    
  • 设置表时列的起始值:想在什么地方更改自增长列的值,则手动插入值(不手动的时候,值是null)。

  • 修改表时设置标识列

    alter table 表 modify column 字段名 字段类型 约束 auto_increment

    ALTER TABLE tab_identity MODIFY COLUMN id INT PRIMARY KEY AUTO_INCREMENT;
    
  • 修改表时删除标识列

    alter table 表 modify column 字段名 字段类型 约束

    ALTER TABLE tab_identity MODIFY COLUMN id INT;
    

TCL(Transaction Control Language)事务控制语言

  • 事务的含义:一个或一组sql语句组成一个执行单元,这个执行单元要么全部执行,要么全部不执行。

  • 存储引擎:在mysql中的数据用各种不同的技术存储在文件(或内存)中。

    • 通过show engines来查看mysql支持的存储引擎。
    • 在mysql中用的最多的存储引擎有:innodb、myisam、memory等。其中innodb支持事务,而myisam、memory等不支持事务。
  • 事务的ACID属性

    1. 原子性(Atomicity):原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
    2. 一致性(Consistency):事务必须使数据库从一个一致性状态变换到另外一个一致性状态。
    3. 隔离性(Isolation):事务的隔离性是指一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
    4. 持久性(Durability):持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来的其他操作和数据库故障不应该对其有任何影响。
  • 事务的创建

    • 隐式事务:事务没有明显的开启和结束的标记。比如insert、update、delete语句

    • 显式事务:事务具有明显的开启和结束的标记

      • 前提:必须先设置自动提交功能为禁用

        SET autocommit=0;
        
      • 步骤1:开启事务

        SET autocommit=0;
        START TRANSACTION;(可选)
        
      • 步骤2:编写事务中的sql语句(select insert update delete,只有增删改查,不包括DDL语言)

        语句1;

        语句2;

      • 步骤3:结束事务

        commit;提交事务

        rollback;回滚事务

    • savepoint 结点名:设置保存点

      SET autocommit = 0 ;
      
      START TRANSACTION;
      DELETE FROM account WHERE id=25;
      SAVEPOINT a;
      DELETE FROM accound WHERE id=28;
      ROLLBACK TO a;
      
    • delete和truncate在事务使用时的区别

      • delete删除后支持回滚
      SET autocommit = 0 ;
      START TRANSACTION;
      DELETE FROM account;
      ROLLBACK;
      
      • truncate删除后不支持回滚
      SET autocommit = 0 ;
      START TRANSACTION;
      TRUNCATE TABLE account;
      ROLLBACK;
      
  • 数据库的隔离级别

    • 对于同时运行的多个事务,当这些事务访问数据库中相同的数据时,如果没有采取必要的隔离机制,就会导致各种并发问题:
      • 脏读:对于两个事务T1,T2。T1读取了已经被T2更新但还没有被提交的字段之后,若T2回滚,T1读取的内容就是临时且无效的。主要是其他事务更新的数据
      • 不可重复读:对于两个事务T1,T2。T1读取了一个字段,然后T2更新了该字段之后,T1再次读取同一个字段,值就不同了。
      • 幻读:对于两个事务T1,T2。T1从一个表中读取了一个字段,然后T2在该表中插入了一些新的行之后,如果T1再次读取同一个表,就会多出几行。主要是其他事务插入的数据
    • 数据库事务的隔离性:数据库系统必须具有隔离并发运行各个事务的能力,使他们不会相互影响,避免各种并发问题。
    • 一个事务与其他事务隔离的程度称为隔离级别。数据库规定了多种事务隔离级别,不同隔离级别对应不同的干扰程度,隔离级别越高,数据一致性就越好,但并发性弱。
    • 数据库提供的4种事务隔离级别:img
    • Oracle支持2种事务隔离级别:READ COMMITED,SERIALIZABLE。Oracle默认的事务隔离级别是:READ COMMITED。
    • Mysql支持4种事务隔离级别。Mysql默认的事务隔离级别为:REPEATABLE READ。
    • 每启动一个mysql程序,就会获得一个单独的数据库连接,每个数据库连接都有一个全局变量@@tx_isolation,表示当前事务隔离级别。
    • 查看当前的隔离级别:select @@tx_isolation;
    • 设置当前mysql连接的隔离级别:set transaction isolation level read committed;
    • 设置数据库系统的全局的隔离级别:set global transaction isolation level read committed;

视图

  • 含义:虚拟表,和普通表一样使用。并且使用视图时动态生成的,值保存了sql逻辑,不保存成查询结果。

  • mysql5.1版本出现的新特性,是通过表动态生成的数据

  • 应用场景:

    • 多个地方用到同样的查询结果
    • 该查询结果使用的sql语句较复杂
  • 案例1:查询姓张的学生名和专业名

    # 普通写法
    SELECT 
      stuname,
      majorname 
    FROM
      suinfo s 
      INNER JOIN major m 
        ON s.majorid = m.`id` 
    WHERE s.stuname LIKE '张%' ;
    
    # 视图写法
    CREATE VIEW v1 AS 
    SELECT 
      stuname,
      majorname 
    FROM
      suinfo s 
      INNER JOIN major m 
        ON s.majorid = m.`id` ;
    
    SELECT 
      * 
    FROM
      v1 
    WHERE stuname LIKE '张%' ;
    
  • 创建视图

    • 语法:

      create view 视图名 as 查询语句;

  • 视图的好处:

    • 重用sql语句
    • 简化复杂的sql操作,不必知道它的查询细节
    • 保护数据,提高安全性
  • 练习题:

    • 习题1:查询姓名中包含a字符的员工名、部门名和工种信息

      CREATE VIEW myv1 AS 
      SELECT 
        last_name,
        department_name,
        job_title 
      FROM
        employees e 
        JOIN departments d 
          ON e.`department_id` = d.`department_id` 
        JOIN jobs j 
          ON j.`job_id` = e.`job_id` ;
      
      SELECT 
        * 
      FROM
        myv1 
      WHERE last_name LIKE '%a%' ;
      
    • 习题2:查询各部门的平均工资级别

      # 创建视图查看每个部门的平均工资
      CREATE VIEW myv2 AS 
      SELECT 
        AVG(salary) ag,
        department_id 
      FROM
        employees 
      GROUP BY department_id ;
      
      # 使用
      SELECT 
        myv2.`ag`,
        g.grade_level 
      FROM
        myv2 
        JOIN job_grades g 
          ON myv2.`ag` BETWEEN g.`lowest_sal` 
          AND g.`highest_sal` ;
      
    • 习题3:查询平均工资最低的部门信息

      # 用习题2的视图myv2
      SELECT 
        * 
      FROM
        myv2 
      ORDER BY ag 
      LIMIT 1 ;
      
    • 习题4:查询平均工资最低的部门名和工资

      CREATE VIEW myv3 AS 
      SELECT 
        * 
      FROM
        myv2 
      ORDER BY ag LIMIT 1 ;
      
      SELECT 
        d.*,
        m.`ag` 
      FROM
        myv3 m 
        JOIN departments d 
          ON m.`department_id` = d.`department_id` ;
      
  • 视图的修改

    • 方式1:create or replace view 视图名 as 查询语句;
    • 方式2:alter view 视图名 as 查询语句;
  • 删除视图

    • 语法:drop view 视图名,视图名,…;
  • 查看视图

    • 语法:

      show create view 视图名;

      desc 视图名;

  • 案例1:创建一个视图emp_v1,要求查询电话号码以‘011’开头的员工姓名和工资、邮箱

    CREATE OR REPLACE VIEW emp_v1 AS 
    SELECT 
      last_name,
      salary,
      email 
    FROM
      employees 
    WHERE phone_number LIKE '011%' ;
    
  • 案例2:创建视图emp_v2,要求查询部门的最高工资高于12000的部门信息

    CREATE OR REPLACE VIEW emp_v2 AS 
    SELECT 
      MAX(salary) mx,
      department_id 
    FROM
      employees 
    GROUP BY department_id 
    HAVING MAX(salary) > 12000 ;
    
    SELECT 
      * 
    FROM
      emp_v2 ;
    
    SELECT 
      d.*,
      m.mx 
    FROM
      departments d 
      JOIN emp_v2 m 
        ON m.department_id = d.`department_id` ;
    
  • 视图的更新

    • 增、删、改(视图基于的表也会发生更改)

      CREATE OR REPLACE VIEW myv1 AS 
      SELECT 
        last_name,
        email
      FROM
        employees;
      
      SELECT * FROM myv1;
      
      # 插入
      INSERT INTO myv1 VALUES('张飞', 'zhangfei');
      
      # 修改
      UPDATE myv1 SET last_name='张无忌' WHERE last_name = '张飞';
      
      # 删除
      DELETE FROM myv1 WHERE last_name='张无忌';
      
    • 视图的可更新性和视图中查询的定义有关系,以下类型的视图是不能更新的。(注意:视图一般用于查询,而不是更新。

      • 包含以下关键字的sql语句:分组函数、distinct、group by、having、union或union all
      • 常量视图
      • select中包含子查询
      • join
      • from 一个不能更新的视图
      • where子句的子查询应用了from子句中的表
  • 视图和表的对比

    创建语法的关键字 是否实际占用物理空间 使用
    视图 create view 只是保存了sql逻辑 增删改查,一般不能增删改
    create table 保存了实际的数据 增删改查
  • 测试题

    • 题1:创建表

      CREATE TABLE book (
        bid INT PRIMARY KEY,
        bname VARCHAR (20) UNIQUE nut NULL,
        price FLOAT DEFAULT 10,
        btypeid INT,
        FOREIGN KEY (btypeid) REFERENCES booktype (id)
      ) ;
      
    • 题2:开启事务,向表中插入1行数据,并结束

      SET autocommit = 0 ;
      INSERT INTO book(bid, bname, price, btypeid)
      VALUES(1, '小李飞刀', 100, 1);
      COMMIT;
      
    • 题3:创建视图,实现查询价格大于100的书名和类型名

      CREATE VIEW myv1 AS 
      SELECT 
        bname,
        NAME 
      FROM
        book b 
        INNER JOIN booktype t 
          ON b.btypeid = t.id 
      WHERE price > 100 ;
      
    • 题4:修改视图,实现查询价格在90-120之间的书名和价格

      CREATE OR REPLACE VIEW myv1 AS 
      SELECT 
        bname,
        price 
      FROM
        book 
      WHERE price BETWEEN 90 
        AND 120 ;
      
    • 题5:删除刚才创建的视图

      DROP VIEW myv1;
      

变量

  • 系统变量

    • 说明:变量由系统提供,不是用户定义,属于服务器层面

    • 注意:如果是全局级别,则需要加global;如果是会话级别,则需要加session;如果不写,则默认session

    • 使用的语法:

      • 查看所有的系统变量

        SHOW GLOBAL|【SESSION】 VARIABLES;
        
      • 查看满足条件的部分系统变量

        SHOW GLOBAL|【SESSION】 VARIABLES LIKE '%char%';
        
      • 查看指定的某个系统变量的值

        SELECT @@GLOBAL|【SESSION】.系统变量名;
        
      • 为某个系统变量赋值

        • 方式一

          set GLOBAL|【SESSION】 系统变量名 = 值;
          
        • 方式二

          set @@GLOBAL|【SESSION】.系统变量名 = 值;
          
    • 分类:

      • 全局变量

        • 服务器层面上的,必须拥有super权限才能为系统变量赋值。

        • 作用域:服务器每次启动将为所有的全局变量赋初始值,针对于所有的会话(连接)有效,但不能跨重启。

        • 查看所有的全局变量

        SHOW GLOBAL VARIABLES;

        
        - 查看部分的全局变量
        
        

        SHOW GLOBAL VARIABLES LIKE '%char%';

        
        - 查看指定的全局变量的值
        
        

        SELECT @@global.autocommit;
        SELECT @@global.tx_isolation;

        
        - 为某个指定的全局变量赋值
        
        - 方式1:
        
          ```
        set global autocommit=0;
          ```
        
        - 方式2:
        
          ```
        SET @@global.autocommit=0;
          ```
        
        
      • 会话变量

        • 服务器为每一个连接的客户端都提供了系统变量。

        • 作用域:仅仅针对于当前会话(连接)有效。

        • 查看所有的会话变量

      SHOW 【SESSION】 VARIABLES;
      ```

      • 查看部分的会话变量

      SHOW 【SESSION】 VARIABLES LIKE '%char%';
      ```

      • 查看指定的某个会话变量

      SELECT @@【SESSION.】autocommit;
      ```

      • 为某个会话变量赋值

        • 方式1:

      set session autocommit=0;
      ```

      - 方式2:
      
        ```
        SET @@【session.】autocommit=0;
        ```
      
  • 自定义变量

    • 变量是用户自定义的,不是由系统定义的

    • 使用步骤:声明 赋值 使用(查看、比较、运算等)

    • 分类

      • 用户变量

        • 作用域:针对于当前会话(连接)有效,等同于会话变量的作用域
        • 应用在任何地方,也就是begin end里面或begin end的外面
        1. 声明并初始化(三种方式)

          set @用户变量名=值;
          set @用户变量名:=值;(推荐)
          select @用户变量名:=值;
          
        2. 赋值(更新用户变量的值)

          • 方式1:通过set或select(同上)

            set @用户变量名=值;
            set @用户变量名:=值;(推荐)
            select @用户变量名:=值;
            
            • 案例1:

              SET @name='John';
              SET @name=100;
              
          • 方式2:通过select into

            select 字段 into 变量名
            from 表;
            
            • 案例1:

              SELECT 
                COUNT(*) INTO @count 
              FROM
                employees ;
              
        3. 使用(查看用户变量的值)

          select @用户变量名;

      • 局部变量

        • 作用域:仅仅在定义它的begin end中有效
        • 应用在begin end中的第一句话
        1. 声明

          declare 变量名 类型;

          declare 变量名 类型 default 值;

        2. 赋值

          • 方式1:通过set或select(同上)

            set 局部变量名=值;
            set 局部变量名:=值;(推荐)
            select @局部变量名:=值;
            
          • 方式2:通过select into

            select 字段 into 局部变量名
            from 表;
            
        3. 使用

          select 局部变量名;

      • 对比用户变量和局部变量:

        作用域 定义和使用的位置 语法
        用户变量 当前会话 会话中的任何地方 必须加@符号,不用限定类型
        局部变量 begin end中 只能在begin end中,且为第一句话 一般不用加@符号,需要限定类型
      • 案例1:声明两个变量并赋初始值,求和,并打印

        • 用户变量

          SET @m=1;
          SET @n=2;
          SET @sum=@m+@n;
          SELECT @sum;
          
        • 局部变量

          # 报错
          DECLARE m INT DEFAULT 1;
          DECLARE n INT DEFAULT 2;
          DECLARE SUM INT;
          SET SUM=m+n;
          SELECT SUM;
          

存储过程和函数

  • 类似于java中的方法
  • 好处:
    • 提高代码的重用性
    • 简化操作

1. 存储过程

  • 含义:一组预先编译好的sql语句的集合,理解成批处理语句

  • 好处:

    • 提高代码的重用性
    • 简化操作
    • 减少了编译次数并且减少了和数据库服务器的连接次数,提高了效率
  • 语法:

    • 创建语法

      CREATE PROCEDURE 存储过程名(参数列表)
      BEGIN
      	存储过程体(一组合法的SQL语句)
      END
      
    • 注意

      • 参数列表包含三部分:参数模式,参数名,参数类型

        • 举例:in stuname varchar(20)
        • 参数模式:
          • in:该参数可以作为输入,也就是该参数需要调用方传入值
          • out:该参数可以作为输出,也就是该参数可以作为返回值
          • inout:该参数既可以作为输入又可以作为输出,也就是该参数既需要传入值,又需要返回值
      • 如果存储过程体仅仅只有一句话,begin end可以省略

      • 存储过程体中的每条SQL语句的结尾要求必须加分号

      • 存储过程的结尾可以使用 DELIMITER 重新设置

        • 语法:DELIMITER 结束标记

        • 案例:

          DELIMITER $
          
    • 调用语法

      • call 存储过程名(实参列表);
  • 空参列表

    • 案例1:插入到admin表中五条记录

      SELECT * FROM admin;
      DELIMITER $
      
      CREATE PROCEDURE myp1()
      BEGIN
      INSERT INTO admin(username, `password`) 
      VALUES
      ('john1', '0000'),
      ('asd', '0000'),
      ('joqqhn1', '0000'),
      ('qa', '0000'),
      ('ww', '0000');
      END $
      
      CALL myp1()$
      
  • 创建带in模式参数的存储过程

    • 案例1:创建存储过程实现:根据女神名,查询对应的男神信息

      CREATE PROCEDURE myp2(IN beautyname VARCHAR(20))
      BEGIN
      SELECT bo.*
      FROM boys bo
      RIGHT JOIN beauty b ON bo.id = b.boyfriend_id
      WHERE b.name=beautyname;
      END $
      
      CALL myp2('柳岩')$
      CALL myp2('王语嫣')$
      
    • 案例2:创建存储过程实现,用户是否登录成功

      CREATE PROCEDURE myp4(IN username VARCHAR(20), IN passward VARCHAR(20))
      BEGIN
      DECLARE result INT DEFAULT 0;	# 声明并初始化
      
      SELECT COUNT(*) INTO result	# 赋值
      FROM admin
      WHERE admin.username = username
      AND admin.password = PASSWORD;
      
      SELECT IF(result>0, '成功!', '失败!'); # 使用
      END $
      
      CALL myp4('张飞', '8888')$
      
  • 创建带out模式的存储过程

    • 案例1:根据女神名,返回对应的男神名

      CREATE PROCEDURE myp5(IN beautyname VARCHAR(20), OUT boyname VARCHAR(20))
      BEGIN
      SELECT bo.boyname INTO boyname
      FROM boys bo
      INNER JOIN beauty b ON bo.id = b.boyfriend_id
      WHERE b.name=beautyname;
      END $
      
      CALL myp5('王语嫣', @bname)$
      SELECT @bname$
      
    • 案例2:根据女神名,返回对应的男神名和男神魅力值

      CREATE PROCEDURE myp6(IN beautyname VARCHAR(20), OUT boyname VARCHAR(20), OUT usercp INT)
      BEGIN
      SELECT bo.boyname, bo.usercp INTO boyname, usercp
      FROM boys bo
      INNER JOIN beauty b ON bo.id = b.boyfriend_id
      WHERE b.name=beautyname;
      END $
      
      CALL myp6('王语嫣', @bname, @usercp)$
      SELECT @bname, @usercp$
      
  • 创建带inout模式参数的存储过程

    • 案例1:传入a和b两个值,最终a和b都翻倍并返回

      CREATE PROCEDURE myp8(INOUT a INT, INOUT b INT)
      BEGIN
      SET a=a*2;
      SET b=b*2;
      END $
      
      SET @m=10$
      SET @n=20$
      CALL myp8(@m,@n)$
      SELECT @m,@n$
      
  • 练习题

    • 习题1:创建存储过程实现传入用户名和密码,插入到admin表中

      CREATE PROCEDURE test_pro1(IN username VARCHAR(20), IN loginpwd VARCHAR(20))
      BEGIN
      INSERT INTO admin(admin.`username`, PASSWORD)
      VALUES(username, loginpwd);
      END $
      
      CALL test_pro1('litian', '1234')$
      SELECT * FROM admin$
      
    • 习题2:创建存储过程实现传入女神编号,返回女神名称和女神电话

      CREATE PROCEDURE test_pro2(IN id INT, OUT NAME VARCHAR(20), OUT phone VARCHAR(20))
      BEGIN
      SELECT b.name, b.phone INTO NAME, phone
      FROM beauty b
      WHERE b.id=id;
      END $
      
      SET @n=''$
      SET @m=''$
      CALL test_pro2(1, @n, @m)$
      SELECT @m,@n$
      
    • 习题3:创建存储过程来实现传入两个女神生日,返回大小

      CREATE PROCEDURE test_pro3(IN birth1 DATETIME, IN birth2 DATETIME, OUT result INT)
      BEGIN
      SELECT DATEDIFF(birth1, birth2) INTO result;
      END $
      
      CALL test_pro3('1990-2-3', NOW(), @result)$
      SELECT @result$
      
  • 存储过程的删除

    • 语法:drop procedure 存储过程名

      DROP PROCEDURE myp1;
      
    • 不能同时删除多个存储过程

  • 查看存储过程的信息

    • 语法:show create procedure 存储过程名

      SHOW CREATE PROCEDURE myp2;
      
    • 不能修改存储过程中的语句,需要修改的话,就删了重写。

  • 练习题

    • 练习题1:创建存储过程实现传入一个日期,格式化成xx年xx月xx日并返回

      CREATE PROCEDURE test_pro4(IN mydate DATETIME, OUT strdate VARCHAR(20))
      BEGIN
      SELECT DATE_FORMAT(mydate, '%y年%m月%d天') INTO strdate;
      END $
      
      CALL test_pro4(NOW(), @str)$
      SELECT @str $
      
    • 练习题2:创建存储过程实现传入女神名称,返回:女神 and 男神 格式的字符串

      CREATE PROCEDURE test_pro5(IN beautyname VARCHAR(20), OUT str VARCHAR(50))
      BEGIN
      SELECT CONCAT(beautyname, ' and ', IFNULL(boyname, 'null')) INTO str
      FROM boys bo
      RIGHT JOIN beauty b
      ON b.boyfriend_id=bo.id
      WHERE b.name=beautyname;
      END $
      
      CALL test_pro5('王语嫣', @result)$
      SELECT @result$
      
    • 练习题3:创建存储过程,根据传入的起始索引和条目数,查询beauty表的记录

      CREATE PROCEDURE test_pro6(IN startindex INT, IN size INT)
      BEGIN
      SELECT * FROM beauty LIMIT startindex, size;
      END $
      
      CALL test_pro6(3, 3)$
      

2. 函数

  • 好处:

    • 提高代码的重用性
    • 简化操作
    • 减少了编译次数并且减少了和数据库服务器的连接次数,提高了效率
  • 与存储过程的区别:

    • 存储过程:可以有0个返回,也可以有多个返回(适合做批量插入、更新)
    • 函数:有且仅有1个返回(适合处理数据后返回一个结果)
  • 创建语法:

    create function 函数名(参数列表) returns 返回类型

    begin

    函数体

    end

    • 注意事项:
      • 参数列表:包含两部分:参数名 参数类型,注意:没有in,out,inout这种模式了
      • 函数体:肯定会有return语句,如果没有会报错
      • 如果return语句没有放在函数体的最后也不报错,但不建议
      • 函数体中仅有一句话,则可以省略begin end
      • 使用delimiter语句设置结束标记
  • 调用语法:select 函数名(参数列表)

    • 无参有返回

      • 案例1:返回公司的员工个数

        CREATE FUNCTION myf1() RETURNS INT
        BEGIN
        DECLARE c INT DEFAULT 0;# 定义变量
        SELECT COUNT(*) INTO c# 赋值
        FROM employees;
        RETURN c;
        END $
        
        SELECT myf1()$
        
    • 有参有返回

      • 案例1:根据员工名,返回他的工资

        CREATE FUNCTION myf2(empname VARCHAR(20)) RETURNS DOUBLE
        BEGIN
        SET @sal=0;# 定义用户变量
        SELECT salary INTO @sal# 赋值
        FROM employees
        WHERE last_name = empname;
        RETURN @sal;
        END $
        
        SELECT myf2('kochhar')$
        
      • 案例2:根据部门名,返回该部门的平均工资

        CREATE FUNCTION myf3(deptname VARCHAR(20)) RETURNS DOUBLE
        BEGIN
        DECLARE sal DOUBLE;
        SELECT AVG(salary) INTO sal
        FROM employees e
        JOIN departments d ON e.department_id = d.department_id
        WHERE d.department_name=deptname;
        
        RETURN sal;
        END $
        
        SELECT myf3('IT')$
        
  • 查看函数:

    SHOW CREATE FUNCTION myf3;
    
  • 删除函数:

    DROP FUNCTION myf3;
    
  • 案例1:创建函数,实现传入两个float,返回二者之和

    CREATE FUNCTION test_fun1(num1 FLOAT, num2 FLOAT) RETURNS FLOAT
    BEGIN
    DECLARE SUM FLOAT DEFAULT 0;
    SET SUM=num1+num2;
    RETURN SUM;
    END $
    
    SELECT test_fun1(1,2)$
    

流程控制结构

  • 分类

    • 顺序结构:结构从上往下依次执行
    • 分支结构:程序从两条或多条路径中选择一条去执行
    • 循环结构:程序在满足一定条件的基础上,重复执行一段代码
  • 分支结构

    • if函数

      • 功能:实现简单的双分支

      • 语法:

        select if(表达式1,表达式2,表达式3)

      • 执行顺序:

        如果表达式1成立,则if函数返回表达式2的值,否则返回表达式3的值

      • 应用环境:任何地方

    • case结构

      • 情况1:类似于java中的switch语句,一般用于实现等值判断

        • 语法:

          case 变量|表达式|字段

          when 要判断的值 then 返回的值1或语句1;

          when 要判断的值 then 返回的值2或语句2;

          else 要返回的值n或语句n;

          end case;

      • 情况2:类似于java中的多重if语句,一般用于实现区间判断

        • 语法:

          case

          when 要判断的条件1 then 返回的值1或语句1;

          when 要判断的条件2 then 返回的值2或语句2;

          else 要返回的值n或语句n;

          end case;

      • 特点:

        • 可以作为表达式,嵌套在其他语句中使用,可以放在任何地方,begin end 中,或 begin end 的外面
        • 也可以作为独立的语句去使用,只能放在begin end 中
        • 如果when中的值满足或条件成立,则执行对应的then后面的语句,并且结束case
        • 如果都不满足,则执行else中的语句或值
        • else可以省略,如果else省略了,并且所有的when条件都不满足,则返回null

      img

      img

    • 案例1:创建存储过程,根据传入的成绩,来显示等级,比如传入的成绩:90-100,显示A;80-90:显示B;60-80:显示C;否则显示D

      CREATE PROCEDURE test_case(IN score INT)
      BEGIN
      CASE 
      WHEN score >= 90 AND score <= 100 THEN SELECT 'A';
      WHEN score >=80 THEN SELECT 'B';
      WHEN score >= 60 THEN SELECT 'C';
      ELSE SELECT 'D';
      END CASE;
      END $
      
      CALL test_case(95)$
      
    • if结构

      • 功能:实现多重分支

      • 语法:

        if 条件1 then 语句1;

        elseif 条件2 then 语句2;

        【else 语句n;】

        end if;

      • 应用场合:应用在begin end中

    • 案例2:创建存储过程,根据传入的成绩,来返回等级,比如传入的成绩:90-100,返回A;80-90:返回B;60-80:返回C;否则返回D

      CREATE FUNCTION test_if(score INT) RETURNS CHAR
      BEGIN
      IF score >= 90 AND score <= 100 THEN RETURN 'A';
      ELSEIF score >=80 THEN RETURN 'B';
      ELSEIF score >= 60 THEN RETURN 'C';
      ELSE RETURN 'D';
      END IF;
      END $
      
      SELECT test_if(86)$
      
  • 循环结构

    • 分类:while、loop、repeat

    • 循环控制:

      • iterate:类似于continue,继续;结束本次循环,继续下一次
      • leave:类似于break,跳出;结束当前所在循环
    • while

      • 语法:

        [标签:】while 循环条件 do

        循环体;

        end while 【标签】;

    • loop

      • 语法:

        【标签:】loop

        循环体;

        end loop 【标签】;

      • 可以用来模拟简单的死循环

    • repeat

      • 语法:

        【标签:】repeat

        循环体;

        until 结束循环的条件

        end repeat【标签】;

    • 对比:

      • 这人三种循环都可以省略名称,但如果循环中添加了循环控制语句(leave或iterate)则必须添加名称
      • loop:一般用于实现简单的死循环
      • while:先判断后执行
      • repeat:先执行后判断,无条件至少执行一次
  • 案例1:没有添加循环控制语句

  • 批量插入,根据次数插入到admin表中多条记录

    CREATE PROCEDURE pro_while1(IN insertcount INT)
    BEGIN
    DECLARE i INT DEFAULT 1;
    WHILE i <= insertcount DO
    INSERT INTO admin(username, `password`) VALUES(CONCAT('Rose', i), '666');
    SET i=i+1;
    END WHILE;
    END $
    
    CALL pro_while1(100)$
    SELECT * FROM admin;
    
  • 案例2:添加leave语句

  • 批量插入,根据次数插入到admin表中20条记录

    CREATE PROCEDURE test_while1(IN insertcount INT)
    BEGIN
    DECLARE i INT DEFAULT 1;
    a:WHILE i <= insertcount DO
    INSERT INTO admin(username, `password`) VALUES(CONCAT('xiaohua', i), '000');
    IF i>=20 THEN LEAVE a;
    END IF;
    SET i=i+1;
    END WHILE a;
    END $
    
    CALL test_while1(100)$
    SELECT * FROM admin;
    
  • 案例3:添加iterate语句

  • 批量插入,根据次数插入到admin表中多条记录,只插入偶数次

    CREATE PROCEDURE test_while2(IN insertcount INT)
    BEGIN
    DECLARE i INT DEFAULT 0;
    a:WHILE i <= insertcount DO
    SET i=i+1;
    IF MOD(i,2) != 0 THEN ITERATE a;
    END IF;
    INSERT INTO admin(username, `password`) VALUES(CONCAT('xiaohua', i), '000');
    END WHILE a;
    END $
    
    CALL test_while2(100)$
    SELECT * FROM admin;
    

img

  • 案例4:已知表stringcontent,其中字段id 自增长;content varchar(20),向该表中插入指定个数的随机的字符串

    USE test;
    DROP TABLE IF EXISTS stringcontent;
    CREATE TABLE stringcontent(
    id INT PRIMARY KEY AUTO_INCREMENT,
    content VARCHAR(20)
    );
    
    DELIMITER $
    CREATE PROCEDURE test_randstr_insert(IN insertcount INT)
    BEGIN
    DECLARE i INT DEFAULT 1;# 定义一个循环变量i,表示插入次数
    DECLARE str VARCHAR(26) DEFAULT 'abcdefghijklmnopqrstuvwxyz';
    DECLARE startindex INT DEFAULT 1;# 代表起始索引
    DECLARE len INT DEFAULT 1;# 代表截取的字符的长度
    WHILE i<=insertcount DO
    SET len=FLOOR(RAND()*(20-startindex+1)+1);# 产生一个随机的整数,代表截取长度,1-(26-startindex+1)
    SET startindex=FLOOR(RAND()*26+1);# 产生一个随机的整数,代表起始索引1-26
    INSERT INTO stringcontent(content) VALUES(SUBSTR(str, startindex, len));
    SET i=i+1;
    END WHILE;
    END $
    
    CALL test_randstr_insert(10)$
    SELECT * FROM stringcontent;
    CALL test_randstr_insert(10)$
    SELECT * FROM stringcontent;
    

我的CSDN:https://blog.csdn.net/qq_21579045

我的博客园:https://www.cnblogs.com/lyjun/

我的Github:https://github.com/TinyHandsome

纸上得来终觉浅,绝知此事要躬行~

欢迎大家过来OB~

by 李英俊小朋友

posted @ 2019-08-01 22:09  李英俊小朋友  阅读(1506)  评论(0编辑  收藏  举报