零基础学 MySQL(基础版)

news2024/11/25 20:16:03

零基础学 MySQL(基础版)

1. 引出

思考一个问题:

淘宝网,京东、微信,抖音 都有各自的功能,那么当我们退出系统的时候,下次再访问时,为什么信息还存在?

2. 解决之道

2.1 解决之道—文件、数据库

  1. 为了解决上述问题,使用更加利于管理数据的东东-数据库,它能更有效的管理数据。

  2. 举一个生活化的案例说明:

    如果说 图书馆是保存书籍的,那么数据库就是保存数据的。

2.2 MySQL 数据库的安装和配置(安装演示)

详见我单独发的《MySQL-5.7.19版本安装详解》

2.3 使用命令行窗口连接MYSQL 数据库[示意图]

2.4 操作示意图

3. 图形化MySQL的管理软件

  1. 在我们实际开发中,会用到一些图形化MySQL的管理软件

  2. 其中用的比较多的是Navicat和SQLyog

  3. 这两个软件安装的方式就是傻瓜式安装,不做过多讲解

  4. 下载地址:

    1. Navicat地址:http://www.navicat.com.cn/products/

    2. SQLyog下载地址:https://sqlyog.en.softonic.com/

  5. 我这里就先用SQLyog操作了

  6. SQLyog安装完以后打开,新建一个本地的MySQL

  7. 然后电脑ip那里可以填写localhost或者127.0.0.1(电脑本机ip),密码可以填写上去也可以不填,其他都不用动,点连接就行

  8. 设置完以后,即可登录MySQL;注意:一定要保证MySQL 服务是运行的状态!

4. 数据库三层结构

  1. 所谓安装Mysql数据库,就是在主机安装一个数据库管理系统(DBMS),这个管理程序可以管理多个数据库。DBMS(database manage system)
  2. 一个数据库中可以创建多个表,以保存数据(信息)。
  3. 数据库管理系统(DBMS)、数据库和表的关系如图所示: 示意图如下

5. 数据在数据库中的存储方式

6. SQL 语句分类

  1. DDL:数据定义语句 [create 表,库…]
  2. DML: 数据操作语句[增加 insert ,修改 update,删除 delete]
  3. DQL: 数据查询语句 [select ]
  4. DCL:数据控制语句[管理数据库: 比如用户权限 grant revoke ]

7. 创建数据库

  1. 基本语法:

说明:

  1. CHARACTER SET: 指定数据库采用的字符集,如果不指定字符集,默认utf8
  2. COLLATE: 指定数据库字符集的校对规则(常用的 utf8_bin[区分大小写]、utf8_general ci[不区分大小写] 注意默认是 utf8_general ci)[举例说明database.sql 文件]

练习:

  1. 练习:创建一个名称为sys_db01的数据库。[图形化和指令 演示)
  2. 创建一个使用utf8字符集的sys_db02数据库
  3. 创建一个使用utf8字符集,并带校对规则的sys_db03数据库
  4. 演示如下:
#演示创建数据库
#1. 练习:创建一个名称为sys_db01的数据库。[图形化和指令 演示)

#使用指令创建数据库
CREATE DATABASE sys_db01
#删除数据库
DROP DATABASE sys_db01

#2. 创建一个使用utf8字符集的sys_db02数据库

CREATE DATABASE sys_db02 CHARACTER SET utf8

#3. 创建一个使用utf8字符集,并带校对规则的sys_db03数据库

CREATE DATABASE sys_db03 CHARACTER SET utf8 COLLATE utf8_bin
  1. 直接生成了3个数据库

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IslQmR6l-1686661207668)(E:\Java study\Markdown\本地图片\演示创建数据库.png)]

  1. 这里稍微提一下校对规则
# 校对规则 utf8_bin 区分大小写	默认utf8_general_ci 不区分大小写

一、当我们在sys_db02数据库中创建一个表t1时,创建时啥都不设置的话,校验规则就是默认utf8_general_ci 不区分大小写

在表t1里面添加两个数据

现在我们进行查询

# 下面是一条查询的sql , selset 查询 * 表示所有字段 FROM 从哪个表查
# WHERE 从哪个字段 NAME = 'jack' 查询名字是 jack
SELECT * 
	FROM t1 
	WHERE NAME = 'jack'

查询结果如下

二、当我们在sys_db03数据库中创建一个表t1时,创建时啥都不设置的话,校验规则就是默认utf8_bin区分大小写

在表t1里面添加两个数据(与上面相同)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aHXgHlHw-1686661207670)(E:\Java study\Markdown\本地图片\t1的数据.png)]

现在我们进行查询

# 下面是一条查询的sql , selset 查询 * 表示所有字段 FROM 从哪个表查
# WHERE 从哪个字段 NAME = 'jack' 查询名字是 jack
SELECT * 
	FROM t1 
	WHERE NAME = 'jack'

查询结果如下

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IZNqHwDO-1686661207671)(E:\Java study\Markdown\本地图片\sys_db03的t1的查询结果.png)]

8. 查看,删除数据库

  1. 基本语法

练习:

  1. 查看当前数据库服务器中的所有数据库
  2. 查看前面创建的sys_db01数据库的定义信息
  3. 删除前面创建的sys_db01数据库
  4. 演示如下
# 演示删除和查询数据库
#1. 查看当前数据库服务器中的所有数据库
SHOW DATABASES 
#2. 查看前面创建的hsp db01数据库的定义信息
SHOW CREATE DATABASE ‘sys_db01‘

#说明:在创建数据库,表的时候,为了规避关键字,可以使用反引号解决

#3. 删除前面创建的hsp db01数据库
DROP DATABASE sys_db01

查看当前数据库服务器中的所有数据库示意图

查看前面创建的hsp db01数据库的定义信息示意图

删除前面创建的hsp db01数据库示意图

9. 备份恢复数据库

  1. 备份数据库(注意:在DOS执行命令行

    mysqldump -u 用户名 -p -B 数据库1 数据库2 数据库n > 文件名.sq

  2. 恢复数据库(注意: 进入Mysql命令行再执行)

    Source 文件名.sgl

练习:

  1. database03.sql 备份sys_db02 库中的数据,并恢复
  2. 先删除原先的数据库sys_db02
#删除数据库
DROP DATABASE sys_db02
  1. 第一个恢复方法:先进入到mysql命令行,然后再mysql命令行输入source f:\bak.sql (source 备份文件的地址+名字)
  2. 第二个恢复方法:直接打开刚刚备份的bak.sql , 然后将里面的内容全部复制到查询编辑器里面,然后执行就能恢复
#恢复数据库(注意:进入mysql命令行再执行)
source f:\\bak.sql

#第二个恢复方法,直接将bak.sql的内容放到查询编辑器中执行

10. 备份恢复数据库的表

  1. 备份数据库中的表格就是备份数据库的命令不要输-B,后面写上数据库+表名
mysqldump -u 用户名 -p密码 数据库 表12 表n > d:文件名sq
  1. 小练习:备份数据库sys_db03中的表t1
#直接在命令行输入mysqldump -U root -p sys_db03 t1 > f:\\table03.sql就备份好了

11. 创建表 (按课程大纲顺序)

  1. 基本语法

  1. 注意: sys_db02创建表时,要根据需保存的数据创建相应的列,并根据数据的类型定义相应的列类型。

    例: 下面那个user表

    id 整形 [图形化,指令]

    name 字符串

    password 字符串

    birthday 日期

CREATE TABLE `user`(
	id INT,
	`name` VARCHAR(255),
	`password` VARCHAR(255),
	birthday DATE)
	CHARACTER SET utf8 COLLATE utf8_bin ENGINE INNODB;

然后在sys_db02页面执行这行命令,就创建成功了

12. Mysql 常用数据类型(列类型)

12.1 简介

  1. Mysql 常用数据类型(也叫列类型)分为数值类型、文本类型、二进制类型和时间日期
  2. 数值类型又分为整型和小数类型:
    1. 整型有:
      1. tinyint [1个字节]
      2. smallint [2个字节]
      3. mediumint [3个字节]
      4. int [4个字节]
      5. bigint [8个字节]
    2. 小数类型有:
      1. float [单精度 4个字节]
      2. double [双精度 8个字节]
      3. decimal [M,D] [大小不确定,由 M D确定]
  3. 文本类型(也叫字符串类型):
    1. char (0~255)
    2. varchar (0~25535) [0~2^16-1]
    3. text [0~2^16-1]
    4. longtext [0~2^32-1]
  4. 二进制类型:
    1. blob [0~2^16-1]
    2. longblob [0~2^32-1]
  5. 日期类型:
    1. date [日期 存放 年月日]
    2. time [存放 时分秒]
    3. datetime [存放 年月日 时分秒 YYYY-MM-DD-HH:MM:ss]
    4. timestamp [时间戳]
    5. year [存放年]
  6. 以上就是Mysql 最常用数据类型(列类型)

12.2 数值型(整数)的基本使用

说明

  1. 使用规范:在能够满足需求的情况下,尽量选择占用空间小的类型

  1. 应用实例:使用tinyint来演示一下范围
#演示整型
#使用tinyint来演示范围 有符号 -128~127  没有符号 0~255
#表的字符集、校验规则,存储引擎使用默认
# 1. 如果没有指定 unsigned ,则 TINYINT 就是有符号
# 2. 如果指定 unsigned , 则 TINYINT 就是无符号 0-255 
CREATE TABLE t3(
	id TINYINT);#有符号
CREATE TABLE t4(
	id TINYINT UNSIGNED);#无符号
	
INSERT INTO t3 VALUES(-128);#非常简单的添加语句

INSERT INTO t4 VALUES(0);#非常简单的添加语句

SELECT * FROM t3;
SELECT * FROM t4;

12.3 型如何定义一个无符号的整数

create table t10 (id tinyint ); //默认是有符号的
create table t11 (id tinyint unsigned ); //无符号的

12.4 数值型(bit)的使用

  1. 基本使用

    mysql> create table t05 (num bit(8));

    mysql> insert into t05 (1, 3);

    mysql> insert into t05 values(2, 65);

  2. 细节说明

    • bit 字段显示时,按照 位 的方式去显示;
    • 查询的时候仍然可以用使用 添加的数值;
    • 如果一个值只有 0 ,1 可以考虑使用 bit(1) ,可以节约空间;
    • 位类型 bit(M) M指定位数,默认值是1,范围是1-64
    • 在实际开发中使用的不多
  3. 演示:

#演示 bit 类型使用
#说明
# 1. bit(m) m在 1-64
# 2. 添加数据范围
# 3. 显示按照bit
# 4. 查询时,仍然可以按照数值来查询
CREATE TABLE t05(num BIT(8));
INSERT INTO t05 VALUES(255);
SELECT * FROM t05;
SELECT * FROM t05 WHERE num = 1;

12.5 数值型(小数)的基本使用

  1. FLOAT/DOUBLE [UNSIGNEDI

    1. Float 单精度精度,
    2. Double 双精度
  2. DECIMAL [M,D] [UNSIGNED]

    • 可以支持更加精确的小数位。M是小数位数(精度)的总数,D是小数点(标度)后面的位数。
    • 如果D是0,则值没有小数点或分数部分。M最大65。D最大是30。如果D被省略,默认是0。如果M被省略,默认是10。
    • 建议:如果希望小数的精度高,推荐使用decimal
  3. 演示decimal类型、float、double使用

#演示decimal类型、floatdouble使用

#创建表
CREATE TABLE t06(
	num1 FLOAT,
	num2 DOUBLE,
	num3 DECIMAL(30,20));
	
#添加数据
INSERT INTO t06 VALUES(88.151351516164161,
	88.151351516164161,88.151351516164161);

SELECT * FROM t06
  1. 可以看到表格显示float精度很低,decimal精度最高

  1. decimalye可以存放特别大的数,但是float和double不能存放特别大的数
#decimal可以存放很大的数
CREATE TABLE t07(
	num DECIMAL(65))
INSERT INTO t07 VALUES(151154111544152148154164616484165184);
SELECT * FROM t07;
  1. 所以,如果要存放特别大的数,或者精度特别高的数就用decimal;如果要求没那么高的话,一般用double就够用了。

12.6 字符串的基本使用

  1. CHAR(size)

    固定长度字符串 最大255 字符

  2. VARCHAR(size)

    0~65535可变长度字符串 最大65532字节 [utf8编码最大21844字符 1-3个字节用于记录大小]

  3. 应用案例 charVarchar.sql 文件

#演示字符串类型使用 char varchar
#注释的快捷键 shift+ctrl+c
#取消注释 shift+ctrl+r
-- 1. CHAR(size)
--    固定长度字符串 最大255 字符
-- 2. VARCHAR(size) 
--    0~65535可变长度字符串 最大65532字节 [utf8编码最大21844字符 1-3个字节用于记录大小]
-- 如果表的编码为 utf8  varchar(size)=(65535-3)/3 = 21844
-- 如果表的编码为 gbk  varchar(size)=(65535-3)/2 = 32766

CREATE TABLE t08(
	`NAME` CHAR(255));
	
CREATE TABLE t09(
	`name` VARCHAR(21844));
	
CREATE TABLE t10(
	`name` VARCHAR(32766) CHARSET gbk);

12.7 字符串使用细节

细节1 charVarcharDetail.sql

  1. char(4) //这个4表示字符数(最大255),不是字节数不管是中文还是字母都是放四个,按字符计算.
  2. varchar(4)//这个4表示字符数,不管是字母还是中文都以定义好的表的编码来存放数据
  3. 不管是 中文还是英文字母,都是最多存放4个,是按照字符来存放的

细节2

  1. char(4)是定长(固定的大小),就是说,即使你 插入aa’,也会占用 分配的4个字符的空间.
  2. varchar(4)是变长(变化的大小),就是说,如果你插入了aa’实际占用空间大小并不是4个字符,而是按照实际占用空间来分配
  3. (说明:varchar本身还需要占用1-3个字节来记录存放内容长度) L(实际数据大小)+(1-3)字节

细节3

什么时候使用 char,什么时候使用varchar1.

  1. 如果数据是定长,推荐使用char,比如md5的密码,邮编,手机号,身份证号码等char(32)
  2. 如果一个字段的长度是不确定,我们使用varchar ,比如留言,文章
  3. 查询速度: char > varchar

细节4

  1. 在存放文本时,也可以使用Text 数据类型.可以将TEXT列视为VARCHAR列,注意 Text 不能有默认值.大小 0-2^16 字节
  2. 如果希望存放更多字符,可以选择MEDIUMTEXT 0-2^24 或者 LONGTEXT O~2^32
#细节1char(4)varchar(4) 这个4表示的是字符,而不是字节
CREATE TABLE t11(
	`name` CHAR(4));
INSERT INTO t11 VALUES('abcd');#如果是5个就存放失败
SELECT * FROM t11;

CREATE TABLE t12(
	`name` VARCHAR(4));
INSERT INTO t12 VALUES('大家好a')#如果是5个就存放失败
SELECT * FROM t12;

#细节4:
#如果varchar不够用,可以考虑mediumtext或者longtext
#如果想简单点,可以直接使用text
CREATE TABLE t13(content1 TEXT,content2 MEDIUMTEXT,content3 LONGTEXT);
INSERT INTO t13 VALUES('sys学java','sys学java加油','sys学java~~~');
SELECT * FROM t13;

12.8 日期类型的基本使用

CREATE TABLE birthday6(

​ t1 DATE, t2 DATETIME,

​ t3 TIMESTAMP NOT NULL DEFAULT

​ CURRENT TIMESTAMP ON UPDATE

​ CURRENT TIMESTAMP ); timestamp时间截

mysql> INSERT INTO birthday (t1,t2)

VALUES(‘2022-11-11’,‘2022-11-11 10:10:10’);

日期类型的细节说明

TimeStamp在Insert和update时,自动更新datetime.sql

#演示时间相关的类型
#创建一张表 , date ,datetime , timestamp
CREATE TABLE t14(
	birthday DATE,-- 生日
	job_time DATETIME,-- 记录年月日 时分秒 
	login_time TIMESTAMP 
	NOT NULL DEFAULT CURRENT_TIMESTAMP 
	ON UPDATE CURRENT_TIMESTAMP);-- 登陆时间,如果希望login_time列自动更新
	
SELECT * FROM t14;

INSERT INTO t14 (birthday,job_time) 
	VALUES('2023.6.1','2023.6.1 10:10:10');
-- 如果我们更新了 t14表的某条记录,login_time列会自动的以当前的时间进行更新

13. 创建表练习

创建一个员工表 emp (课堂练习),选用适当的数据类型 createtable.sql

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-l2ccHj1x-1686661207672)(E:\Java study\Markdown\本地图片\创建表练习.png)]

代码如下:

#创建一个员工表 emp (课堂练习),选用适当的数据类型
-- 字段	属性
-- id	整形
-- name	字符型
-- sex	字符型
-- brithday	日期型(DATE)
-- entry date	日期型(DATE)
-- job	字符型
-- Salary	小数型
-- resume	文本型
CREATE TABLE `emp`(
	id INT,
	`name` VARCHAR(32),
	sex CHAR(1),
	birthday DATE,
	entry_date DATETIME,
	job VARCHAR(32),
	salary FLOAT,
	`resume` TEXT)
	CHARSET utf8 COLLATE utf8_bin ENGINE INNODB;
-- 添加一条记录
INSERT INTO `emp`
	VALUES(168,'张三','男','1993-11-26',
	'2023-7-11 8:00:00','java','15000','java');
	
-- 查询
SELECT * FROM `emp`;

效果如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6g5NI52C-1686661207673)(E:\Java study\Markdown\本地图片\创建表emp.png)]

14. 修改表

14.1 基本介绍

使用 ALTER TABLE 语句追加,修改,或删除列的语法:

修改表名: Rename table 表名 to 新表名

修改表字符集: alter table 表名 character set 字符集:

修改表列名:alter table user change column name username varchar(20);

14.2 应用实例

  1. 员工表emp的上增加一个image列,varchar类型(要求在resume后面)
  2. 修改job列,使其长度为60。
  3. 删除sex列。
  4. 表名改为employee。
  5. 修改表的字符集为utf8
  6. 列名name修改为user name
  7. 代码演示如下:
#修改表的操作练习
-- 1. 员工表emp的上增加一个image列,varchar类型(要求在resume后面)
ALTER TABLE emp  -- 修改表emp
	-- 增加 image列,varchar类型,不允许为空,后面加个空字符串
	ADD image VARCHAR(32) NOT NULL DEFAULT '' 
	AFTER RESUME -- 加在resume字段后面
DESC employee -- 显示表结构,可以查看表的所有列
-- 2. 修改job列,使其长度为60。
ALTER TABLE emp
	MODIFY job VARCHAR(60) NOT NULL DEFAULT''
-- 3. 删除sex列。
ALTER TABLE emp
	DROP sex
-- 4. 表名改为employee。
RENAME TABLE emp TO employee
-- 5. 修改表的字符集为utf8
ALTER TABLE employee CHARACTER SET utf8
-- 6. 列名name修改为user name
ALTER TABLE employee
	CHANGE `name` user_name VARCHAR(64) NOT NULL DEFAULT ''

15. 数据库CRUD语句(增删改查)

数据库C[create]R[read]U[update]D[delete]语句

  1. Insert语句 (添加数据)
  2. Update语句 (更新数据)
  3. Delete语句 (删除数据)
  4. Select语句 (找数据)

15.1 Insert 语句

  1. 使用 INSERT 语句向表中插入数据。insert.sql

练习1:

  1. 创建一张商品表goods (id int,goods_name varchar(10) ,price double );
  2. 添加2条记录
# 练习 insert 语句
-- 1. 创建一张商品表goods (id int,goods_name varchar(10) ,price double );
-- 2. 添加2条记录
CREATE TABLE goods(
	id INT,
	goods_name VARCHAR(10),
	price DOUBLE);
-- 添加数据
INSERT INTO `goods`(id,goods_name,price)
	VALUES(1,'小米手机',1999);
	
INSERT INTO `goods`(id,goods_name,price)
	VALUES(2,'苹果手机',9999);

-- 查阅
SELECT * FROM goods;

效果如下

练习2:

  1. 使用insert 语句向上面做的表employee 中插入 2 个员工的信息。

15.2 insert 细节说明

  1. 插入的数据应与字段的数据类型相同。

    – 比如 把 ‘abc’ 添加到 int 类型会错误

  2. 数据的长度应在列的规定范围内,例如:不能将一个长度为 80 的字符串加入到长度为 40 的列中。

  3. 在 values 中列出的数据位置必须与被加入的列的排列位置相对应。

  4. 字符和日期型数据应包含在单引号中。

  5. 列可以插入空值[前提是该字段允许为空],insert into table value(null)

  6. insert into tab_name (列名…) values (),(),() 形式添加多条记录

  7. 如果是给表中的所有字段添加数据,可以不写前面的字段名称

  8. 默认值的使用,当不给某个字段值时,如果有默认值就会添加默认值,否则报错

    – 如果某个列 没有指定 not null ,那么当添加数据时,没有给定值,则会默认给 null

    – 如果我们希望指定某个列的默认值,可以在创建表时指定

  9. 演示如下:

#说明 insert 语句的细节
CREATE TABLE goods2(
	id INT,
	goods_name VARCHAR(10),
	price DOUBLE NOT NULL DEFAULT 100);
-- 1. 插入的数据应与字段的数据类型相同。
	-- 比如 把 'abc' 添加到 int 类型会错误
INSERT INTO `goods2`(id,goods_name,price)
	VALUES('abc','小米手机',1999);-- 会直接报错
	
-- 2. 数据的长度应在列的规定范围内,例如:不能将一个长度为 80 的字符串加入到长度为 40 的列中。
INSERT INTO `goods2`(id,goods_name,price)
	VALUES(3,'锤子手机锤子手机锤子手机锤子手机',1999);-- goods_name的长度为10,超过范围
	
-- 3. 在 values 中列出的数据位置必须与被加入的列的排列位置相对应。
INSERT INTO `goods2`(id,goods_name,price)
	VALUES('小米手机',1,1999);-- 位置不对应
	
-- 4. 字符和日期型数据应包含在单引号中。
INSERT INTO `goods2`(id,goods_name,price)
	VALUES(1,小米手机,1999);-- varchar 不用单引号会报错

-- 5. 列可以插入空值[前提是该字段允许为空],insert into table value(null) 
INSERT INTO `goods2`(id,goods_name,price)
	VALUES(NULL,'小米手机',1999);-- 在定义字段属性的时候后面没有写 not null就可以填空值

-- 6.  insert into tab_name (列名..)	values (),(),()	形式添加多条记录
INSERT INTO `goods2`(id,goods_name,price)
	VALUES(2,'榔头手机',2999),(3,'菠萝手机',2199);

-- 7. 如果是给表中的所有字段添加数据,可以不写前面的字段名称
INSERT INTO `goods2`
	VALUES(4,'砖头手机',1999);

-- 8. 默认值的使用,当不给某个字段值时,如果有默认值就会添加默认值,否则报错
	-- 如果某个列 没有指定 not null ,那么当添加数据时,没有给定值,则会默认给 null
	-- 如果我们希望指定某个列的默认值,可以在创建表时指定
INSERT INTO `goods2`(id,goods_name)
	VALUES(5,'椰子手机');
	
SELECT * FROM goods2;

表格如下:

15.3 update 语句

1. 基本语法

2. 基本使用

要求:在上面创建的employee表中修改表中的纪录

  1. 将所有员工薪水修改为5000元。
  2. 将姓名为 张三的员工薪水修改为3000元
  3. 将小明的薪水在原有基础上增加1000元
#演示update语句
-- 1. 将所有员工薪水修改为5000元。
-- [如果没有带where条件,会修改所有的记录,因此用的时候要小心]
UPDATE employee SET salary = 5000
-- 2. 将姓名为 张三的员工薪水修改为3000元
UPDATE employee 
	SET salary = 3000
	WHERE user_name = '张三';
-- 3. 将小明的薪水在原有基础上增加1000元
UPDATE employee 
    SET salary = salary + 1000
	WHERE user_name = '小明';
#查阅
SELECT * FROM employee;

修改后的表格如下:

3. 使用细节

  1. UPDATE语法可以用新值更新原有表行中的各列。
  2. SET子句指示要修改哪些列和要给予哪些值。
  3. WHERE子句指定应更新哪些行。如没有WHERE子句,则更新所有的行(记录),因此操作的时候一定小心。
  4. 如果需要修改多个字段,可以通过 set 字段1=值1,字段2=值2…
  5. 例如下面:
-- 4. 可以修改多个值
UPDATE employee 
	SET salary = salary + 1000,job = '法官'
	WHERE user_name = '小明';

15.4 delete 语句

1. 基本语法

2. 基本使用

  1. 删除表中名称为’小红’的记录。
  2. 删除表中所有记录。
#delete演示
-- 1. 删除表中名称为’小红’的记录。
DELETE FROM employee
	WHERE user_name = '小红';
-- 2. 删除表中所有记录。
DELETE FROM employee

SELECT * FROM employee;

3. 使用细节

  1. 如果不使用where子句,将删除表中所有数据
  2. Delete语句不能删除某一列的值 (可使用update 设为 null 或者’')
-- Delete语句不能删除某一列的值 (可使用update 设为 null 或者'')
UPDATE employee SET job = ''
	WHERE user_name = '小明';
  1. 使用delete语句仅删除记录,不删除表本身。如要删除表,使用drop table语句。drop table 表名
-- 使用delete语句仅删除记录,不删除表本身。如要删除表,使用drop table语句。drop table 表名
DROP TABLE employee;

15.5 select 语句

1. 基本语法

2. 注意事项 (创建测试表学生表 )

  1. Select 指定查询哪些列的数据。
  2. column指定列名。
  3. *号代表查询所有列
  4. From指定查询哪张表
  5. DISTINCT可选,指显示结果时,是否去掉重复数据

3. 基本使用

  1. 查询表中所有学生的信息。
  2. 查询表中所有学生的姓名和对应的英语成绩
  3. 过滤表中重复数据 distinct 。
  4. 要查询的记录,每个字段都相同,才会去重
-- select 语句【重点 难点】
CREATE TABLE student(
	id INT NOT NULL DEFAULT 1,
	`NAME` VARCHAR(20) NOT NULL DEFAULT '', 
	chinese FLOAT NOT NULL DEFAULT 0.0, 
	english FLOAT NOT NULL DEFAULT 0.0,
	math FLOAT NOT NULL DEFAULT 0.0);
	
INSERT INTO student(id,`name`,chinese,english,math)
	VALUES(1,'孙裕松',88,85,92),(2,'张飞',78,66,98),
	(3,'关羽',78,75,82),(4,'刘备',98,86,96),
	(5,'吕布',86,90,86),(6,'赵云',92,88,95);

-- 1. 查询表中所有学生的信息。
SELECT * FROM student;
-- 2. 查询表中所有学生的姓名和对应的英语成绩
SELECT `name`,english FROM student;
-- 3. 过滤表中重复数据 distinct 。
SELECT DISTINCT * FROM student
-- 4. 要查询的记录,每个字段都相同,才会去重
SELECT DISTINCT chinese FROM student

4. 使用表达式对查询的列进行运算

5. 在select 语句中可使用 as 语句

6. 练习 select02.sql

  1. 统计每个学生的总分
  2. 在所有学生总分加10分的情况
  3. 使用别名表示学生分数。
-- select 语句【重点 难点】
-- 1. 统计每个学生的总分
SELECT `name`,(chinese+english+math) FROM student
-- 2. 在所有学生总分加10分的情况
SELECT `name` ,(chinese+english+math+10) FROM student
-- 3. 使用别名表示学生分数。
SELECT `name`,(chinese+english+math+10) AS total_score FROM student

效果如下:

7. 在where 子句中经常使用的运算符

8. 练习题:使用where 子句,进行过滤查询 select03.sql

练习题1:

  1. 查询姓名为赵云的学生成绩
  2. 查询英语成绩大于90分的同学
  3. 查询总分大于200分的所有同学

使用where子句:

  1. 查询math大于60 并且(and) id大于90的学生成绩
  2. 查询英语成绩大于语文成绩的同学
  3. 查询总分大于200分 并且 数学成绩小于语文成绩,的
  4. 姓孙的学生
-- select 语句【重点 难点】
-- 1. 查询姓名为赵云的学生成绩
SELECT * FROM student
	WHERE `name` = '赵云';
-- 2. 查询英语成绩大于90分的同学
SELECT * FROM student
	WHERE english > 90;
-- 3. 查询总分大于200分的所有同学
SELECT * FROM student
	WHERE (chinese+english+math)>200;

-- 使用where子句:
-- 1. 查询math大于60 并且(and) id大于5的学生成绩
SELECT * FROM student
	WHERE math > 90 AND id > 5;
-- 2. 查询英语成绩大于语文成绩的同学
SELECT * FROM student
	WHERE english > chinese;
-- 3. 查询总分大于200分 并且 数学成绩小于语文成绩,的姓刘的学生
--% 表示名字以 刘开头的就可以
SELECT * FROM student
	WHERE (chinese+english+math)>200 AND 
	math < chinese AND `name` LIKE '刘%'

练习题2:

  1. 查询英语分数在 80- 90之间的同学
  2. 查询数学分数为89,90,91的同学。
  3. 查询所有姓李的学生成绩。
  4. 查询数学分>80,语文分>80的同学
-- 练习题2-- 1. 查询英语分数在 80- 90之间的同学
SELECT * FROM student
	WHERE english >= 80 AND english <= 90;
SELECT * FROM student
	WHERE english BETWEEN 80 AND 90;
-- 2. 查询英语分数为89,90,91的同学。
SELECT * FROM student
	WHERE english = 89 OR english = 90 OR english = 91;
SELECT * FROM student
	WHERE english IN (89 , 90 , 91);
-- 3. 查询所有姓李的学生成绩。
SELECT * FROM student
	WHERE `name` LIKE '孙%';
-- 4. 查询数学分>80,语文分>80的同学
SELECT * FROM student
	WHERE math >80 AND chinese > 80;

练习题3:

  1. 查询语文分数在 70 - 80之间的同学
  2. 查询总分为189,190,191的同学
  3. 查询所有姓赵 或者 姓张的学生成绩
  4. 查询数学比语文多30分的同学
-- 练习题3-- 1. 查询语文分数在 70 - 80之间的同学
SELECT * FROM student
	WHERE chinese IN (70,80);
-- 2. 查询总分为189,190,191的同学
SELECT * FROM student
	WHERE (chinese+english+math) IN (189,190,191);

-- 3. 查询所有姓赵 或者 姓张的学生成绩
SELECT * FROM student
	WHERE `name` LIKE '赵%' OR `name` LIKE '张%';
-- 4. 查询数学比语文多30分的同学
SELECT * FROM student
	WHERE (math-chinese) >= 30;

9. 使用order by 子句排序查询结果

  1. 基本语法

  1. 基本介绍

    1. Order by 指定排序的列,排序的列既可以是表中的列名,也可以是select语句后指定的列名。
    2. Asc 升序[默认]、Desc 降序
    3. ORDER BY 子句应位于SELECT语句的结尾
  2. 练习:

    1. 对数学成绩排序后输出[升序]
    2. 对总分按从高到低的顺序输出
    3. 对姓李的学生成绩排序输出(升序)
    -- 演示 order by使用
    -- 1. 对数学成绩排序后输出[升序]
    SELECT * FROM student
    	ORDER BY math;
    -- 2. 对总分按从高到低的顺序输出
    SELECT `name`,(chinese+english+math) AS total_score FROM student
    	ORDER BY total_score DESC;
    -- 3. 对姓张的学生成绩排序输出(升序)
    SELECT `name` ,(chinese + english + math) AS total_score FROM student
    	WHERE `name` LIKE '张%'
    	ORDER BY total_score ASC;
    

16. 函数

16.1 合计/统计函数

1. count

1. 基本语法

2. 情景案例
  1. 统计一个班级共有多少学生?
  2. 统计数学成绩大于90的学生有多少个?
  3. 统计总分大于250的人数有多少?
  4. count(*)和 count(列) 的区别
# 演示 mysql 的统计函数的使用
-- 1. 统计一个班级共有多少学生?
SELECT COUNT(*) FROM student;
-- 2. 统计数学成绩大于90的学生有多少个?
SELECT COUNT(*) FROM student
	WHERE math > 90;
-- 3. 统计总分大于250的人数有多少?
SELECT COUNT(*) FROM student
	WHERE (chinese + english + math) > 250;
-- 4. count(*)count() 的区别
-- count(*) 返回满足条件的记录的行数
-- count():统计满足条件的某列有多少个,但是会排出null

CREATE TABLE t15(
	id VARCHAR(10));
INSERT INTO t15 VALUES ('jack');
INSERT INTO t15 VALUES ('lucy');
INSERT INTO t15 VALUES ('john');
INSERT INTO t15 VALUES ('hebe');
INSERT INTO t15 VALUES (NULL);

SELECT * FROM t15;

SELECT COUNT(*) FROM t15;-- 5
SELECT COUNT(id) FROM t15;-- 4

2. sum

1. 基本语法

Sum函数返回满足where条件的行的和: 一般使用在数值列

2. 情景案例
  1. 统计一个班级数学总成绩?
  2. 统计一个班级语文、英语、数学各科的总成绩
  3. 统计一个班级语文、英语、数学的成绩总和
  4. 统计一个班级语文成绩平均分

注意:sum仅对数值起作用,没有意义

注意:对多列求和,“,”号不能少。

-- 演示 sum的使用
-- 1. 统计一个班级数学总成绩?
SELECT SUM(math) FROM student;
-- 2. 统计一个班级语文、英语、数学各科的总成绩
SELECT SUM(math) AS math_total,SUM(english) AS english_total,
	SUM(chinese) AS chinese_total FROM student;
-- 3. 统计一个班级语文、英语、数学的成绩总和
SELECT SUM(math + english + chinese) AS total_score FROM student;
-- 4. 统计一个班级语文成绩平均分
SELECT SUM(chinese)/COUNT(*) FROM student;

3. avg

1. 基本语法

AVG函数返回满足where条件的一列的平均值

2. 情景案例
  1. 求一个班级数学平均分?
  2. 求一个班级总分平均分
-- 演示avg的使用
-- 1. 求一个班级数学平均分?
SELECT AVG(math) FROM student;
-- 2. 求一个班级总分平均分
SELECT AVG(math + chinese + english) FROM student;

4. max/min

1. 基本语法

Max/min函数返回满足where条件的一列的最大/最小值

2. 情景案例
  1. 求班级最高分和最低分(数值范围在统计中特别有用)
  2. 求班级数学最高分和最低分
-- 演示 max/min 的使用
-- 1. 求班级最高分和最低分(数值范围在统计中特别有用)
SELECT MAX(chinese + math + english) AS total_score_high,
	MIN(chinese + math + english) AS total_score_low FROM student;
-- 2. 求班级数学最高分和最低分
SELECT MAX(math),MIN(math) FROM student;

5. group by 和 having

1. group by 基本语法

使用group by 子句对列进行分组 [先创建测试表]

2. having 基本语法

使用having 子句对分组后的结果进行过滤

3. 情景案例
  1. group by用于对查询的结果分组统计,(示意图)
  2. havin心子句用于限制分组显示结果?
  3. 如何显示每个部门的平均工资和最高工资?
  4. 显示每个部门的每种岗位的平均工资和最低工资?
  5. 显示平均工资低于2000的部门号和它的平均工资 // 别名

先创建要用到的表

CREATE TABLE dept( /*部门表*/
	deptno MEDIUMINT   UNSIGNED  NOT NULL  DEFAULT 0, 
	dname VARCHAR(20)  NOT NULL  DEFAULT "",
	loc VARCHAR(13) NOT NULL DEFAULT ""
	);

INSERT INTO dept VALUES(10, 'ACCOUNTING', 'NEW YORK'),-- 会计 
			(20, 'RESEARCH', 'DALLAS'), -- 研究人员
			(30, 'SALES', 'CHICAGO'), -- 销售员
			(40, 'OPERATIONS', 'BOSTON');-- 运营总监

SELECT * FROM dept;

#创建表EMP雇员
CREATE TABLE emp
(empno  MEDIUMINT UNSIGNED  NOT NULL  DEFAULT 0, /*编号*/
	ename VARCHAR(20) NOT NULL DEFAULT "", /*名字*/
	job VARCHAR(9) NOT NULL DEFAULT "",/*工作*/
	mgr MEDIUMINT UNSIGNED ,/*上级编号*/
	hiredate DATE NOT NULL,/*入职时间*/
	sal DECIMAL(7,2)  NOT NULL,/*薪水*/
	comm DECIMAL(7,2) ,/*红利*/
	deptno MEDIUMINT UNSIGNED NOT NULL DEFAULT 0 /*部门编号*/
	);

 
 INSERT INTO emp VALUES(7369, 'SMITH', 'CLERK', 7902, '1990-12-17', 800.00,NULL , 20), 
	(7499, 'ALLEN', 'SALESMAN', 7698, '1991-2-20', 1600.00, 300.00, 30),  
	(7521, 'WARD', 'SALESMAN', 7698, '1991-2-22', 1250.00, 500.00, 30),  
	(7566, 'JONES', 'MANAGER', 7839, '1991-4-2', 2975.00,NULL,20),  
	(7654, 'MARTIN', 'SALESMAN', 7698, '1991-9-28',1250.00,1400.00,30),  
	(7698, 'BLAKE','MANAGER', 7839,'1991-5-1', 2850.00,NULL,30),  
	(7782, 'CLARK','MANAGER', 7839, '1991-6-9',2450.00,NULL,10),  
	(7788, 'SCOTT','ANALYST',7566, '1997-4-19',3000.00,NULL,20),  
	(7839, 'KING','PRESIDENT',NULL,'1991-11-17',5000.00,NULL,10),  
	(7844, 'TURNER', 'SALESMAN',7698, '1991-9-8', 1500.00, NULL,30),  
	(7900, 'JAMES','CLERK',7698, '1991-12-3',950.00,NULL,30),  
	(7902, 'FORD', 'ANALYST',7566,'1991-12-3',3000.00, NULL,20),  
	(7934,'MILLER','CLERK',7782,'1992-1-23', 1300.00, NULL,10);
SELECT * FROM emp;
	
#工资级别表
CREATE TABLE salgrade
	(
	grade MEDIUMINT UNSIGNED NOT NULL DEFAULT 0,/*工资级别*/
	losal DECIMAL(17,2)  NOT NULL,/*该级别的最低工资*/
	hisal DECIMAL(17,2)  NOT NULL/*该级别的最高工资*/
	);

INSERT INTO salgrade VALUES (1,700,1200);
INSERT INTO salgrade VALUES (2,1201,1400);
INSERT INTO salgrade VALUES (3,1401,2000);
INSERT INTO salgrade VALUES (4,2001,3000);
INSERT INTO salgrade VALUES (5,3001,9999);
	
SELECT * FROM salgrade;

然后演示 group by 和 having 的使用

# 演示 group by + having
-- 1. group by用于对查询的结果分组统计,(示意图)
-- 2. having子句用于限制分组显示结果
-- 3. 如何显示每个部门的平均工资和最高工资?
-- 分析: 使用avg 和 max
-- 按照部门来分组查询
SELECT AVG(sal),MAX(sal),deptno
	FROM emp GROUP BY deptno;
-- 4. 显示每个部门的每种岗位的平均工资和最低工资?
-- 分析:1. 显示每个部分的平均工资和最低工资
--	2. 显示每个部分的每种岗位的平均工资和最低工资
SELECT AVG(sal),MIN(sal),deptno,job
	FROM emp GROUP BY deptno,job;

-- 5. 显示平均工资低于2000的部门号和它的平均工资 // 别名?
-- 思路分析[化繁为简,各个击破]
-- 1. 先显示各部门号的平均工资
SELECT AVG(sal) ,deptno
	FROM emp GROUP BY deptno;
-- 2. 然后在1的结果上进行过滤,保留平均工资低于2000SELECT AVG(sal) ,deptno
	FROM emp GROUP BY deptno
	HAVING AVG(sal) < 2000;
-- 3. 使用别名进行过滤,提高效率
SELECT AVG(sal) AS avg_sal ,deptno
	FROM emp GROUP BY deptno
	HAVING avg_sal < 2000;

16.2 字符串相关函数

1. 常用函数

2. 情景案例

  1. 演示上面的常用字符串函数
  2. 以首字母小写的方式显示所有员工emp表的姓名 str.sql 使用两种方式
-- 演示字符串相关函数的使用 使用emp表来演示
-- CHARSET(str) 返回字串字符集
SELECT CHARSET(ename) FROM emp;

-- CONCAT (string2 [.... ]) 连接字串
SELECT CONCAT (ename ,' job is ' ,job) FROM emp;

-- INSTR (string ,substring ) 返回substring在string中出现的位置,没有返回0
-- dual 亚元表
SELECT INSTR('sys学java','java') FROM DUAL;

-- UCASE (string2 ) 转换成大写
SELECT UCASE(ename) FROM emp;

-- LCASE (string2 ) 转换成小写
SELECT LCASE(ename) FROM emp;

-- LEFT (string2 ,length ) 从string2中的左边起取length个字符
-- RIGHT (string2 ,length ) 从string2中的右边起取length个字符
SELECT LEFT(ename,3) FROM emp;
SELECT RIGHT(ename,3) FROM emp;

-- LENGTH (string ) string长度[按照字节]
SELECT LENGTH('sys学java') FROM emp;

-- REPLACE (str ,search_str ,replace str ) 
-- 在str中用replace str替换search str
SELECT ename , REPLACE(job,'MANAGER','经理') FROM emp;
SELECT * FROM emp;

-- STRCMP (string1 ,string2 ) 逐字符比较两字串大小,
SELECT STRCMP('jack','jadk') FROM DUAL;

-- SUBSTRING (str , position [,length])
-- 从str的position开始[1开始计算],取length个字符
-- 从 ename 列的第一个位置开始,截取3个字符
SELECT SUBSTRING(ename,1,3) FROM emp;

-- LTRIM (string2 ) RTRIM (string2 ) TRIM(string) 
-- 去除前端空格或后端空格
SELECT LTRIM(' sys java ') FROM DUAL;
SELECT RTRIM(' sys java ') FROM DUAL;
SELECT TRIM(' sys java ') FROM DUAL;

-- 以首字母小写的方式显示所有员工emp表的姓名 str.sql 使用两种方式
-- 第一种方式
-- 先截取首字母,转为小写 LCASE( LEFT(ename,1)
-- 然后截取后面的字母 UCASE(SUBSTRING(ename,2))
-- 将两段字母拼接到一起 CONCAT (LCASE( LEFT(ename,1)) , UCASE(SUBSTRING(ename,2)))
SELECT CONCAT (LCASE( LEFT(ename,1)) , UCASE(SUBSTRING(ename,2))) FROM emp;
-- 第二种方式
-- 先截取首字母,转为小写 LCASE( LEFT(ename,1)
-- 然后再截取首字母 LEFT(ename,1)
-- 将这两个字母替换 REPLACE(ename,LEFT(ename,1) ,LCASE( LEFT(ename,1)))
SELECT REPLACE(ename,LEFT(ename,1) ,LCASE( LEFT(ename,1))) FROM emp;

16.3 数学相关函数

1. 常用方法

rand0返回一个随机浮点值 v范围在 0到1 之间(即,其范围为 0 s vs 1.0)。

若已指定一个整数参数 N,则它被用作种子值,用来产生重复序列。

2. 情景案例

  1. 演示上面的数学函数常用方法
-- 演示数学相关函数
-- ABS(num)  绝对值
SELECT ABS(-10) FROM DUAL; -- 10

-- BIN (decimal number )  十进制转二进制
SELECT BIN(10) FROM DUAL; -- 1010

-- CEILING (number2 )  向上取整,得到比num2 大的最小整数
SELECT CEILING(10.2) FROM DUAL; -- 11

-- CONV(number2,from base,to base)  进制转换
SELECT CONV(16,16,10) FROM DUAL; -- 22

-- FLOOR(number2 )  向下取整,得到比 num2 小的最大整数
SELECT FLOOR(11.9) FROM DUAL; -- 11

-- FORMAT (number,decimal_places )  保留小数位数(四舍五入)
SELECT FORMAT(16.23452,3) FROM DUAL; -- 16.235

-- HEX (DecimalNumber )  转十六进制
SELECT HEX(10) FROM DUAL; -- A

-- LEAST (number , number2 [,..])  求最小值
SELECT LEAST(1,2,5,3,2,1) FROM DUAL; -- 1

-- MOD (numerator ,denominator )  求余
SELECT MOD(12,5) FROM DUAL; -- 2

-- RAND([seed])  RAND([seed])其范围为0≤v≤1.0
-- 1. 如果使用 rand() 每次返回不同的随机数 ,在 0 ≤ v ≤ 1.0
-- 2. 如果使用 rand(seed) 返回随机数, 范围 0 ≤ v ≤ 1.0, 如果 seed 不变,
--	该随机数也不变了
SELECT RAND() FROM DUAL; 

SELECT RAND(5) FROM DUAL;

16.4 时间日期相关函数 date.sql

1. 常用方法

2. 情景案例

第一部分

第二部分

查询

  1. 显示所有留言信息,发布日期只显示 日期,不用显示时间.
  2. 请查询在10分钟内发布的帖子
  3. 请在mysql 的sql语句中求出 2011-11-11 和1990-1-1 相差多少天
  4. 请用mysql 的sql语句求出你活了多少天?[练习]中
  5. 如果你能活80岁,求出你还能活多少天.[练习]

细节说明:

  1. DATE ADD() 中的 interval 后面可以是 year minute second day 等
  2. DATE SUB() 中的 interval 后面可以是 year minute second hour day 等
  3. DATEDIFF(date1,date2) 得到的是天数,而且是date1-date2 的天数,因此可以取负数
  4. 这四个函数的日期类型可以是 date,datetime 或者 timestamp
第三部分

在实际开发中,我们也经常使用int来保存一个unix时间截

然后使用 from unixtime( 进行转换,还是非常有实用价值的

综合演示
-- 日期时间相关函数
-- CURRENT_DATE ( ) 当前日期
SELECT CURRENT_DATE() FROM DUAL;
-- CURRENT_TIME ( ) 当前时间
SELECT CURRENT_TIME() FROM DUAL;
-- CURRENT_TIMESTAMP ( ) 当前时间戳
SELECT CURRENT_TIMESTAMP() FROM DUAL;

-- 创建测试表 信息表
CREATE TABLE mes(
	id INT , 
	content VARCHAR(30), 
	sendtime DATETIME);
-- 添加一条记录
INSERT INTO mes
	VALUES(1,'北京时间',CURRENT_TIMESTAMP);
INSERT INTO mes
	VALUES(2,'今日快报',NOW());
INSERT INTO mes
	VALUES(3,'叶子快报',NOW());

-- 查询
SELECT * FROM mes;


-- DATE (datetime )  返回datetime的日期部分
-- DATE_ADD (date2, INTERVAL d_value d_type )  在date2中加上日期或时间
-- DATE_SUB (date2 , INTERVAL d_value d_type )  在date2上减去一个时间
-- DATEDIFF (date1 ,date2 )  两个日期差(结果是天)

-- 应用实例	
-- 显示所有信息,发布日期只显示日期,不用显示时间.
SELECT id,content,DATE(sendtime) 
	FROM mes;
-- 请查询在10分钟内发布的帖子
-- 用 date_add
SELECT * 
	FROM mes
	WHERE DATE_ADD(sendtime,INTERVAL 10 MINUTE) >= NOW();
-- 用 date_sub
SELECT *
	FROM mes
	WHERE DATE_SUB(NOW(),INTERVAL 10 MINUTE) <= sendtime;
	
-- 请在mysql 的sql语句中求出 2011-11-111990-1-1 相差多少天
SELECT DATEDIFF('2011-11-11','1990-01-01') FROM DUAL; -- 7984

-- 请用mysql 的sql语句中求出你活了多少天?[练习]
SELECT DATEDIFF(NOW(),'1998-11-26') FROM DUAL; -- 10783

-- 如果你能活80岁,求出你还能活多少天.[练习]
-- 思路:
-- 先算出活到80岁是到哪一年  DATE_ADD('1993-11-26',INTERVAL 80 YEAR)
-- 然后算出80岁哪一年到现在的时间差 
-- DATEDIFF( DATE_ADD('1998-11-26',INTERVAL 80 YEAR),NOW())
SELECT DATEDIFF( DATE_ADD('1998-11-26',INTERVAL 80 YEAR),NOW()) FROM DUAL;

-- 细节注意:
-- INTERVAL 80 YEAR : YEAR 可以是 年月日,时分秒
-- '1986-11-11' 可以 date,datetime timestamp

-- TIMEDIFF(datel,date2)  两个时间差(多少小时多少分钟多少秒)
SELECT TIMEDIFF('11:11:11','08:08:08') FROM DUAL;-- 03:03:03

-- YEAR | Month | DAYI DATE (datetime)  年月日
SELECT YEAR(NOW()) FROM DUAL; -- 只显示年
SELECT MONTH(NOW()) FROM DUAL; -- 只显示月
SELECT DAY(NOW()) FROM DUAL; -- 只显示日
SELECT YEAR('2023-6-5') FROM DUAL; -- 只显示年

-- unix_timestamp(); 返回的是1970-1-1 到现在的秒数
SELECT UNIX_TIMESTAMP() FROM DUAL;

-- FROM_UNIXTIME(); 可以把一个 unix_timestamp 的秒数,转换成指定格式的日期
-- %Y-%m-%d 格式表示 年--日 ,这是规定好的
-- Y-%m-%d %H:%i:%s 格式表示 年--日 时::秒,这也是规定好的
-- 意义:在开发中可以存放一个整数,然后表示时间,通过FROM_UNIXTIME 转换
SELECT FROM_UNIXTIME(1561615656,'%Y-%m-%d') FROM DUAL;
SELECT FROM_UNIXTIME(1561615656,'%Y-%m-%d %H:%i:%s') FROM DUAL;

-- 在实际开发中,我们也经常使用int来保存一个unix时间截
-- 然后使用 from unixtime( )进行转换,还是非常有实用价值的

16.5 加密和系统函数 pwd.sql

1. 常用方法

2. 情景案例

简单演示一下用法

-- 演示加密函数和系统函数
-- USER()	查询用户
-- 可以查看登录到 mysql 的有哪些用户,以及登录的 IP 
SELECT USER() FROM DUAL; -- 用户@IP 地址

-- DATABASE() 查询当前使用数据库名称 
SELECT DATABASE();

-- MD5(str) 为字符串算出一个 MD5 32 的字符串,常用(用户密码)加密
-- root 密码是 hsp -> 加密 md5 -> 在数据库中存放的是加密后的密码
SELECT MD5('sys') FROM DUAL; -- 36bcbb801f5052739af8220c6ea51434
-- 计算用 MD5 加密后的密码有多少位
SELECT LENGTH(MD5('sys')) FROM DUAL; -- 32

-- 演示用户表 ,存放密码时,是md5
CREATE TABLE sys_user
	(id INT ,
	`name` VARCHAR(32) NOT NULL DEFAULT '', 
	pwd CHAR(32) NOT NULL DEFAULT '');
	
INSERT INTO sys_user
	VALUES(1,'孙裕松',MD5('sys'));
	
SELECT * FROM sys_user;

-- 当这样加密后,根据对应的名字和密码查是查不到的
-- 查出来的是一张空表
SELECT * FROM sys_user
	WHERE `name` = '孙裕松' AND pwd = 'sys';
-- 密码要加上 md5 才能查询到
SELECT * FROM sys_user		-- 涉及到SQL注入问题
	WHERE `name` = '孙裕松' AND pwd = MD5('sys');

-- PASSWORD(str) -- 加密函数, 
-- MySQL 数据库的用户密码就是 PASSWORD 函数加密
SELECT PASSWORD('sys') FROM DUAL; -- 数据库的 *BE353D0D7826681F8B7C136ED9824915F5B99E7D

-- select * from mysql.user \G	从原文密码 str 计算并返回密码字符串
-- 通常用于对 mysql 数据库的用户密码加密
-- mysql.user 表示 数据库.表
SELECT * FROM mysql.user;

16.6 流程控制函数

1. 引出

先看如下需求

  1. 查询emp 表, 如果 comm 是null,则显示0.0
  2. 如果emp 表的 job 是 CLERK 则显示 职员,
  3. 如果是 MANAGER 则显示经理
  4. 如果是 SALESMAN 则显示 销售人员,其它正常显示
  5. 这时候我们就可以使用流程控制函数来解决

2. 常用方法

2. 情景案例

流程控制演示及上述问题的解决:

-- 演示流程控制函数
-- IF(expr1,expr2,expr3) 如果 expr1 为 True ,则返回 expr2 否则返回 expr3 
SELECT IF(TRUE,'上海','深圳') FROM DUAL;

-- IFNULL(expr1,expr2) 如果 expr1 不为空 NULL,则返回 expr1,否则返回 expr2
SELECT IFNULL(NULL,'sys学java') FROM DUAL;

-- SELECT CASE WHEN expr1 THEN expr2 WHEN expr3 THEN expr4 ELSE expr5 END; [类似多重分支.] 
-- 如果 expr1 为 TRUE,则返回 expr2,如果 expr3 为 t, 返回 expr4, 否则返回 expr5
SELECT CASE 
	WHEN TRUE THEN 'lucy'
	WHEN FALSE THEN 'tom'
	ELSE 'jack' END;

-- 1. 查询emp 表, 如果 comm 是null,则显示0.0
-- 正常查询
SELECT ename,comm
	FROM emp;
--null 显示为 0.0 ;
-- 方法一:
-- 判断是否为 null 要使用 is null, 判断不为空 使用 is not 
SELECT ename,IF(comm IS NULL,0.0,comm)
	FROM emp;
-- 方法二:
SELECT ename,IFNULL(comm,0.0)
	FROM emp;

-- 2. 如果emp 表的 job 是 CLERK 则显示 职员,
-- 3. 如果是 MANAGER 则显示经理
-- 4. 如果是 SALESMAN 则显示 销售人员,其它正常显示
SELECT ename,(CASE job 
	WHEN 'CLERK' THEN '职员'
	WHEN 'MANAGER' THEN '经理'
	WHEN 'SALESMAN' THEN '销售人员'
	ELSE job END) AS job
	FROM emp;

17. mysql 表查询–加强

17.1 介绍

前面我们讲的mysql表的基本查询都是对一张表进行的查询,但是在实际的软件开发中,还远远的不够。

下面将使用前面创建 三张表 (emp,dept,salgrade) 为大家演示如何进行多表查询

17.2 情景案例

  • 使用where子句

​ 如何查找1992.1.1后入职的员工?

  • 如何使用like操作符

​ %: 表示到多个字符_: 表示单个字符

​ 如何显示首字符为S的员工姓名和工资?

​ 如何显示第三个字符为大写O的所有员工的姓名和工资?

  • 如何显示没有上级的雇员的情况

  • 查询表结构 selectinc.sql

  • 使用order by子句

​ 如何按照工资的从低到高的顺序,显示雇员的信息?

​ 按照部门号升序而雇员的工资降序排列,显示雇员信息?

  • 代码演示如下:
-- 查询增强
-- 使用where子句
-- 	如何查找1992.1.1后入职的员工?
-- 说明:在mysql中,日期类型可以直接比较,
-- 需要注意时间格式和表格中的格式一样
SELECT * FROM emp
	WHERE hiredate > '1992-01-01';

-- 如何使用like操作符
-- 	%: 表示0到多个字符 _: 表示单个任意字符
-- 	如何显示首字符为S的员工姓名和工资?
SELECT ename,sal FROM emp
	WHERE ename LIKE 'S%'
	
-- 	如何显示第三个字符为大写O的所有员工的姓名和工资?
SELECT ename,sal FROM emp
	WHERE ename LIKE '__O%'

-- 如何显示没有上级的雇员的情况  mgl就是上级
SELECT * FROM emp
	WHERE mgr IS NULL

-- 查询表结构
DESC emp

-- 使用order by子句
-- 	如何按照工资的从低到高的顺序,显示雇员的信息?
SELECT * FROM emp
	ORDER BY sal ASC
	
-- 	按照部门号升序而雇员的工资降序排列,显示雇员信息?
SELECT * FROM emp
	ORDER BY deptno ASC,sal DESC

17.3 分页查询

  1. 按雇员的id号升序取出,每页显示3条记录,请分别显示 第1页,第2页,第3页

  2. 基本语法: select … limit start, rows

    表示从start+1 行开始取,取出rows行,start 从0开始计算

  3. 公式:

  1. 练习

    1. 按雇员的empno号降序取出,每页显示5条记录。
    2. 请分别显示 第3页,第5页 对应的sql语句
    3. 第5页 : select * from emp order by empno desc limit 20, 5
  2. 演示代码如下:

-- 演示分页查询
-- 1. 按雇员的id号升序取出,每页显示3条记录,请分别显示 第1页,第2页,第3-- 2. 基本语法: select ... limit start, rows
--    表示从start+1 行开始取,取出rows行,start 从0开始计算
-- 第一页
SELECT * FROM emp
	ORDER BY empno ASC
	LIMIT 0,3
-- 第二页
SELECT * FROM emp
	ORDER BY empno ASC
	LIMIT 3,3
-- 第三页
SELECT * FROM emp
	ORDER BY empno ASC
	LIMIT 6,3
	
-- 推导一个公式
SELECT * FROM emp
	ORDER BY empno ASC
	LIMIT 每页显示的记录数*(第几页-1),每页显示的记录数

-- 1. 按雇员的empno号降序取出,每页显示5条记录。
SELECT * FROM emp
	ORDER BY empno DESC
	LIMIT 0,5
SELECT * FROM emp
	ORDER BY empno DESC
	LIMIT 5,5
SELECT * FROM emp
	ORDER BY empno DESC
	LIMIT 10,5
-- 2. 请分别显示 第3页,第5页 对应的sql语句
-- 第三页
SELECT * FROM emp
	ORDER BY empno DESC
	LIMIT 10,5
-- 第五页
SELECT * FROM emp
	ORDER BY empno DESC
	LIMIT 20,5

17.4 使用分组函数和分组子句 group by

  1. 显示每种岗位的雇员总数、平均工资
  2. 显示雇员总数,以及获得补助的雇员数
  3. 显示管理者的总人数
  4. 显示雇员工资的最大差额
  5. 代码演示:
-- 增强 group by 的使用
-- 1. 显示每种岗位的雇员总数、平均工资
SELECT COUNT(*),AVG(sal) ,job
	FROM emp
	GROUP BY job
	
-- 2. 显示雇员总数,以及获得补助的雇员数
-- 获得补助的雇员数 就是 comm 列为非null
SELECT COUNT(*),COUNT(comm)
	FROM emp;
-- 拓展:统计没有获得补助的雇员数
-- 写法一:
SELECT COUNT(*),COUNT(IF(comm IS NULL,1,NULL)) 
	FROM emp;
-- 写法二:
SELECT COUNT(*),COUNT(*)-COUNT(comm)
	FROM emp	

-- 3. 显示管理者的总人数 也就是 mgr 里面的人
-- DISTINCT可选,指显示结果时,是否去掉重复数据
SELECT COUNT(DISTINCT mgr)
	FROM emp;

-- 4. 显示雇员工资的最大差额
SELECT MAX(sal)-MIN(sal) 
	FROM emp;

17.5 数据分组的总结

1. 总结

如果select语句同时包含有group by ,having,limit , order by 那么他们的顺序是group by , having,order by , limit

2. 情景案例

  1. 请统计各个部门的平均工资,
  2. 并且是大于1000的
  3. 并且按照平均工资从高到低排序,
  4. 取出前两行记录.
-- 1. 请统计各个部门的平均工资,
-- 2. 并且是大于1000-- 3. 并且按照平均工资从高到低排序,
-- 4. 取出前两行记录.

-- 分析 各个部门 用到group by
-- 平均工资 用到avg
-- 大于1000 用到 having
-- 从高到低排序 用到 order by
-- 取出前两行记录 用到 limit

SELECT deptno,AVG(sal) AS avg_sal
	FROM emp
	GROUP BY deptno
	HAVING avg_sal > 1000
	ORDER BY avg_sal DESC
	LIMIT 0,2

18. mysql 多表查询

18.1 问题的引出(重点,难点)

如上图所示,应该有一张商品表,有一张评论表;

从而引出了多表查询;

18.2 说明

  1. 多表查询是指基于两个和两个以上的表查询.
  2. 在实际应用中,查询单个表可能不能满足你的需求,
  3. 从而需要使用到(dept表和emp表)

18.3 练习

  1. 显示雇员名,雇员工资及所在部门的名字? [笛卡尔集]
  2. 小技巧:多表查询的条件不能少于 表的个数-1,否则会出现笛卡尔集
  3. 如何显示部门号为10的部门名、员工名和工资?
  4. 显示各个员工的姓名,工资,及其工资的级别?
  5. 显示雇员名,雇员工资及所在部门的名字并按部门排序[降序排].

第一题初步分析:

  1. 雇员名,雇员工资 来自 emp 表
  2. 部门的名字 来自 dept 表
  3. 我们先单独查询emp表,发现为13行8列
SELECT *
	FROM emp;

  1. 我们单独查询dept表,发现为4行三列
SELECT *
	FROM dept;

  1. 我们现在尝试两个表一起查,发现为52行11列
SELECT *
	FROM emp,dept;

  1. 由此我们发现当两个表查询时的规则为:

    1. 从第一张表中取出一行和第二张表的每一行进行组合,然后返回结果[含有两张表的所有列]
    2. 返回的行数就是第一张表的行数*第二张表的行数
    3. 这样多表查询默认处理返回的结果就是笛卡尔集
    4. 但是这样的话会有很多重复的信息,因此我们要对其进行过滤,通过where方法
    SELECT ename,sal,dname,emp.deptno
    	FROM emp,dept
    	WHERE emp.deptno = dept.deptno 
    
  2. 因此可以看出剩下的题目也是如此解决

  3. 全部代码如下:

-- 多表查询
-- 1. 显示雇员名,雇员工资及所在部门的名字? [笛卡尔集]
-- 分析:
-- (1)雇员名,雇员工资 来自 emp 表
-- (2)部门的名字 来自 dept 表
-- (3)需求对 emp 和 dept 查询 ename,sal,dname,deptno
-- (4)当我们需要指定显示某个表的列是 表.列名
-- 查询emp表
SELECT *
	FROM emp;
-- 查询dept表
SELECT *
	FROM dept;
-- 尝试两个表一起查询
SELECT *
	FROM emp,dept;
-- 将两个表合并的结果进行过滤
SELECT ename,sal,dname,emp.deptno
	FROM emp,dept
	WHERE emp.deptno = dept.deptno 

-- 2. 小技巧:多表查询的条件不能少于 表的个数-1,否则会出现笛卡尔集
-- 3. 如何显示部门号为10的部门名、员工名和工资?
SELECT dname,sal,emp.deptno
	FROM emp,dept
	WHERE emp.deptno = dept.deptno AND emp.deptno = 10
	
-- 4. 显示各个员工的姓名,工资,及其工资的级别?
-- 查询工资等级
SELECT * FROM salgrade;
-- 用 WHERE sal BETWEEN losal AND hisal 来过滤
SELECT ename,sal,grade
	FROM emp,salgrade
	WHERE sal BETWEEN losal AND hisal
	
-- 5. 显示雇员名,雇员工资及所在部门的名字并按部门排序[降序排].
SELECT ename,sal,dname,emp.deptno
	FROM emp,dept
	WHERE emp.deptno = dept.deptno
	ORDER BY dept.deptno DESC

18.4 自连接

  1. 介绍

    自连接是指在同一张表的连接查询 [将同一张表看做两张表]

  2. 思考题: 显示公司员工和他的上级的名字

    • 第一步,先查询总表看一下
      • 发现员工名字 在emp ,上级的名字 也在emp
      • 员工和上级是通过 emp的mgr列关联

    • 现在我们把这张表看成两张表,给工人弄个别名 woker ,上级弄个别名为 boss
    -- 我们现在给工人弄个别名 woker ,上级弄个别名为 boss
    SELECT * 
    	FROM emp worker,emp boss -- 16913*13
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VodvvWMo-1686661207674)(E:\Java study\Markdown\本地图片\当两张表看.png)]

    • 但是这样显示的信息太多,我们只需要员工跟他上级的名字即可
    • 然后再将重复的过滤掉 当员工上级的编号 = 上级的编号
    • 这样就得到最终我们需要的表
    SELECT worker.ename AS '职员',boss.ename AS '上级'
    	FROM emp worker,emp boss
    	WHERE worker.mgr = boss.empno;-- 12行
    

  3. 完整的代码如下:

    -- 多表查询的 自连接
    -- 思考题: 显示公司员工和他的上级的名字
    -- 先查询总表
    SELECT * FROM emp;
    -- 发现员工名字 在emp ,上级的名字 也在emp
    -- 员工和上级是通过 emp的mgr列关联
        
    -- 我们现在给工人弄个别名 woker ,上级弄个别名为 boss
    SELECT * 
    	FROM emp worker,emp boss -- 16913*13
    
    -- 先筛选出员工的名字和上级的名字
    -- 然后经过where过滤掉重复的
    -- 当员工上级的编号 = 上级的编号
    SELECT worker.ename AS '职员',boss.ename AS '上级'
    	FROM emp worker,emp boss
    	WHERE worker.mgr = boss.empno;-- 12-- 自连接的特点
    --  1.把同一张表当做两张表来使用
    --  2.需要给表取别名,要不然会显示不清晰
    --		别名格式:表名 表别名
    -- 3. 列名不明确的话,可以指定列的别名
    -- 		列的别名: 列名 as 列的别名
    

19. mysql 表子查询

19.1 什么是子查询 subquery.sql

子查询是指嵌入在其它 sql 语句中的 select 语句,也叫嵌套查询

19.2 单行子查询

单行子查询是指只返回一行数据的子查询语句

19.3 子查询演示:

请思考:如何显示与SMITH 同一部门的所有员工?

-- 演示子查询
-- 请思考:如何显示与SMITH 同一部门的所有员工?
/*
	1. 先查询到 SMITH 的部门号
	2. 然后把查询 SMITH 部门号的语句当成一个子查询
	
*/
SELECT deptno
	FROM emp
	WHERE ename = 'SMITH'
	
SELECT *
	FROM emp
	WHERE deptno = (
		SELECT deptno
		FROM emp
		WHERE ename = 'SMITH'
	)

19.4 多行子查询

多行子查询指返回多行数据的子查询 使用关键字 in

练习:

如何查询和部门10的工作相同的雇员的名字、岗位、工资、部门号,但是不含10号部门自己的.

思路:

  1. 先查询到10号部门有哪些工作
-- 工作可能会有重叠的,用 distinct去掉重叠的信息
SELECT DISTINCT job
	FROM emp
	WHERE deptno = 10

查询结果如下:

  1. 把上面的10号部门的job作为多行子查询
-- 2.把上面的10号部门的job作为多行子查询
SELECT ename,job,sal,deptno
	FROM emp
	WHERE job IN (
		SELECT DISTINCT job
		FROM emp
		WHERE deptno = 10
	) AND deptno != 10

查询结果如下:

19.5 子查询当做临时表使用

练习题 subquery.sql

查询ecshop中各个类别中,价格最高的商品

思路:

第一步:

  • 查询商品表
SELECT * FROM ecs_goods

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IuBYVQqp-1686661207675)(E:\Java study\Markdown\本地图片\ecs_goods原表.png)]

第二步:

  • 由于原表信息太多,很多我们用不到
  • 我们先筛选出我们大概需要的几个信息
  • 商品id , 商品类别 ,商品名字 , 商品价格
SELECT goods_id,cat_id,goods_name,shop_price
	FROM ecs_goods;

第三步:

  • 然后查询各个类别中价格最高的几个
SELECT cat_id,MAX(shop_price)
	FROM ecs_goods
	GROUP BY cat_id

然后我们将第三步的子查询表先单独当做一张表,用于跟第二步的表格进行对比查找

第四步:

  • 我们将上面几步合并到一起
SELECT goods_id,ecs_goods.cat_id,goods_name,shop_price
	FROM (
		SELECT cat_id,MAX(shop_price) AS max_price
		FROM ecs_goods
		GROUP BY cat_id
	)temp,ecs_goods
	WHERE temp.cat_id = ecs_goods.cat_id
	AND temp.max_price = ecs_goods.shop_price;

我们最终需要的查询结果如下:

19.6 在多行子查询中使用all 操作符

请思考:显示工资比部门30的所有员工的工资高的员工的姓名、工资和部门号

-- 请思考:显示工资比部门30的所有员工的工资高的员工的姓名、工资和部门号
SELECT ename,sal,deptno
	FROM emp
	WHERE sal > ALL(
		SELECT sal
			FROM emp
			WHERE deptno = 30)
-- 还有一种写法
SELECT ename,sal,deptno
	FROM emp
	WHERE sal > ALL(
		SELECT MAX(sal)
		FROM emp
		WHERE deptno = 30)

19.7 在多行子查询中使用any 操作符

请思考:如何显示工资比部门30的其中一个员工的工资高的员工的姓名、工资和部门号

-- 请思考:如何显示工资比部门30的其中一个员工的工资高的员工的姓名、工资和部门号
SELECT ename,sal,deptno
	FROM emp
	WHERE sal > ANY(
		SELECT sal
			FROM emp
			WHERE deptno = 30)
		
-- 另一种写法
SELECT ename,sal,deptno
	FROM emp
	WHERE sal > ANY(
		SELECT MIN(sal)
			FROM emp
			WHERE deptno = 30)

19.8 多列子查询 manycolumn.sql

多列子查序则是指查询返回多个列数据的子查询语句

情景案例1

请思考:如何查询与allen的部门和岗位完全相同的所有雇员(并且不含allen本人)

(字段1,字段2 …) = (select 字段 1,字段2 from 。。。。)

思路:

  • 先查询Allen的信息
-- 先查询Allen的信息
SELECT deptno,job
	FROM emp
	WHERE ename = 'ALLEN'

  • 然后查询与Allen部门和岗位完全相同的所有雇员(并且排除Allen本人)
-- 然后查询与Allen部门和岗位完全相同的所有雇员
-- 并且排除Allen本人
SELECT *
	FROM emp
	WHERE (deptno,job)=(
		SELECT deptno,job
			FROM emp
			WHERE ename = 'ALLEN'
		)AND ename!='ALLEN'

情景案例2

和宋江数学,英语,语文成绩 完全相同的学生

与上面的写法类似

-- 和赵云数学,英语,语文成绩 完全相同的学生
SELECT *
	FROM student
	WHERE (math,english,chinese)=(
		SELECT math,english,chinese
			FROM student
			WHERE `name` = '赵云')

19.9 在from 子句中使用子查询

请思考: 查找每个部门工资高于本部门平均工资的人的资料

思路:

  1. 先求出每个部门的平均工资
SELECT deptno,AVG(sal) AS avg_sal
	FROM emp
	GROUP BY deptno
  1. 然后将平均工资作为子查询,求出我们需要的信息
SELECT ename,temp.avg_sal,sal,emp.deptno
	FROM (
		SELECT deptno,AVG(sal) AS avg_sal
		FROM emp
		GROUP BY deptno)temp,emp
	WHERE emp.deptno = temp.deptno 
	AND emp.sal > temp.avg_sal

查询每个部门的信息(包括: 部门名,编号,地址)和人员数量,

思路

  1. 先求出每个部门的最高工资
SELECT deptno,MAX(sal)AS max_sal
	FROM emp
	GROUP BY deptno
  1. 然后将最高工资作为子查询,求出我们需要的信息
SELECT ename,temp.max_sal,sal,emp.deptno
	FROM (SELECT deptno,MAX(sal)AS max_sal
	FROM emp
	GROUP BY deptno)temp,emp
	WHERE emp.deptno = temp.deptno
	AND emp.sal = temp.max_sal

19.10 在from 子句中使用子查询—课堂小练习

查询每个部门的信息(包括: 部门名,编号,地址)和人员数量。

思路:

  1. 先计算各个部门的人员数量 构建临时表
SELECT COUNT(*),deptno
	FROM emp
	GROUP BY deptno
  1. 然后查询每个部门的信息和人员数量
SELECT dname,dept.deptno,loc,tmp.per_num
	FROM dept,(
		SELECT COUNT(*)AS per_num,deptno
			FROM emp
			GROUP BY deptno
			)tmp
	WHERE dept.deptno = tmp.deptno
  1. 第2步还有一种写法:
    1. 表.*表示将该表的所有列都显示出来,可以简化sql语句
    2. 在多表查询中,当多个表的列不重复时,才可以直接写列名
SELECT tmp.*,dname,loc
	FROM dept,(
		SELECT COUNT(*)AS per_num,deptno
			FROM emp
			GROUP BY deptno
			)tmp
	WHERE dept.deptno = tmp.deptno

20. 表复制

20.1 自我复制数据(蠕虫复制)

  1. 有时,为了对某个sql语句进行效率测试,我们需要海量数据时,可以使用此法为表创建海量数据。

  2. 简单代码操作:

-- 表的复制
-- 为了对某个sql语句进行效率测试,我们需要海量数据时,可以使用此法为表创建海量数据。 
-- 先创建一个表格
CREATE TABLE my_table01
	( id INT,
	`name` VARCHAR(32),
	sal DOUBLE,
	job VARCHAR(32),
	deptno INT)
DESC my_table01;
SELECT * FROM my_table01;

-- 演示如何自我复制
-- 1. 先把 emp 表记录复制到 my_table01
INSERT INTO my_table01
	(id,`name`,sal,job,deptno)
	SELECT empno,ename,sal,job,deptno FROM emp

-- 2. 自我复制
INSERT INTO my_table01
	SELECT * FROM my_table01;
	
-- 查询一下
SELECT COUNT(*) FROM my_table01;

20.2 情景案例

  1. 思考题:如何删除掉一张表重复记录

  2. 思路分析:

    1. 先创建一张表 my_table02

      这个语句 把emp表的结构(列),复制到my_table02

      CREATE TABLE my_table02 LIKE emp;
      
    2. 让 my_table02 有重复记录

      INSERT INTO my_table02
      	SELECT * FROM emp;
      
    3. 考虑去重 my_table02 的记录(这里用的是最原始的步骤,实际上可以不用这么麻烦).

    4. 思路:

      (1)先创建一张临时表 my_tmp , 该表的结构和 my_tab02 一样

      (2)把 my_tmp 的记录 通过 distinct 关键字 处理后 把记录复制到 my_tmp

      (3)清除掉 my_tab02 记录

      (4)把 my_tmp 表的记录复制到 my_tab02

      (5)drop 掉 临时表 my_tmp

      代码如下:

      -- (1)先创建一张临时表 my_tmp , 该表的结构和 my_tab02 一样
      CREATE TABLE my_tmp LIKE my_table02;
      
      -- (2)把 my_tmp 的记录 通过 distinct 关键字 处理后 把记录复制到 my_tmp
      INSERT INTO my_tmp 
      	SELECT DISTINCT * FROM my_table02
      
      -- (3)清除掉 my_tab02 记录
      DELETE FROM my_table02
      
      -- (4)把 my_tmp 表的记录复制到 my_tab02
      INSERT INTO my_table02
      	SELECT * FROM my_tmp
      
      -- (5)drop 掉 临时表 my_tmp
      DROP TABLE my_tmp
      

21. 合并查询

21.1 介绍

有时在实际应用中,为了合并多个select语句的结果,可以使用集合操作符号union , union all

  1. 比如下面两个查询语句,我们现在要将其合并
select ename,sal,job from emp where sal>2500 union
select ename,sal,job from emp where job='MANAGER
  1. 那么就可以使用union 或者 union all
  2. union all 就是将两个查询结果合并,不会去重
  3. union 就是将两个查询结果合并,会去重
SELECT ename,sal,job FROM emp WHERE sal>2500
SELECT ename,sal,job FROM emp WHERE job='MANAGER'

-- 现在我们将上面两个查询合并,有两种方法
-- union all 就是将两个查询结果合并,不会去重
SELECT ename,sal,job FROM emp WHERE sal>2500
UNION ALL
SELECT ename,sal,job FROM emp WHERE job='MANAGER'

-- union 就是将两个查询结果合并,会去重
SELECT ename,sal,job FROM emp WHERE sal>2500
UNION
SELECT ename,sal,job FROM emp WHERE job='MANAGER'

22. mysql 表外连接

22.1 提出一个问题

  1. 前面用到的查询,是利用 where 子句对两张表或者多张表,形成的笛卡尔积进行筛选,根据关联条件,显示所有匹配的记录,匹配不上的,不显示
  2. 比如: 列出部门名称和这些部门的员工名称和工作,同时要求 显示出那些没有员工的部门。
  3. 使用我们学习过的多表查询的SQL,看不到没有员工的部门,这时候,我们可以用到 outer.sql -> 外连接
-- 比如:列出部门名称和这些部门的员工名称和工作,
-- 同时要求 显示出那些没有员工的部门。
SELECT dname,ename,job
	FROM emp,dept
	WHERE emp.deptno = dept.deptno
-- 以上操作只能显示第一个要求,
-- 但是显示不了没有员工的部门
-- 我们使用外连接
SELECT dname,ename,job
	FROM dept LEFT JOIN emp
	ON emp.deptno = dept.deptno
-- 这样我们就能同时满足两个要求了

22.2 外连接

  1. 左外连接:(如果左侧的表完全显示我们就说是左外连接)
  2. 右外连接:(如果右侧的表完全显示我们就说是右外连接)
  3. 我们举例说明下面两个表

思路:

  1. 先创建这两张表
-- 创建 stu
/*
id	name
1	Jack
2	Tom
3	Kity
4	nono
*/
CREATE TABLE stu
	(id INT,
	`name` VARCHAR(32) 
	)
INSERT INTO stu
	VALUES(1,'jack'),(2,'tom'),
	(3,'kity'),(4,'nono');
SELECT * FROM stu
DELETE FROM stu
	
-- 创建 exam
/*
id	grade
1	56
2	76
11	8
*/

CREATE TABLE exam
	(id INT,
	grade INT)
INSERT INTO exam
	VALUES(1,56),(2,76),
	(11,8);
SELECT * FROM exam
  1. 使用左外连接 (显示所有人的成绩,如果没有成绩,也要显示该人的姓名和id号,成绩显示为空)

    select … from 表1 left join 表2 on条件 [表1:就是左表 表2: 就是右表]

-- 使用左外连接 (显示所有人的成绩,如果没有成绩,也要显示该人的姓名和id号,成绩显示为空)
SELECT stu.id,`name`,grade
	FROM stu LEFT JOIN exam
	ON stu.id = exam.id
  1. 使用右外连接(显示所有成绩如果没有名字匹配,显示空)

    select … from 表1 right join 表2 on 条件 [表1: 就是左表 表2:就是右表]

-- 使用右外连接(显示所有成绩如果没有名字匹配,显示空)
SELECT stu.id,`name`,grade
	FROM exam RIGHT JOIN stu
	ON stu.id = exam.id	
  1. 两个显示结果相同

23. mysql 约束

23.1 基本介绍

约束用于确保数据库的数据满足特定的商业规则。

在mysql中,约束包括: not null、uniqueprimary key,foreign key,和check 五种

23.2 primary key(主键)-基本使用

用于唯一的标示表行的数据,当定义主键约束后,该列不能重复

案例演示:

-- 主键使用 primary key
CREATE TABLE t16(
	id INT PRIMARY KEY,
	`name` VARCHAR(32),
	sex VARCHAR(6))
	
-- -- 主键列的值是不可以重复
INSERT INTO t16
	VALUES(1,'jack','man')
INSERT INTO t16
	VALUES(2,'lucy','woman')
-- id与第一条重复,添加失败
INSERT INTO t16
	VALUES(1,'mike','man') 

primary key(主键)-细节说明

  1. primary key不能重复而且不能为null。
  2. 一张表最多只能有一个主键,但可以是复合主键
  3. 主键的指定方式有两种
    1. 直接在字段名后指定:字段名 primakry key
    2. 在表定义最后写 primary key(列名);
  4. 使用desc 表名,可以看到primary key的情况
  5. 在实际开发中,每个表往往都会设计一个主键
-- primary key(主键)-细节说明
-- 
-- 1. primary key不能重复而且不能为null。
INSERT INTO t16(NULL,'mike','man') -- 不可以

-- 2. 一张表最多只能有一个主键,但可以是复合主键
-- 3. 主键的指定方式有两种
--    1. 直接在字段名后指定:字段名 primakry key
--    2. 在表定义最后写 primary key(列名);
CREATE TABLE t17(
	id INT,
	`name` VARCHAR(32),
	sex VARCHAR(6),
	PRIMARY KEY(id,`name`))
-- 这时候的id跟name是复合主键
INSERT INTO t17
	VALUES(1,'jack','man')
INSERT INTO t17
	VALUES(2,'lily','woman')
INSERT INTO t17
	VALUES(1,'jack','man')-- 这里就违反了复合主键

SELECT * FROM t17;

-- 4. 使用desc 表名,可以看到primary key的情况
DESC t16
DESC t17
-- 5. 在实际开发中,每个表往往都会设计一个主键

23.3 not null(非空)

  1. 如果在列上定义了not null,那么当插入数据时,必须为列提供数据

  1. 这个在前面写过很多次,就不再详细说了

23.4 unique(唯一)

  1. 当定义了唯一约束后,该列值是不能重复的。

  1. unique 细节(注意):

    1. 如果没有指定 not null,则 unique 字段可以有多个null
    2. 一张表可以有多个unique字段
  2. 简单代码演示:

-- unique(唯一)
CREATE TABLE t18(
	id INT UNIQUE,-- 表示 id 列是不可以重复的.
	`name` VARCHAR(32),
	sex VARCHAR(6))
INSERT INTO t18
	VALUES(1,'lisa','woman')
INSERT INTO t18
	VALUES(2,'tom','man')
INSERT INTO t18
	VALUES(1,'jack','man')-- 添加失败
	
SELECT * FROM t18

-- unqiue 使用细节
-- 1. 如果没有指定 not null , 则 unique 字段可以有多个 null
-- 如果一个列(字段), 是 unique not null 使用效果类似 primary key 
INSERT INTO t18
	VALUES(NULL,'smith','man')
INSERT INTO t18
	VALUES(NULL,'jack','man')

-- 2. 一张表可以有多个 unique 字段
CREATE TABLE t19(
	id INT UNIQUE,-- 表示 id 列是不可以重复的.
	`name` VARCHAR(32) UNIQUE,-- 表示name列是不可以重复的
	sex VARCHAR(6))

DESC t19

23.5 foreign key(外键)

  1. 用于定义主表和从表之间的关系: 外键约束要定义在从表上,主表则必须具有主键约束或是unique约束.,
  2. 当定义外键约束后,要求外键列数据必须在主表的主键列存在或是为null (学生/班级 图示)

  1. 简单代码操作:
-- foreign key(外键)
-- 创建 主表 my_class 
CREATE TABLE my_class(
	id INT PRIMARY KEY,
	`name` VARCHAR(32)NOT NULL DEFAULT'',
	`add` VARCHAR(32)NOT NULL DEFAULT'')

-- 创建 从表 my_stu 
CREATE TABLE my_stu(
	id INT PRIMARY KEY,
	`name` VARCHAR(32)NOT NULL DEFAULT'',
	class_id INT,
	-- 下面指定外键关系
	FOREIGN KEY(class_id) REFERENCES my_class(id))

-- 测试数据
INSERT INTO my_class 
	VALUES(100, 'java','北京'), (200, 'web','上海');
INSERT INTO my_class 
	VALUES(300, 'php','深圳');

SELECT * FROM my_class

-- 给 my_stu 填入数据
INSERT INTO my_stu
	VALUES(1,'tom', 100);
INSERT INTO my_stu 
	VALUES(2,'jack', 200);
INSERT INTO my_stu
	VALUES(3, 'hsp', 300);
-- 这里会失败...因为 400 班级不存在
INSERT INTO my_stu
	VALUES(4, 'mary', 400); 
	
SELECT * FROM my_stu

-- 这里可以, 因为外键没有写 not null 
INSERT INTO my_stu
	VALUES(5, 'king', NULL); 
	
-- 一旦建立主外键的关系,数据不能随意删除了
DELETE FROM my_class 
	WHERE id = 100;

23.6 check

  1. 用于强制行数据必须满足的条件,假定在sal列上定义了check约束,并要求sal列值在1000~2000之间
  2. 如果不再1000~2000之间就会提示出错。
  3. oracle 和 sql server 均支持check ,但是mysql5.7目前还不支持check ,只做语法校验,但不会生效。
  4. 在mysql中实现check的功能,一般是在程序中控制或者通过触发器完成。
  5. 基本语法

  1. 简单代码演示:
-- 演示 check 的使用
-- mysql5.7 目前还不支持 check ,只做语法校验,但不会生效
-- 了解
-- 学习 oracle, sql server, 这两个数据库是真的生效.

-- 测试
CREATE TABLE t20 (
	id INT PRIMARY KEY,
	`name` VARCHAR(32) ,
	sex VARCHAR(6) CHECK (sex IN('man','woman')), -- 性别限制为男或者女
	sal DOUBLE CHECK ( sal > 1000 AND sal < 2000) -- 工资限制在1000~2000
	);

-- 添加数据
-- mysql5.7 目前还不支持 check ,只做语法校验,但不会生效
INSERT INTO t20 
	VALUES(1, 'jack', 'mid', 1);

SELECT * FROM t20;

23.7 商店售货系统表设计案例

  1. 现有一个商店的数据库shop_db,记录客户及其购物情况,

  2. 由下面三个表组成:

    1. 商品goods (商品号goods_id,商品名goods_name,单价unitprice,商品类别category,供应商provider);
    2. 客户customer (客户号customer_id,姓名name,住址address,电邮email性别sex,身份证card_Id);
    3. 购买purchase (购买订单号order_id,客户号customer_id,商品号goods_id,购买数量nums);
  3. 建表,在定义中要求声明 [进行合理设计]:

    (1)每个表的主外键;

    (2)客户的姓名不能为空值;

    (3)电邮不能够重复;

    (4)客户的性别[男|女] check 枚举.

    (5)单价unitprice 在 1.0 - 9999.99 之间 check

-- 1. 现有一个商店的数据库shop_db,记录客户及其购物情况,
CREATE DATABASE shop_db;

-- 2. 由下面三个表组成: 
--    1. 商品goods (商品号goods_id,商品名goods_name,单价unitprice,商品类别category,供应商provider);
CREATE TABLE goods
	(goods_id INT PRIMARY KEY,
	goods_name VARCHAR(64)NOT NULL DEFAULT '',
	unitprice DECIMAL(10,2)NOT NULL DEFAULT 0 ,
		CHECK(unitprice >= 1.0 AND unitprice <= 9999.99),
	category INT NOT NULL DEFAULT 0,
	provider VARCHAR(64)UNIQUE)
	
--    2. 客户customer (客户号customer_id,姓名name,住址address,电邮email性别sex,身份证card_Id);
CREATE TABLE customer
	(customer_id INT PRIMARY KEY,
	`name` VARCHAR(64)NOT NULL DEFAULT'',
	address VARCHAR(64)NOT NULL DEFAULT'',
	email VARCHAR(64) UNIQUE,
	-- sex VARCHAR(6)CHECK(sex IN('man','woman')),
	-- 这里使用枚举类型
	sex ENUM('男','女') NOT NULL,
	card_id CHAR(18)
	)
	
--    3. 购买purchase (购买订单号order_id,客户号customer_id,商品号goods_id,购买数量nums);
CREATE TABLE purchase
	(order_id INT UNSIGNED PRIMARY KEY,-- unsigned 正数
	customer_id INT NOT NULL DEFAULT 0,
	goods_id INT NOT NULL DEFAULT 0,
	nums INT NOT NULL DEFAULT 0, 
	FOREIGN KEY(customer_id)REFERENCES customer(customer_id),
	FOREIGN KEY(goods_id)REFERENCES goods(goods_id))
-- 3. 建表,在定义中要求声明 [进行合理设计]:
--    (1)每个表的主外键;
--    (2)客户的姓名不能为空值;
--    (3)电邮不能够重复;
--    (4)客户的性别[|] check 枚举.
--    (5)单价unitprice 在 1.0 - 9999.99 之间 check

DESC goods;
DESC customer;
DESC purchase;

24. 自增长

24.1 基本介绍

我们先看一个问题:

在某张表中,存在一个id列(整数),我们希望在添加记录的时候,该列从1开始,自动的增长,怎么处理?

那么我们就用到了自增长,基本语法如下:

添加 自增长的字段方式

insert into xxx (字段1, 字段2…) values(null,值…);

insert into xxx (字段2…) values(值1,值2’…);

insert into xxx values(null, 值1’…)

-- 自增长
-- 在某张表中,存在一个id列(整数),我们希望在添加记录的时候,该列从1开始,自动的增长,怎么处理? 
CREATE TABLE t21(
	id INT PRIMARY KEY AUTO_INCREMENT,
	email VARCHAR(32) NOT NULL DEFAULT '',
	`name` VARCHAR(32) NOT NULL DEFAULT '')
	
DESC t21
-- 测试自增长的使用
-- 添加 自增长的字段方式
-- insert into xxx (字段1, 字段2.....) values(null,....);
INSERT INTO t21(id,email,`name`)
	VALUES(NULL,'1241955497@qq.com','sys')

-- insert into xxx (字段2.....) values(1,2'....);
INSERT INTO t21(email,`name`)
	VALUES('jack@qq.com','jack')

-- insert into xxx values(null,1'......)
INSERT INTO t21
	VALUES(NULL,'lily@qq.com','lily');
	
SELECT * FROM t21

24.2 自增长使用细节

  1. 一般来说自增长是和primary key 配合使用的

  2. 自增长也可以单独使用[但是需要配合一个unique]

  3. 自增长修饰的字段为整数型的(虽然小数也可以但是非常非常少这样使用)

  4. 自增长默认从 1开始,你也可以通过如下命令修改

    alter table 表名 auto increment = 新的开始值;

  5. 如果你添加数据时,给自增长字段(列)指定的有值,则以指定的值为准;

    如果指定了自增长,一般来说,就按照自增长的规则来添加数据.

-- 修改默认的自增长的开始值
-- alter table 表名 auto increment = 新的开始值;
ALTER TABLE t22 AUTO_INCREMENT = 100;
CREATE TABLE t22(
	id INT PRIMARY KEY AUTO_INCREMENT,
	email VARCHAR(32) NOT NULL DEFAULT '',
	`name` VARCHAR(32) NOT NULL DEFAULT '')

INSERT INTO t22
	VALUES(NULL,'tom@qq.com','tom')

-- 如果你添加数据时,给自增长字段()指定的有值,则以指定的值为准;
-- 如果指定了自增长,一般来说,就按照自增长的规则来添加数据.
INSERT INTO t22
	VALUES(666,'jack@qq.com','jack')

SELECT * FROM t22;

25. mysql 索引

25.1 快速入门

  1. 说起提高数据库性能,索引是最物美价廉的东西了。
  2. 索引不用加内存,不用改程序,不用调sql,查询速度就可能提高百倍干倍。
  3. 这里我们举例说明索引的好处[构建海量表8000000]
-- 索引
-- 创建测试数据库 tmp
CREATE DATABASE tmp;

-- 下面是海量数据表,只为了测试索引用,先不用理解
CREATE TABLE dept( /*部门表*/
deptno MEDIUMINT   UNSIGNED  NOT NULL  DEFAULT 0,
dname VARCHAR(20)  NOT NULL  DEFAULT "",
loc VARCHAR(13) NOT NULL DEFAULT ""
) ;

#创建表EMP雇员
CREATE TABLE emp
(empno  MEDIUMINT UNSIGNED  NOT NULL  DEFAULT 0, /*编号*/
ename VARCHAR(20) NOT NULL DEFAULT "", /*名字*/
job VARCHAR(9) NOT NULL DEFAULT "",/*工作*/
mgr MEDIUMINT UNSIGNED NOT NULL DEFAULT 0,/*上级编号*/
hiredate DATE NOT NULL,/*入职时间*/
sal DECIMAL(7,2)  NOT NULL,/*薪水*/
comm DECIMAL(7,2) NOT NULL,/*红利*/
deptno MEDIUMINT UNSIGNED NOT NULL DEFAULT 0 /*部门编号*/
) ;

#工资级别表
CREATE TABLE salgrade
(
grade MEDIUMINT UNSIGNED NOT NULL DEFAULT 0,
losal DECIMAL(17,2)  NOT NULL,
hisal DECIMAL(17,2)  NOT NULL
);

#测试数据
INSERT INTO salgrade VALUES (1,700,1200);
INSERT INTO salgrade VALUES (2,1201,1400);
INSERT INTO salgrade VALUES (3,1401,2000);
INSERT INTO salgrade VALUES (4,2001,3000);
INSERT INTO salgrade VALUES (5,3001,9999);

DELIMITER $$

#创建一个函数,名字 rand_string,可以随机返回我指定的个数字符串
CREATE FUNCTION rand_string(n INT)
RETURNS VARCHAR(255) #该函数会返回一个字符串
BEGIN
#定义了一个变量 chars_str, 类型  varchar(100)
#默认给 chars_str 初始值   'abcdefghijklmnopqrstuvwxyzABCDEFJHIJKLMNOPQRSTUVWXYZ'
 DECLARE chars_str VARCHAR(100) DEFAULT
   'abcdefghijklmnopqrstuvwxyzABCDEFJHIJKLMNOPQRSTUVWXYZ'; 
 DECLARE return_str VARCHAR(255) DEFAULT '';
 DECLARE i INT DEFAULT 0; 
 WHILE i < n DO
    # concat 函数 : 连接函数mysql函数
   SET return_str =CONCAT(return_str,SUBSTRING(chars_str,FLOOR(1+RAND()*52),1));
   SET i = i + 1;
   END WHILE;
  RETURN return_str;
  END $$


 #这里我们又自定了一个函数,返回一个随机的部门号
CREATE FUNCTION rand_num( )
RETURNS INT(5)
BEGIN
DECLARE i INT DEFAULT 0;
SET i = FLOOR(10+RAND()*500);
RETURN i;
END $$

 #创建一个存储过程, 可以添加雇员
CREATE PROCEDURE insert_emp(IN START INT(10),IN max_num INT(10))
BEGIN
DECLARE i INT DEFAULT 0;
#set autocommit =0 把autocommit设置成0
 #autocommit = 0 含义: 不要自动提交
 SET autocommit = 0; #默认不提交sql语句
 REPEAT
 SET i = i + 1;
 #通过前面写的函数随机产生字符串和部门编号,然后加入到emp表
 INSERT INTO emp VALUES ((START+i) ,rand_string(6),'SALESMAN',0001,CURDATE(),2000,400,rand_num());
  UNTIL i = max_num
 END REPEAT;
 #commit整体提交所有sql语句,提高效率
   COMMIT;
 END $$

 #添加8000000数据
CALL insert_emp(100001,8000000)$$

#命令结束符,再重新设置为;
DELIMITER ;
  1. 上面这张有8000000个数据的海量表仅为了测试索引的好处
  2. 接下来我们在没有索引的前提下随便搜索一个数据,花费的时间很久
-- 在没有创建索引时,我们随便查询一条记录
-- 花费了7.380s,效率很低
SELECT * 
	FROM emp
	WHERE empno = 6666666 
  1. 我们看一下在添加索引之前文件大小是512m,创建索引后文件是640m
  2. 由此我们可以看到,索引本身也是占用空间

  1. 创建索引后我们发现,效率提升特别大
-- 现在我们使用索引来优化一下,体验一下索引的好处

-- 在没有创建索引前,emp.idb文件大小是512m
-- 创建索引后,emp.idb文件大小是640m
-- 由此可以看出索引本身也会占用空间

-- empno_index是索引名称
-- ON emp(empno):表示在emp表的empno列创建索引
CREATE INDEX empno_index ON emp(empno)

-- 创建索引后,我们还是查询一样的记录
-- 只花费了0.001s,效率提升特别大
SELECT * 
	FROM emp
	WHERE empno = 6666666 
  1. 但是创建索引后,只对创建了索引的列有效,当我们查询其他列的时候,还是会很慢

  2. 所以我们创建一个索引其实对整体是没有很大作用的

25.2 索引的原理

  1. 没有索引的时候扫描为什么会慢?

    因为没有索引的时候,会进行全表扫描,所以查询速度很慢

  2. 使用索引为什么会快?

    当创建了索引以后形成一个索引的数据结构,比如二又树索引的代价

    1. 磁盘占用
    2. 如果对表进行 dml (update delete insert) 会对索引进行维护,对速度有影响
  3. 在我们项目中,select用的比较多[占比90%],update,delete,insert用的比较少[占比10%]

25.3 索引的类型

  1. 主键索引,主键自动的为主索引(类型Primary key)

    create table t1 
        (id int primary key,-- 主键,同时也是索引,称为主键索引
         name varchar(32));
    
  2. 唯一索引(UNIQUE)

    create table t2
        (id int unique,-- id是唯一的,同时也是索引,称为unique索引
         name varchar(32));
    
  3. 普通索引(INDEX)

  4. 全文索引(FULLTEXT) [适用于MyISAM]

  5. 一般开发,不使用 mysql 自带的全文索引,而是使用:全文搜索 Solr 和 ElasticSearch (ES)

25.4 索引使用

  1. 添加索引→主要分为三种:

    1. 添加唯一索引
    2. 添加普通索引
    3. 添加主键索引
    -- 先创建一个表用于测试 id ,name
    CREATE TABLE t23(
    	id INT,
    	`name` VARCHAR(32));
    	
    -- 查询表是否有索引,没有索引
    SHOW INDEXES FROM t23;
    
    -- 添加索引
    -- 1. 添加唯一索引
    -- create [UNIQUE] index index_name on tbl name (col_name [(length)]
    -- [ASC | DESC] , .....);
    CREATE UNIQUE INDEX id_index ON t23(id);
    
    -- 查询表是否有索引,查到有索引
    -- 注意:这里显示的0代表唯一索引,1代表不是唯一索引
    SHOW INDEXES FROM t23;
    
    -- 2. 添加普通索引
    -- 添加普通索引方式1
    CREATE INDEX id_index ON t23(id);
    
    -- 添加普通索引方式2
    -- alter table table_name ADD INDEX [index_name] (index_col_name....)
    ALTER TABLE t23 ADD INDEX id_index(id)
    
    -- 如何选择唯一索引和普通索引
    -- 如果某列的值,是不会重复的,则优先考虑unique索引,否则使用普通索引
    
    -- 3. 添加主键(索引)
    -- ALTER TABLE 表名 ADD PRIMARY KEY(列名...);
    
    -- 先创建一个表用于测试 id ,name
    CREATE TABLE t24(
    	id INT,
    	`name` VARCHAR(32));
    
    ALTER TABLE t24 ADD PRIMARY KEY(id);
    
    -- 查询表是否有索引,没有索引
    SHOW INDEXES FROM t24;
    
  2. 删除索引→主要分为两种:

    1. 删除普通索引和删除唯一索引
    2. 删除主键索引
    -- 删除索引
    -- DROP INDEX index_name ON tbl_name
    -- alter table table_name drop index index_name;
    DROP INDEX id_index ON t23
    
    -- 删除主键索引 比较特别: 
    -- alter table t_b drop primary key;
    ALTER TABLE t24 DROP PRIMARY KEY;
    
  3. 修改索引:就是把索引删除,然后再添加索引

  4. 查询索引→主要分为三种:

    -- 查询索引(三种方式)
    -- show index(es) from table_name;
    SHOW INDEX FROM t23;
    SHOW INDEXES FROM t23;
    
    -- show keys from table_name;
    SHOW KEYS FROM t23;
    
    -- desc table_Name;
    DESC t23;
    

25.5 索引小练习

1. 建立索引(主键)

要求:

创建一张订单表order (id号,商品名,订购人,数量).

要求id号为主键,请使用2种方式来创建主键.(order1,order2 )

-- 方式一
CREATE TABLE order1(
	id INT PRIMARY KEY,
	produce_name VARCHAR(32)NOT NULL DEFAULT '',
	`order` VARCHAR(32)NOT NULL DEFAULT '',
	num INT);
SHOW INDEX FROM order1;
CREATE INDEX id_index ON order1(id);

-- 方式二
CREATE TABLE order2(
	id INT NOT NULL DEFAULT 0,
	produce_name VARCHAR(32)NOT NULL DEFAULT '',
	`order` VARCHAR(32)NOT NULL DEFAULT '',
	num INT);
SHOW INDEX FROM order2;
ALTER TABLE order2 ADD PRIMARY KEY(id);

2. 建立索引(唯一)

要求:

创建一张特价菜谱表menu (id号,菜谱名,厨师,点餐人身份证,价格).

要求id号为主键,点餐人身份证是unique 请使用两种方式来创建unique.(menu1,menu2)

-- 方式一
CREATE TABLE menu1(
	id INT UNIQUE,
	menu_name VARCHAR(32)NOT NULL DEFAULT '',
	chef_name VARCHAR(32)NOT NULL DEFAULT '',
	card_id CHAR(18),
	price DOUBLE )
	
SHOW KEYS FROM menu1;

CREATE INDEX id_index ON menu1(id);

-- 方式二
CREATE TABLE menu2(
	id INT ,
	menu_name VARCHAR(32)NOT NULL DEFAULT '',
	chef_name VARCHAR(32)NOT NULL DEFAULT '',
	card_id CHAR(18),
	price DOUBLE )
	
SHOW KEYS FROM menu2;

CREATE UNIQUE INDEX id_index ON menu2(id);

3. 建立索引(普通)

要求:

创建一张运动员表sportman (id号,名字,特长).要求id号为主键名字为普通索引,

请使用2种方式来创建索引( sportman1,sportman2)

-- 方式一
CREATE TABLE sportman1(
	id INT PRIMARY KEY,
	`name` VARCHAR(32) NOT NULL DEFAULT '',
	strong_point VARCHAR(32) NOT NULL DEFAULT '');
	
SHOW INDEX FROM sportman1;
CREATE INDEX id_index ON sportman1(id);
CREATE INDEX name_index ON sportman1(`name`);

-- 方式二
CREATE TABLE sportman2(
	id INT ,
	`name` VARCHAR(32) NOT NULL DEFAULT '',
	strong_point VARCHAR(32) NOT NULL DEFAULT '');
	
SHOW INDEX FROM sportman2;

ALTER TABLE sportman2 ADD PRIMARY KEY(id);
CREATE INDEX name_index ON sportman2(`name`);

25.6 索引小结: 哪些列上适合使用索引

  1. 较频繁的作为查询条件字段应该创建索引

    select * from emp where empno = 1

  2. 唯一性太差的字段不适合单独创建索引,即使频繁作为查询条件

    select * from emp where sex = ‘男’

  3. 更新非常频繁的字段不适合创建索引

    select * from emp where logincount = 1

  4. 不会出现在WHERE子句中字段不该创建索引

26. mysql 事务

26.1 什么是事务

事务用于保证数据的一致性,它由一组相关的dml语句组成,该组的dml语句要么全部成功,要么全部失败。

例如:转账就要用事务来处理,用以保证数据的一致性。

26.2 事务和锁

当执行事务操作时(dml语句),mysql会在表上加锁,防止其它用户改表的数据这对用户来讲是非常重要的

事物的几个重要基本操作

mysql 数据库控制台事务的几个重要操作:

start transaction – 开始一个事务

savepoint 保存点名 – 设置保存点

rollback to 保存点名 – 回退事务

rollback – 回退全部事务

commit – 提交事务所有的操作生效不能回退

-- 事物的一个重要概念和具体操作
-- mysql 数据库控制台事务的几个重要操作:
-- start transaction -- 开始一个事务
-- savepoint 保存点名 -- 设置保存点
-- rollback to 保存点名 -- 回退事务
-- rollback -- 回退全部事务
-- commit -- 提交事务所有的操作生效不能回退

-- 演示
-- 1. 创建一张测试表
CREATE TABLE t25(
	id INT,
	`name`VARCHAR(32));
-- 2. 开启事务
START TRANSACTION

-- 3. 设置保存点
SAVEPOINT a

-- 4. 执行dml操作
INSERT INTO t25 VALUES(1,'jack');

-- 5. 设置保存点
SAVEPOINT b

-- 6. 执行dml操作
INSERT INTO t25 VALUES(2,'lucy');

-- 7. 回退到b
ROLLBACK TO b;

-- 8. 回退到a
ROLLBACK TO a;

-- 如果这样操作,那么将会回到事物最初的起点
ROLLBACK

-- 当执行了这个操作以后,之前所有的保存点都会清除
-- 不能再进行回退,所以要谨慎使用
COMMIT

-- 查询表格
SELECT * FROM t25

26.3 回退事务

  1. 在介绍回退事务前,先介绍一下保存点(savepoint):保存点是事务中的点.用于取消部分事务,
  2. 当结束事务时 (commit) ,会自动的删除该事务所定义的所有保存点
  3. 当执行回退事务时,通过指定保存点可以回退到指定的点,

26.4 提交事务

  1. 使用commit语句可以提交事务.当执行了commit语句子后会确认事务的变化、结束事务、删除保存点、释放锁,数据生效。
  2. 当使用commit语句结束事务子后,其它会话 [其他连接] 将可以查看到事务变化后的新数据 [所有数据就正式生效.]

26.5 事务细节讨论

  1. 如果不开始事务,默认情况下,dml操作是自动提交的,不能回滚

  2. 如果开始一个事务,你没有创建保存点.你可以执行 rollback,默认就是回退到你事务开始的状态.

  3. 你也可以在这个事务中(还没有提交时),创建多个保存点.

    比如: savepoint aaa: 执行 dml, savepoint bbb;

  4. 你可以在事务没有提交前,选择回退到哪个保存点.

  5. mysql的事务机制需要innodb的存储引擎才可以使用,myisam不好使

  6. 开始一个事务 start transaction,set autocommit=off;

-- 讨论事物细节
-- 1. 如果不开始事务,默认情况下,dml操作是自动提交的,不能回滚
INSERT INTO t25 VALUES(3,'lisa')

SELECT * FROM t25

-- 2. 如果开始一个事务,你没有创建保存点.你可以执行 rollback,
-- 默认就是回退到你事务开始的状态.
START TRANSACTION
INSERT INTO t25 VALUES(4,'milan')
INSERT INTO t25 VALUES(5,'duck')

ROLLBACK -- 表示直接回退到事物开始的状态
COMMIT

-- 3. 你也可以在这个事务中(还没有提交时),创建多个保存点.
-- 比如: savepoint aaa: 执行 dml, savepoint bbb;

-- 4. 你可以在事务没有提交前,选择回退到哪个保存点.

-- 5. mysql的事务机制需要innodb的存储引擎才可以使用,myisam不好使
-- InnoDB 存储引擎支持事务,MyISAM不支持

-- 6. 开始一个事务 start transaction,set autocommit=off;
SET autocommit = off;

27. mysql 事务隔离级别

27.1 事务隔离级别介绍

  1. 通俗解释:多个连接开启各自事务操作数据库中数据时,数据库系统要负责隔离操作,以保证各个连接在获取数据时的准确性。
  2. 如果不考虑隔离性,可能会引发如下问题:
    1. 脏读
    2. 不可重复读
    3. 幻读

27.2 查看事务隔离级别

  1. 脏读(dirty read):当一个事务读取另一个事务尚未提交的改变(update,insert,delete)时,产生脏读
  2. 不可重复读(nonrepeatable read): 同一查询在同一事务中多次进行,由于其他提交事务所做的修改或删除,每次返回不同的结果集,此时发生不可重复读
  3. 幻读 (phantom read): 同一查询在同一事务中多次进行,由于其他提交事务所做的插入操作,每次返回不同的结果集,此时发生幻读。

27.3 事务隔离级别

概念:Mysql隔离级别定义了事务与事务之间的隔离程度。

具体的见下表:

27.4 mysql 的事务隔离级–案例

我们举例一个案例来说明mysql的事务隔离级别以对account表进行操作为例。(id,name,money)

我们就以读未提交为例,做一个演示:

  1. 先打开两个mysql的控制台

  2. 查看当前mysql的隔离级别

    操作指令:SELECT @@tx_isolation;

    查到我们的事务隔离级别是可重复读

    /*
    mysql> SELECT @@tx_isolation;
    +-----------------+
    | @@tx_isolation  |
    +-----------------+
    | REPEATABLE-READ |
    +-----------------+
    */
    
  3. 接下来我们把其中一个控制台隔离级别设置 Read uncommitted(读未提交)

    操作指令:SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;

    /*
    mysql> SELECT @@tx_isolation;
    +------------------+
    | @@tx_isolation   |
    +------------------+
    | READ-UNCOMMITTED |
    +------------------+
    */
    
  4. 两个控制台都开始事物

    START TRANSACTION;
    
  5. 接下来我们在可重复读隔离级别的控制台里面创建表

    CREATE TABLE `account`(
    	id INT,
    	`name`VARCHAR(32),
    	money INT);
    
  6. 这时候我们在可重复读隔离级别的控制台里面添加一条数据

    INSERT INTO `account` 
    	VALUES(100,'jcak',1000);
    
  7. 然后在读未提交隔离级别的控制台里搜索表account

    能看到添加进去的数据,这就是脏读

  8. 这时候我们在可重复读隔离级别的控制台里面修改一下数据

    UPDATE `account` SET money = 800 WHERE id = 100;
    
  9. 我们再添加一个数据

    INSERT INTO `account` 
    	VALUES(200,'tom',2000);
    
  10. 然后我们提交数据

    COMMIT;
    
  11. 然后我们在读未提交隔离级别的控制台里搜索表account,能看到新的数据

    能看到修改的数据,这就是不可重复读;

    也能看到新加入的数据,这就是幻读;

综上所述,其他的事物隔离级别也是相同的道理,具体参照上面那张事务隔离级别表格,这里就不做过多详述;

  1. 当另一个窗口是读已提交事物级别的时候,将不会出现脏读,但是还会出现不可重复读和幻读

  2. 当另一个窗口是可重复读事物级别的时候,将不会出现脏读,不可重复读和幻读

  3. 当另一个窗口是可串行化事物级别的时候,将不会出现脏读,不可重复读和幻读,

    而且当操作的那个窗口没提交的时候,可串行化的窗口会被锁在那里,不能进行操作;

27.5 设置事务隔离级别

  1. 查看当前会话隔离级别

    select @@tx isolation;

  2. 查看系统当前隔离级别

    select @@global.tx isolation;

  3. 设置当前会话隔离级别

    set session transaction isolation level repeatable read;

  4. 设置系统当前隔离级别4.

    set global transaction isolation level repeatable read;

  5. mysql 默认的事务隔离级别是 repeatable read,

    一般情况下,没有特殊要求,没有必要修改 (因为该级别可以满足绝大部分项目需求)

  6. 但是如果你非要修改默认的事务隔离级别的话,不用每次都修改,可以修改my.ini配置文件,

    在my.ini配置文件最后加上你要设置的事物级别:

    READ-UNCOMMITTED,读未提交
    
    READ-COMMITTED,读已提交
    
    REPEATABLEREAD, 可重复读
    
    SERIALIZABLE.可串行化
    
  7. 在里面加入指令即可,指令为:transaction-isolation = [你要设置的事物级别]

28. mysql 事务 ACID

28.1 事务的acid 特性

  1. 原子性 (Atomicity)

    原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。

  2. 一致性 (Consistency)

    事务必须使数据库从一个一致性状态变换到另外一个一致性状态

  3. 隔离性 (lsolation)

    事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。

  4. 持久性 (Durability)

    持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响

事务的课堂练习

  1. 登录mysql控制客户端A,创建表 dog (id,name),开始一个事务,添加两条记录;
  2. 登录mysql控制客户端B,开始一个事务,设置为读未提交
  3. A客户端修改Dog 一条记录,不要提交。看看B客户端是否看到变化,说明什么问题?
  4. 登录mysql客户端C,开始一个事务,设置为读已提交,这时A客户修改一条记录,不要提交,看看C客户端是否看到变化,说明什么问题?

解题思路:

  1. 先打开三个mysql控制台

  2. 进入mysql -> 进入数据库 sys_db02

    mysql -u root -p
    USE sys_db02
    
  3. 把控制台B设置为 读未提交

    SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
    
  4. 把控制台C设置为 读已提交

    SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
    
  5. A控制台创建表 dog (id,name),开始一个事务,添加两条记录;

    1. 创建dog表

      CREATE TABLE dog(
      	id INT,
      	`name` VARCHAR(32));
      
    2. 三个控制台开始事物

      START TRANSACTION;
      
    3. 添加两条记录

      INSERT INTO dog VALUES(1,'皮皮');
      INSERT INTO dog VALUES(2,'大黄');
      
  6. 在控制台b搜索dog表,发现是加入数据的表

    说明 读未提交 会发生 脏读

    /*
    mysql> select * from dog;
    +------+--------+
    | id   | name   |
    +------+--------+
    |    1 | 皮皮   |
    |    2 | 大黄   |
    +------+--------+
    */
    
  7. 在控制台a修改大黄的名字

    UPDATE  dog SET `name` = '小虎' WHERE id = 2; 
    
  8. 在控制台c查询表格,发现还是原来的表格

    说明 读已提交 不会发生 脏读

    /*
    mysql> select * from dog;
    +------+--------+
    | id   | name   |
    +------+--------+
    |    1 | 皮皮   |
    |    2 | 大黄   |
    +------+--------+
    */
    
  9. 如果控制台a提交的话,则会显示修改好的表格

    说明 读已提交会发生 不可重复读

    /*
    mysql> select * from dog;
    +------+--------+
    | id   | name   |
    +------+--------+
    |    1 | 皮皮   |
    |    2 | 小虎   |
    +------+--------+
    */
    

29. mysql 表类型和存储引擎

29.1 基本介绍

  1. MySQL的表类型由存储引擎 (Storage Engines)决定,主要包括MyISAM、innoDB、Memory等。
  2. MySQL 数据表主要支持六种类型 ,分别是: CSV、Memory、ARCHIVE、MRG_MYISAM、MYISAM、InnoDB.
  3. 这六种又分为两类:
    1. 一类是”事务安全型”(transaction-safe),比如:InnoDB;
    2. 其余都属于第二类,称为”非事务安全 型”(non-transaction-safe)[mysiam 和 memory]
  4. 显示当前数据库支持的存储引擎的指令:show engines;

  1. 由上图可以看出各种引擎的作用
    1. InnoDB:支持事务、行级锁和外键
    2. MRG_MYISAM:相同的MyISAM表的集合
    3. MEMORY:基于哈希,存储在内存中,对临时表很有用
    4. BLACKHOLE:/dev/null存储引擎(写入的内容都会消失),用来销毁数据
    5. MyISAM:MyISAM存储引擎
    6. CSV:CSV存储引擎
    7. ARCHIVE:归档存储引擎
    8. PERFORMANCE_SCHEMA:性能模式
    9. FEDERATED:联邦MySQL存储引擎
  2. 主要支持前面6种存储引擎。

29.2 主要的存储引擎/表类型特点

29.3 细节说明

这里重点介绍三种: MyISAM、InnoDB、MEMORY

  1. MyISAM不支持事务、也不支持外键,但其访问速度快,对事务完整性没有要求
  2. InnoDB存储引擎提供了具有提交、回滚和崩溃恢复能力的事务安全。但是比起MyISAM存储引擎,InnoDB写的处理效率差一些并且会占用更多的磁盘空间以保留数据和索引。
  3. MEMORY存储引擎使用存在内存中的内容来创建表。每个MEMORY表只实际对应一个磁盘文件。MEMORY类型的表访问非常得快,因为它的数据是放在内存中的,并且默认使用HASH索引。但是一旦MySQL服务关闭,表中的数据就会丢失掉,表的结构还在。

29.4 三种存储引擎表使用案例

-- 表类型和存储引擎
-- 查看所有存储引擎
SHOW ENGINES

-- innodb 存储引擎,是前面使用过.
-- 1. 支持事务 2. 支持外键 3. 支持行级锁

-- myisam 存储引擎
CREATE TABLE t26 (
	id INT,
	`name` VARCHAR(32)) ENGINE MYISAM
-- 1. 添加速度快 2. 不支持外键和事务 3. 支持表级锁
-- 开始事物
START TRANSACTION;
-- 添加保存点t1
SAVEPOINT t1;
-- 添加一条数据
INSERT INTO t26 VALUES(1,'jack');
-- 回滚到t1
ROLLBACK TO t1;
-- 通过搜索发现,回滚失败
-- 所以 MYISAM 引擎是不支持事物的
SELECT * FROM t26;

-- memory 存储引擎
-- 1. 数据存储在内存中[关闭了 Mysql 服务,数据丢失, 但是表结构还在]
-- 2. 执行速度很快(没有 IO 读写) 3. 默认支持索引(hash 表)

-- 创建表t27
CREATE TABLE t27(
	id INT,
	`name` VARCHAR(32)) ENGINE MEMORY
-- 添加三条数据
INSERT INTO t27 VALUES(1,'jack'),(2,'lucy'),(3,'lisa');
-- 搜索表
SELECT * FROM t27;

-- 当我们在控制台重启mysql以后,再次搜索表t27
-- 发现所有数据都不见了,但是表的结构还在
DESC t27;

当我们在控制台重启mysql图例:

29.5 如何选择表的存储引擎

  1. 如果你的应用不需要事务,处理的只是基本的CRUD操作,那么MyISAM是不二选择,速度快

  2. 如果需要支持事务,选择InnoDB.

  3. Memory 存储引擎就是将数据存储在内存中,由于没有磁盘 I/O 的等待速度极快。

    但由于是内存存储引擎,所做的任何修改在服务器重启后都将消失。(经典用法 用户的在线状态().)

29.6 修改存储引擎

ALTER TABLE 表名 ENGINE = 储存引擎;

-- 修改t27的存储引擎
ALTER TABLE t27 ENGINE = INNODB;

30. 视图(view)

30.1 引出

先看一个需求:

  1. emp 表的列信息很多,有些信息是个人重要信息(比如 sal , comm , mgr , hiredate),
  2. 如果我们希望某个用户只能查询emp表的(empno,ename,job 和 deptno)信息,有什么办法?
  3. 那么就可以通过视图来实现

30.2 基本概念

  1. 视图是一个虚拟表,其内容由查询定义。同真实的表一样,视图包含列,其数据来自对应的真实表(基表)
  2. 视图和基表关系的示意图

30.3 视图的基本使用

  1. create view 视图名 as selecti语句
  2. alter view 视图名 as select语句 – 更新成新的视图
  3. SHOW CREATE VIEW 视图名
  4. drop view 视图名1,视图名2

30.4 完成前面提出的需求

创建一个视图emp_view01,只能查询 emp表的(empno , ename , job 和 deptno)信息

-- 视图的使用
-- 创建一个视图emp_view01,只能查询 emp表的(empno , ename , job 和 deptno)信息

-- 创建视图
CREATE VIEW emp_view01
	AS
	SELECT empno,ename,job,deptno FROM emp;
	
-- 查看视图
DESC emp_view01;

SELECT * FROM emp_view01;

SELECT ename FROM emp_view01;


-- 查看创建视图的指令
SHOW CREATE VIEW emp_view01;

-- 1.创建视图
-- create view 视图名 as selecti语句

-- 2.alter view 视图名 as select语句 -- 更新成新的视图

-- 3. SHOW CREATE VIEW 视图名

-- 4. drop view 视图名1,视图名2
DROP VIEW emp_view01;

30.5 视图细节讨论

  1. 创建视图后,到数据库去看,对应视图只有一个视图结构文件(形式: 视图名.frm)

  2. 视图的数据变化会影响到基表,基表的数据变化也会影响到视图 [insert update delete ]

  3. —·针对前面的雇员管理系统-----

    mysql> create view myview as select empno ,ename , job, comm from emp;

    mysql> select * from myview;

    mysql> update myview set comm=200 where empno=7369;//修改视图,对基表都有变化

    mysql> update emp set comm=100 where empno=7369; //修改基表,对视频也有变化

  4. 视图中可以再使用视图,数据仍然来自基表…[案例演示]

-- 视图细节的讨论
-- 1. 创建视图后,到数据库去看,对应视图只有一个视图结构文件(形式: 视图名.frm)
-- 2. 视图的数据变化会影响到基表,基表的数据变化也会影响到视图 [insert update delete ]

-- 修改视图,会影响到基表
-- 把编号7369 SMITH的job改为MANAGER 
UPDATE emp_view01 
	SET job = 'MANAGER'
	WHERE empno = 7369;
-- 查询基表后发现修改了视图的数据,
-- 基表也跟着修改了
SELECT * FROM emp;
-- 现在我们通过基表把SIMTH的job修改为SALESMAN

UPDATE emp 
	SET job = 'SALESMAN'
	WHERE empno = 7369;
-- 查询视图
SELECT * FROM emp_view01;

-- 视图中可以再使用视图,数据仍然来自基表..[案例演示]
-- 我们从emp_view01的视图中,选出empno和ename作出新的视图
CREATE VIEW emp_view02
	AS
	SELECT empno,ename FROM emp_view01;
-- 查询视图emp_view02
SELECT * FROM emp_view02;

30.6 视图最佳实践

  1. 安全:

    一些数据表有着重要的信息。有些字段是保密的,不能让用户直接看到。这时就可以创建一个视图,在这张视图中只保留一部分字段。这样,用户就可以查询自己需要的字段,不能查看保密的字段。

  2. 性能:

    关系数据库的数据常常会分表存储,使用外键建立这些表的之间关系。这时,数据库查询通常会用到连接 (JOIN)。这样做不但麻烦,效率相对也比较低。如果建立一个视图,将相关的表和字段组合在一起,就可以避免使用JOIN查询数据

  3. 灵活:

    如果系统中有一张旧的表,这张表由于设计的问题,即将被废弃。然而,很多应用都是基于这张表,不易修改。这时就可以建立一张视图,视图中的数据直接映射到新建的表。这样,就可以少做很多改动,也达到了升级数据表的目的。

30.7 视图课堂练习

针对 emp,dept,和 salgrade 张三表.创建一视图 emp view03,

可以显示雇员编号,雇员名,雇员部门名称和 薪水级别[即使用三张表,构建一个视图]

-- 针对 emp,dept,和 salgrade 张三表.创建一视图 emp view03,
-- 可以显示雇员编号,雇员名,雇员部门名称和 薪水级别[即使用三张表,构建一个视图] 
CREATE VIEW emp_view03
	AS
	SELECT empno,ename,dname,grade
		FROM emp,dept,salgrade
		WHERE emp.deptno = dept.deptno
		AND (sal BETWEEN losal AND hisal);
-- 查询emp_view03视图		
SELECT * FROM emp_view03;

31. Mysql 管理

31.1 Mysql 用户

  1. mysql中的用户,都存储在系统数据库mysql中 user 表中

其中user表的重要字段说明:

  1. host:
    允许登录的“位置”,localhost表示该用户只允许本机登录,也可以指定ip地址,比如:192.168.1.100
  2. user:用户名;
  3. authentication_string: 密码,是通过mysql的password()函数加密之后的密码。

31.2 创建用户

create user ‘用户名’ @ ‘允许登录位置’ identified by ‘密码’

说明: 创建用户,同时指定密码

案例演示:

-- mysql用户的管理
-- 原因:当我们做开发时,可以根据不同的开发人员,赋给他们相应的mysql操作权限
-- 所以,mysql数据库管理人员(root)根据需要创建不同的用户,赋给相应的权限,供开发人员使用

-- 1. 创建新的用户
-- 解读:
-- 'sys_stu'@'localhost' 表示用户的完整信息 
-- 'sys_stu' 用户名  'localhost' 登录的ip  '123456' 登录的密码
-- 但是注意存放到mysql.user表时,是password('123456')加密后的密码
-- *6BB4837EB74329105EE4568DDA7DC67ED2CA2AD9
CREATE USER 'sys_stu'@'localhost' IDENTIFIED BY '123456'

-- 密码加密
SELECT PASSWORD('123456')

SELECT * FROM mysql.user

SELECT `host`,`user`, authentication_string
	FROM mysql.user

这时候我们就可以登录新创的用户 sys_stu

由此可得不同的数据库用户,登录到DBMS后,根据相应的权限,可以操作的数据库和数据对象(表,视图,触发器)都不一样

31.3 删除用户

drop user ‘用户名’ @ ‘允许登录位置’;

-- 删除用户
DROP USER 'sys_stu'@'localhost'

31.4 用户修改密码

  1. 修改自己的密码

    set password = password("密码);

  2. 修改他人的密码 (需要有修改用户密码权限)
    set password for用户名@'登录位置 = password(密码);

sys_stu用户可以修改自己的密码,但是修改不了root用户的密码

但是root用户可以修改任何用户的密码

-- root用户可以修改sys_stu的密码
SET PASSWORD FOR 'sys_stu'@'localhost' = PASSWORD('123456')

31.5 mysql 中的权限

31.6 给用户授权

31.7 回收用户授权

基本语法
revoke 权限列表 on 库.对象名 from ‘用户名’@‘登录位置’;

31.8 权限生效指令

如果权限没有生效,可以执行下面命令

基本语法:FLUSH PRIVILEGES;

31.9 课堂练习题 grant.sql

用户管理练习题

  1. 创建一个用户(你的名字,拼音),密码 123,并且只可以从本地登录,不让远程登录mysql
  2. 创建库和表 testdb下的 news 表,要求:使用root 用户创建
  3. 给用户分配查看 news 表和添加数据的权限
  4. 测试看看用户是否只有这几个权限
  5. 修改密码为 abc,要求: 使用root 用户完成
  6. 重新登录
  7. 演示 回收权限
  8. 使用root 用户删除你的用户

root用户的代码演示:

-- 1. 创建一个用户(你的名字,拼音),密码 123,并且只可以从本地登录,不让远程登录mysql
CREATE USER 'song'@'localhost' IDENTIFIED BY '123';
-- 2. 创建库和表 testdb下的 news 表,要求:使用root 用户创建
CREATE DATABASE testdb
CREATE TABLE news(
	id INT,
	content VARCHAR(32));
INSERT INTO news VALUES(1,'木叶')
INSERT INTO news VALUES(2,'杰西')


-- 3. 给用户分配查看 news 表和添加数据的权限
-- grant 权限列表 on 库对象名 to用户名'@’登录位置[identified by密码]
GRANT SELECT,INSERT
	ON news
	TO 'song'@'localhost'
-- 4. 测试看看用户是否只有这几个权限

-- 增加一个权限
GRANT UPDATE
	ON news
	TO 'song'@'localhost'

-- 5. 修改密码为 abc,要求: 使用root 用户完成
SET PASSWORD FOR 'song'@'localhost' = PASSWORD('abc')
-- 6. 重新登录

-- 7. 演示 回收权限
-- revoke 权限列表 on 库.对象名 from '用户名'@'登录位置';
REVOKE SELECT,INSERT,UPDATE
	ON news
	FROM 'song'@'localhost'
-- 8. 使用root 用户删除你的用户
DROP USER 'song'@'localhost'

新创的song 用户的代码演示:

-- 这里在默认情况下,song用户只能看到一个默认的系统数据库
-- 可以查看
SELECT * FROM news
-- 可以插入数据
INSERT INTO news VALUES(3,'大圣')
-- 不能修改,没有权限
UPDATE news SET content = '彼岸'
	WHERE id = 1;-- 失败

-- 当root重新赋给我们update权限过后,可以修改了	
UPDATE news SET content = '彼岸'
	WHERE id = 1;

-- 重新登陆

-- 当root回收我们的权限以后,我们就不能进行操作了

31.10 细节说明 manage_detail.sql

  1. 在创建用户的时候,如果不指定Host, 则为%,%表示表示所有IP都有连接权限create user xxx:

  2. 你也可以这样指定create user ‘xxx’@‘192.168.1.%’

    表示xxx用户在 192.168.1.的ip可以登录mysql

  3. 在删除用户的时候,如果 host 不是 %,需要明确指定’用户’@‘host值’

-- 细节说明
-- 1. 在创建用户的时候,如果不指定Host, 则为%%表示表示所有IP都有连接权限create user xxx:
CREATE USER jack
-- 查询信息
SELECT `host`,`user` FROM mysql.user

-- 2. 你也可以这样指定create user 'xxx'@'192.168.1.%'
--    表示xxx用户在 192.168.1.的ip可以登录mysql
CREATE USER 'sys'@'192.168.1.%'

-- 3. 在删除用户的时候,如果 host 不是 %,需要明确指定'用户'@'host值'
-- 因为jack的host是%,所以可以直接删除
-- 等价于 DROP USER 'jack'@'%'
DROP USER jack

-- 而sys的host不是%,直接删除会报错
-- Operation DROP USER failed for 'sys'@'%'
DROP USER sys
-- 删除的时候要明确的写明host的值
DROP USER 'sys'@'192.168.1.%'

32 关于MySQL的一些小练习

练习一 homework01

  1. 以下哪条语句是错误的? [D ]

    A. SELECT empno,ename name,sal salary FROM emp;
    
    B. SELECT empno,ename name,sal AS salary FROM emp;
    
    C. SELECT ename,sal*12 AS "Annual Salary" FROM emp;*
    
    D. SELECT ename,sal*12 Annual Salary FROM emp;
    
  2. 某用户希望显示补助非空的所有雇员信息,应该使用哪条语句? [B]

    A. SELECT ename,sal,comm FROM emp WHERE comm<>null;
    
    B. SELECT ename,sal,comm FROM emp WHERE comm IS NOT null;
    
    C. SELECT ename,sal,comm FROM emp WHERE comm<>0;
    
  3. 以下哪条语句是错误的? [C]

    A. SELECT ename,sal salary FROM emp ORDER BY sal:
    
    B. SELECT ename,sal salary FROM emp ORDER BY salary:
    
    C. SELECT ename,sal salary FROM emp ORDER BY 3;
    

练习二 homework02

  1. 写出 查看DEPT表和EMP表的结构 的sql语句

  2. 使用简单查询语句完成:

    1. 显示所有部门名称。
    2. 显示所有雇员名及其全年收入 13月(工资+补助),并指定列别名”年收入
  3. 限制查询数据

    1. 显示工资超过2850的雇员姓名和工资
    2. 显示工资不在1500到2850之间的所有雇员名及工资
    3. 显示编号为7566的雇员姓名及所在部门编号。
    4. 显示部门10和30中工资超过1500的雇员名及工资
    5. 显示无管理者的雇员名及岗位。
  4. 排序数据。

    1. 显示在1991年2月1日到1991年5月1日之间雇用的雇员名,岗位及雇佣日期,并以雇佣日期进行排序
    2. 显示获得补助的所有雇员名,工资及补助,并以工资降序排序
  5. 编码如下:

-- 1. 写出 查看DEPT表和EMP表的结构 的sql语句 homework02.sql
DESC dept;
DESC emp;

-- 2. 使用简单查询语句完成:
--    1. 显示所有部门名称。
SELECT dname FROM dept;

--    2. 显示所有雇员名及其全年收入 13(工资+补助),并指定列别名”年收入
SELECT ename,(sal+IFNULL (comm,0))*13 AS '年收入'
	FROM emp

-- 3. 限制查询数据
--    1. 显示工资超过2850的雇员姓名和工资
SELECT ename,sal
	FROM emp
	WHERE sal<2850
	
--    2. 显示工资不在15002850之间的所有雇员名及工资
SELECT ename,sal
	FROM emp
	WHERE NOT(sal BETWEEN 1500 AND 2850)
	-- 或者可以这样写
	-- where sal < 1500 or sal > 2850
	
--    3. 显示编号为7566的雇员姓名及所在部门编号。
SELECT ename,deptno
	FROM emp
	WHERE empno = 7566
	
--    4. 显示部门1030中工资超过1500的雇员名及工资
SELECT ename,sal
	FROM emp
	WHERE deptno IN(10,30)
	-- where (deptno = 10 or deptno = 30)
	-- 还可以这样写
	AND sal > 1500
	
--    5. 显示无管理者的雇员名及岗位。
SELECT ename,job
	FROM emp
	WHERE mgr IS NULL

-- 4. 排序数据。
--    1. 显示在199121日到199151日之间雇用的雇员名,岗位及雇佣日期,并以雇佣日期进行排序
SELECT ename,job,hiredate
	FROM emp
	WHERE hiredate BETWEEN '1991-02-01' AND '1991-05-01'
	ORDER BY hiredate
	
--    2. 显示获得补助的所有雇员名,工资及补助,并以工资降序排序
SELECT ename,sal,comm
	FROM emp
	WHERE comm IS NOT NULL
	ORDER BY sal DESC

练习三 homework03

根据: emp员工表 写出正确SQL

  1. 选择部门30中的所有员工.
  2. 列出所有办事员(CLERK)的姓名,编号和部门编号.
  3. 找出佣金高于薪金的员工.
  4. 找出佣金高于薪金60%的员工
  5. 找出部门10中所有经理(MANAGER)和部门20中所有办事员(CLERK)的详细资料
  6. 找出部门10中所有经理(MANAGER),部门20中所有办事员(CLERK),还有既不是经理又不是办事员但其薪金大于或等于2000的所有员工的详细资料.
  7. 找出收取佣金的员工的不同工作.
  8. 找出不收取佣金或收取的佣金低于100的员工
  9. 找出各月倒数第3天受雇的所有员工.
  10. 找出早于12年前受雇的员工.
  11. 以首字母小写的方式显示所有员工的姓名
  12. 显示正好为5个字符的员工的姓名.
  13. 显示不带有"R"的员工的姓名
  14. 显示所有员工姓名的前三个字符.
  15. 显示所有员工的姓名,用a替换所有"A"
  16. 显示满10年服务年限的员工的姓名和受雇日期.
  17. 显示员工的详细资料,按姓名排序.
  18. 显示员工的姓名和受雇日期,根据其服务年限,将最老的员工排在最前面
  19. 显示所有员工的姓名、工作和薪金,按工作降序排序,若工作相同则按薪金排序.
  20. 显示所有员工的姓名、加入公司的年份和月份,按受雇日期所在月排序,若月份相同则将最早年份的员工排在最前面.
  21. 显示在一个月为30天的情况所有员工的日薪金,忽略余数
  22. 找出在(任何年份的)2月受聘的所有员工
  23. 对于每个员工,显示其加入公司的天数
  24. 显示姓名字段的任何位置包含"A”的所有员工的姓名
  25. 以年月日的方式显示所有员工的服务年限.(大概)
  26. 编码如下:
-- 根据: emp员工表 写出正确SQL
-- 1. 选择部门30中的所有员工.
SELECT *
	FROM emp
	WHERE deptno = 30
	
-- 2. 列出所有办事员(CLERK)的姓名,编号和部门编号.
SELECT ename,empno,deptno
	FROM emp
	WHERE job = 'CLERK'
	
-- 3. 找出佣金高于薪金的员工.
SELECT *
	FROM emp
	WHERE IFNULL(comm,0) > sal
	
-- 4. 找出佣金高于薪金60%的员工
SELECT *
	FROM emp
	WHERE IFNULL(comm,0) > (sal*0.6)
	
-- 5. 找出部门10中所有经理(MANAGER)和部门20中所有办事员(CLERK)的详细资料
SELECT *
	FROM emp
	WHERE (deptno = 10 AND job = 'MANAGER')
	OR (deptno = 20 AND job = 'CLERK')
	
-- 6. 找出部门10中所有经理(MANAGER),部门20中所有办事员(CLERK),
--  还有既不是经理又不是办事员但其薪金大于或等于2000的所有员工的详细资料.
SELECT *
	FROM emp
	WHERE (deptno = 10 AND job = 'MANAGER')
	OR (deptno = 20 AND job = 'CLERK')
	OR (job NOT IN('MANAGER','CLERK')
	AND sal>2000)
	
-- 7. 找出收取佣金的员工的不同工作.
SELECT DISTINCT job
	FROM emp
	WHERE comm IS NOT NULL
	
-- 8. 找出不收取佣金或收取的佣金低于100的员工
SELECT ename,job
	FROM emp
	WHERE comm IS NULL
	OR IFNULL(comm,0) < 100
	
-- 9. 找出各月倒数第3天受雇的所有员工.
-- LAST_DAY() 表示日期的月份的最后一天
SELECT ename,hiredate
	FROM emp
	WHERE LAST_DAY(hiredate)-hiredate = 2
	
-- 10. 找出早于12年前受雇的员工.
SELECT ename,hiredate
	FROM emp
	WHERE DATE_ADD(hiredate,INTERVAL 12 YEAR) - NOW() < 0
	
-- 11. 以首字母小写的方式显示所有员工的姓名
SELECT CONCAT(LCASE(LEFT(ename,1)),UCASE(SUBSTRING(ename,2)))AS'姓名'
	FROM emp

-- 12.显示正好为5个字符的员工的姓名.
SELECT ename
	FROM emp
	WHERE LENGTH(ename)=5

-- 13. 显示不带有"R"的员工的姓名
SELECT ename
	FROM emp
	WHERE ename NOT LIKE '%R%'

-- 14. 显示所有员工姓名的前三个字符.
SELECT LEFT(ename,3)
	FROM emp
	
-- 15. 显示所有员工的姓名,用a替换所有"A"
SELECT REPLACE(ename, 'A','a')
	FROM emp
	
-- 16. 显示满10年服务年限的员工的姓名和受雇日期.
SELECT ename,hiredate
	FROM emp
	WHERE DATE_ADD(hiredate,INTERVAL 10 YEAR)<=NOW()
		
-- 17. 显示员工的详细资料,按姓名排序.
SELECT *
	FROM emp
	ORDER BY ename
	
-- 18. 显示员工的姓名和受雇日期,根据其服务年限,将最老的员工排在最前面
SELECT ename,hiredate
	FROM emp
	ORDER BY hiredate
	
-- 19. 显示所有员工的姓名、工作和薪金,按工作降序排序,若工作相同则按薪金排序.
SELECT ename,job,sal
	FROM emp
	ORDER BY job DESC,sal

-- 20. 显示所有员工的姓名、加入公司的年份和月份,按受雇日期所在月排序,
--      若月份相同则将最早年份的员工排在最前面.
SELECT ename,CONCAT(YEAR(hiredate),'-',MONTH(hiredate))
	FROM emp
	ORDER BY MONTH(hiredate),YEAR(hiredate)

-- 21. 显示在一个月为30天的情况所有员工的日薪金,忽略余数
-- floor 向下取整
SELECT ename,FLOOR(sal/30) ,sal/30
	FROM emp

-- 22. 找出在(任何年份的)2月受聘的所有员工
SELECT *
	FROM emp
	WHERE MONTH(hiredate) = 2

-- 23. 对于每个员工,显示其加入公司的天数
SELECT ename,DATEDIFF(NOW(),hiredate)AS'入职天数'
	FROM emp

-- 24. 显示姓名字段的任何位置包含"A”的所有员工的姓名
SELECT ename
	FROM emp
	WHERE ename LIKE '%A%'

-- 25. 以年月日的方式显示所有员工的服务年限.(大概)
-- 思路:大概估算的,平均一个月算30.5-- 1. 先求出工作了多少天
SELECT DATEDIFF(NOW(),hiredate)FROM emp;
-- 2.算出工作了多少年
FLOOR(DATEDIFF(NOW(),hiredate)/365)AS '年'
-- 3.算出工作了多少月
FLOOR(DATEDIFF(NOW(),hiredate)%365/30.5)AS '月'
-- 4.算出工作了多少天
FLOOR(DATEDIFF(NOW(),hiredate)%30.5) AS '工作天'
-- 5.然后把他们汇总到一起
SELECT ename, FLOOR(DATEDIFF(NOW(),hiredate)/365)AS '年',
	FLOOR(DATEDIFF(NOW(),hiredate)%365/30.5)AS '月',
	FLOOR(DATEDIFF(NOW(),hiredate)%30.5) AS '工作天'
	FROM emp;

练习四 homework04

根据:emp员工表,dept部门表,工资 = 薪金sal + 佣金 comm 写出正确SQL

  1. 列出至少有一个员工的所有部门1.
  2. 列出薪金比“SMITH”多的所有员工。
  3. 列出受雇日期晚于其直接上级的所有员工。
  4. 列出部门名称和这些部门的员工信息,同时列出那些没有员工的部门
  5. 列出所有“CLERK” (办事员)的姓名及其部门名称。
  6. 列出最低薪金大于1500的各种工作。
  7. 列出在部门“SALES” (销售部) 工作的员工的姓名
  8. 列出薪金高于公司平均薪金的所有员工。
  9. 列出与“SCOTT”从事相同工作的所有员工
  10. 列出薪金高于所在部门30工作的所有员工的薪金的员工姓名和薪金
  11. 列出在每个部门工作的员工数量、平均工资和平均服务期限。
  12. 列出所有员工的姓名、部门名称和工资
  13. 列出所有部门的详细信息和部门人数。
  14. 列出各种工作的最低工资。
  15. 列出MANAGER (经理)的最低薪金
  16. 列出所有员工的年工资,按年薪从低到高排序
  17. 代码如下:
-- 根据:emp员工表,dept部门表,工资 = 薪金sal + 佣金 comm  写出正确SQL 
SELECT * FROM emp
SELECT * FROM dept
-- 1. 列出至少有一个员工的所有部门
/*
	1. 先查出各个部门有多少人
	2. 使用having子句过滤
*/
SELECT COUNT(*)AS num,deptno
	FROM emp
	GROUP BY deptno
	HAVING num >= 1
	

-- 2. 列出薪金比“SMITH”多的所有员工。
/*
	1. 先算出SMITH的sal --> 然后作为子查询
	2.然后算出其他员工sal大于SMITH的即可
*/
SELECT sal
	FROM emp
	WHERE ename = 'SMITH'
	
SELECT ename,sal
	FROM emp
	WHERE sal >(
		SELECT sal
			FROM emp
			WHERE ename = 'SMITH'
	)

-- 3. 列出受雇日期晚于其直接上级的所有员工。
/*
	1.先把emp表当做两张表woker,leader
	2.然后满足两个条件
		1. woker.hiredate > leader.hiredate
		2. woker.mgr = leader.empno
*/
SELECT woker.ename AS '员工名',woker.hiredate AS '员工入职时间',
	leader.ename AS '上级名',leader.hiredate AS '上级入职时间'
	FROM emp woker,emp leader
	WHERE woker.hiredate > leader.hiredate
	AND woker.mgr = leader.empno

-- 4. 列出部门名称和这些部门的员工信息,同时列出那些没有员工的部门
/*
	因为这里要显示所有的部门
	所以我们将用到外连接
*/
SELECT dname,emp.*
	FROM dept LEFT JOIN emp
	ON emp.deptno = dept.deptno
	
-- 5. 列出所有“CLERK” (办事员)的姓名及其部门名称。
SELECT ename,dname
	FROM emp,dept
	WHERE emp.deptno = dept.deptno
	AND job = 'CLERK'

-- 6. 列出最低薪金大于1500的各种工作。
/*
	1. 先算出各个部门的最低薪金
	2. 然后用having子句过滤
*/
SELECT job,MIN(sal)AS min_sal
	FROM emp
	GROUP BY job
	HAVING min_sal > 1500

-- 7. 列出在部门“SALES” (销售部) 工作的员工的姓名
SELECT ename,dname
	FROM emp,dept
	WHERE emp.deptno = dept.deptno
	AND dname = 'SALES'

-- 8. 列出薪金高于公司平均薪金的所有员工。
/*
	1. 先算出公司的平均工资 --> 作为子查询
	2. 然后算出薪金高于平均工资的员工
*/
SELECT AVG(sal)
	FROM emp
	
SELECT ename,sal
	FROM emp
	WHERE sal > (
		SELECT AVG(sal)
			FROM emp)

-- 9. 列出与“SCOTT”从事相同工作的所有员工
/*
	1. 先查出'SCOTT'从事的工作 --> 作为子查询
	2. 然后查出跟“SCOTT”从事相同工作的所有员工
*/
SELECT job
	FROM emp
	WHERE ename = 'SCOTT'
SELECT *
	FROM emp
	WHERE job = (
		SELECT job
			FROM emp
			WHERE ename = 'SCOTT')

-- 10. 列出薪金高于所在部门30工作的所有员工的薪金的员工姓名和薪金
/*
	1.先算出在部门30工作的最高工资 --> 作为子查询
	2.然后算出薪金高于这个工资的所有员工
*/
SELECT MAX(sal)
	FROM emp
	WHERE deptno = 30
	
SELECT ename,sal
	FROM emp
	WHERE sal >(
		SELECT MAX(sal)
			FROM emp
			WHERE deptno = 30)

-- 11. 列出在每个部门工作的员工数量、平均工资和平均服务期限。
SELECT dname,COUNT(dname),AVG(sal)AS '部门员工数量',
	FORMAT(AVG(DATEDIFF(NOW(),hiredate)/365),2)AS '工作年限'
	FROM emp,dept
	WHERE emp.deptno = dept.deptno
	GROUP BY dname

-- 12. 列出所有员工的姓名、部门名称和工资
SELECT ename,dname,sal
	FROM emp,dept
	WHERE emp.deptno = dept.deptno

-- 13. 列出所有部门的详细信息和部门人数。
--   1.先算出部门人数
SELECT COUNT(*)AS num ,deptno
	FROM emp
	GROUP BY deptno
--   2.然后算出所有部门的详细信息
SELECT dept.*,tem.num AS'部门人数'
	FROM dept, (
		SELECT COUNT(*)AS num ,deptno
			FROM emp
			GROUP BY deptno
		)tem
	WHERE tem.deptno = dept.deptno
	
	
-- 14. 列出各种工作的最低工资。
SELECT dname,MIN(sal+IFNULL(comm,0))
	FROM emp,dept
	WHERE dept.deptno = emp.deptno
	GROUP BY dname
	
-- 15. 列出MANAGER (经理)的最低薪金
SELECT MIN(sal+IFNULL(comm,0))
	FROM emp
	WHERE job = 'MANAGER'

-- 16. 列出所有员工的年工资,按年薪从低到高排序
SELECT ename,(sal+IFNULL(comm,0))*12 AS year_sal
	FROM emp
	ORDER BY year_sal

练习五 homework05

设学校环境如下:一个系有若干个专业,每个专业一年只招一个班,每个班有若干个学生。

现要建立关于系、学生、班级的数据库,关系模式为:

班CLASS (班号classid,专业名subject, 系名deptname,入学年份enrolltime,人数num)

学生STUDENT (学号studentid,姓名name,年龄age,班号classid)

系 DEPARTMENT (系号departmentid,系名deptname)

试用SQL语言完成以下功能:

  1. 建表,在定义中要求声明:

    (1) 每个表的主外码

    (2) deptname是唯一约束

    (3)学生姓名不能为空

  2. 插入如下数据

    CLASS (
    101,软件,计算机,1995,20;

    102,微电子,计算机,1996,30;

    111,无机化学,化学,1995,29;

    112,高分子化学,化学,1996,25;

    121,统计数学,数学,1995,20;

    131,现代语言,中文,1996,20;

    141,国际贸易,经济,1997,30;

    142,国际金融,经济,1996,14)

    STUDENTS (
    8101,张三,18,101;

    8102,钱四,16,121;

    8103,王玲,17,131;

    8105,李飞,19,102;

    8109,赵四,18,141;

    8110,李可,20,142;

    8201,张飞,18,111;

    8302,周瑜,16,112;

    8203,王亮,17,111;

    8305,董庆,19,102;

    8409,赵龙,18,101;

    8510,李丽,20,142 )

    DEPARTMENT (

    ​ 001数学;

    ​ 002计算机:

    ​ 003化学;

    ​ 004中文;

    ​ 005经济;)

  3. 完成以下查询功能

    3.1 找出所有姓李的学生

    3.2 列出所有开设超过1个专业的系的名字

    3.3 列出人数大于等于30的系的编号和名字

  4. 学校又新增加了一个物理系,编号为006

  5. 学生张三退学,请更新相关的表

-- 设学校环境如下:一个系有若干个专业,每个专业一年只招一个班,每个班有若干个学生。
-- 现要建立关于系、学生、班级的数据库,关系模式为:
-- 班CLASS (班号classid,专业名subject, 系名deptname,入学年份enrolltime,人数num)
-- 学生STUDENT (学号studentid,姓名name,年龄age,班号classid)
-- 系 DEPARTMENT (系号departmentid,系名deptname)
-- 试用SQL语言完成以下功能:
  
-- 1. 建表,在定义中要求声明:
--    (1) 每个表的主外码
--    (2) deptname是唯一约束 
--    (3) 学生姓名不能为空

-- 第一步,先创建 系 DEPARTMENT (系号departmentid,系名deptname) 
CREATE TABLE department(
	departmentid VARCHAR(32) PRIMARY KEY,
	deptname VARCHAR(8)UNIQUE NOT NULL);
SELECT * FROM department;
DROP TABLE department

-- 第二步,创建 班CLASS (班号classid,专业名subject, 系名deptname,入学年份enrolltime,人数num)
CREATE TABLE class(
	classid INT PRIMARY KEY,
	`subject` VARCHAR(32)NOT NULL DEFAULT '',
	deptname VARCHAR(32),-- 外键字段,在表后面定义
	enrolltime INT NOT NULL DEFAULT 2000,
	num INT NOT NULL DEFAULT 0,
	FOREIGN KEY (deptname) REFERENCES department(deptname));
SELECT * FROM class;
DROP TABLE class

-- 第三步,创建 学生STUDENT (学号studentid,姓名name,年龄age,班号classid)
CREATE TABLE students(
	studentid INT PRIMARY KEY,
	`name` VARCHAR(32)NOT NULL DEFAULT '',
	age INT NOT NULL DEFAULT 0,
	classid INT,-- 外键
	FOREIGN KEY(classid) REFERENCES class(classid));
SELECT * FROM students;
DROP TABLE students


-- 2. 插入如下数据 
-- 系DEPARTMENT 的信息
/*
DEPARTMENT ( 
​	001数学;
​	002计算机:
​	003化学;
​	004中文;
​	005经济;)
*/
INSERT INTO department
	VALUES('001','数学'),
	('002','计算机'),
	('003','化学'),
	('004','中文'),
	('005','经济');
DELETE FROM department
	
-- 班级	CLASS 的信息
/*
CLASS (
	101,软件,计算机,1995,20;
	102,微电子,计算机,1996,30;
	111,无机化学,化学,1995,29;
	112,高分子化学,化学,1996,25;
	121,统计数学,数学,1995,20;
	131,现代语言,中文,1996,20;
	141,国际贸易,经济,1997,30;
	142,国际金融,经济,1996,14)
*/
INSERT INTO class
	VALUES(101,'软件','计算机',1995,20),
	(102,'微电子','计算机',1996,30),
	(111,'无机化学','化学',1995,29),
	(112,'高分子化学','化学',1996,25),
	(121,'统计数学','数学',1995,20),
	(131,'现代语言','中文',1996,20),
	(141,'国际贸易','经济',1997,30),
	(142,'国际金融','经济',1996,14);

-- 学生 STUDENTS 的信息
/*
STUDENTS (
	8101,张三,18,101;
	8102,钱四,16,121;
	8103,王玲,17,131;
	8105,李飞,19,102;
	8109,赵四,18,141;
	8110,李可,20,142;
	8201,张飞,18,111;
	8302,周瑜,16,112;
	8203,王亮,17,111;
	8305,董庆,19,102;
	8409,赵龙,18,101;
	8510,李丽,20,142 )
*/
INSERT INTO students
	VALUES(8101,'张三',18,101),
	(8102,'钱四',16,121),
	(8103,'王玲',17,131),
	(8105,'李飞',19,102),
	(8109,'赵四',18,141),
	(8110,'李可',20,142),
	(8201,'张飞',18,111),
	(8302,'周瑜',16,112),
	(8203,'王亮',17,111),
	(8305,'董庆',19,102),
	(8409,'赵龙',18,101),
	(8510,'李丽',20,142);

-- 3. 完成以下查询功能 
--    3.1 找出所有姓李的学生
SELECT *
	FROM students
	WHERE `name` LIKE '李%'
	
--    3.2 列出所有开设超过1个专业的系的名字
--	1. 先查询各个系有多少个专业
--	2.添加having子句过滤
SELECT COUNT(*)AS num,deptname
	FROM class
	GROUP BY deptname
	HAVING num > 1;

--    3.3 列出人数大于等于30的系的编号和名字
--	1. 先算出各个系有多少人 --> 临时表
SELECT SUM(num)AS nums,deptname
	FROM class
	GROUP BY deptname
	HAVING nums > 30
	
--	2. 把上面那个当做临时表,然后查出人数大于等于30的系的编号和名字
SELECT tmp.*,department.departmentid,department.deptname
	FROM department,(
		SELECT SUM(num)AS nums,deptname
			FROM class
			GROUP BY deptname
			HAVING nums > 30)tmp
	WHERE department.deptname = tmp.deptname
	
-- 4. 学校又新增加了一个物理系,编号为006
INSERT INTO department
	VALUES('006','物理系')

-- 5. 学生张三退学,请更新相关的表
-- 分析:1.张三所在班级的人数-1
-- 	2.张三从学生表删除
--	所以要进行事务控制

-- 1.开启事务
START TRANSACTION;

-- 2.张三所在班级的人数-1
UPDATE class SET num = num -1
	WHERE classid = (
		SELECT classid
			FROM students
			WHERE `name` = '张三');

-- 3.删除张三
DELETE 
	FROM students
	WHERE `name` = '张三';

-- 4.提交事务
COMMIT;

-- 5.将上面四步全部选中,直接全部执行操作
-- 将会发现students里的张三被删除了
-- class里面张三的班级号里人数少了一个

SELECT * FROM class;
SELECT * FROM students;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/644035.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

chatgpt赋能python:Introduction

Introduction 在机器学习中&#xff0c;模型的训练是非常重要的步骤之一。模型训练意味着为数据拟合合适的参数&#xff0c;以便能够准确地预测未来的值。Python是一种功能强大的编程语言&#xff0c;提供许多库和框架来训练机器学习模型。在本文中&#xff0c;我们将探讨如何…

FFmpeg编程入门

标题 播放器框架常用音视频术语解复用器和编解码器FFmpeg库简介FFmpeg有8个常用库&#xff1a; FFmpeg函数简介封装格式相关编解码器相关 FFmpeg数据结构简介关系数据结构分析 播放器框架 流程&#xff1a;媒体文件通过复用器将音频流&#xff0c;视频流&#xff0c;字幕流分离…

iCache dCache

前言 CPU 和 RAM 之间存在多级高速缓存&#xff0c;一般分为 3 级&#xff0c;分别是 L1、L2、L3。 另外&#xff0c;我们的代码都是由两部分组成的&#xff1a;指令、数据。 L1 Cache 比较特殊&#xff0c;每个 CPU 会有两个 L1 Cache&#xff0c;分别为 iCache&#xff08;指…

互联网 Java 工程师高级面试八股文汇总(1260 道题目附解析)

今年的行情&#xff0c;让招聘面试变得雪上加霜。已经有不少大厂&#xff0c;如腾讯、字节跳动的招聘名额明显减少&#xff0c;面试门槛却一再拔高&#xff0c;如果不用心准备&#xff0c;很可能就被面试官怼得哑口无言&#xff0c;甚至失去了难得的机会。 现如今&#xff0c;…

苹果将在 iOS 17 引入新功能,Safari隐私浏览有重大更新

苹果公司正在对Safari隐私浏览系统进行重大更新&#xff0c;为用户在浏览网页时提供更好的保护&#xff0c;防止第三方跟踪器。 iPhone制造商说&#xff1a;先进的跟踪和指纹保护有助于防止网站使用最新的技术来跟踪或识别用户的设备。隐私浏览现在可以在不使用时锁定&#xf…

MODIS数据下载

MODIS数据常用下载网址&#xff1a; Find Data - LAADS DAAC 在下载之前需要注册一个账号&#xff0c;才可进行下载。 1.选择数据产品&#xff0c;本人选取MOD09Q1数据产品&#xff08;250m8天合成的反射率数据&#xff09; 2.设置时间限制如下 3.找到感研究区域所在的位置&…

chatgpt赋能python:Python怎么拦截Windows浏览器的请求

Python怎么拦截Windows浏览器的请求 Python是一种流行的编程语言&#xff0c;并且被广泛用于Web开发。在这篇文章中&#xff0c;我们将深入探讨Python如何拦截Windows浏览器的请求。 什么是拦截请求? 拦截请求是指在网络传输过程中&#xff0c;对请求进行截获并进行处理的过…

C++结构体

目录 一、结构体的概念 二、结构体定义和使用 三、结构体数组 四、结构体指针 五、结构体嵌套结构体 六、结构体做函数参数 七、结构体中const使用场景 一、结构体的概念 结构体属于用户自定义的数据类型&#xff0c;允许用户存储不同的数据类型 二、结构体定义和使用 语法&…

对称二叉树(C++)

题目描述 一棵有点权的有根树如果满足以下条件&#xff0c;则被轩轩称为对称二叉树: 1. 二叉树; 2. 将这棵树所有节点的左右子树交换&#xff0c;新树和原树对应位置的结构相同且点权相等。 下图中节点内的数字为权值&#xff0c;节点外的 id 表示节点编号。 现在给出一棵二叉…

Spring 事务的相关配置、传播行为、隔离级别及注解配置声明式事务

目录 一、事务的相关配置 1. 添加测试标签 2. 添加对应方法 3. 测试 二、事务的传播行为 三、事务的隔离级别 四、注解配置声明式事务 1. 注册事务注解驱动 2. 加上注解 3. 配置类代替xml文件中的注解事务支持 4. 测试 往期专栏&文章相关导读 1. Maven系列专栏…

用了【WRITE-BUG数字空间】,其他文档软件可以卸载、注销账号了

都3202年了文档都进化成在线协同编辑文档了 让我看看谁还在用本地软件写文档啊~滋滋滋 使用【WRITE-BUG数字空间】云文档全键盘写作不是梦&#xff01;铁汁&#xff0c;听我句劝&#xff0c;把本地软件卸载了奥&#xff0c;你把握不住~ 程序员兄弟姐妹们的最爱编辑器&#x…

Zotero jasminum茉莉花插件

github地址&#xff1a;https://github.com/l0o0/jasminum 一个简单的Zotero 插件&#xff0c;用于识别中文元数据 非常感谢作者开发了这么好用的工具 安装步骤 首先要安装zotero 下载茉莉花插件安装包 https://github.com/l0o0/jasminum/releases 下载这个xpi格式的文件…

chatgpt赋能python:Python怎么抢优惠券?优惠不再是梦想!

Python怎么抢优惠券&#xff1f;优惠不再是梦想&#xff01; 在如今的消费社会&#xff0c;优惠券已成为人们购物时追逐的目标。而优惠券的数量有限且抢手&#xff0c;往往仅能在短时间内领取&#xff0c;因此初次抢到心仪的优惠券可谓令人欣喜不已。而对于程序员们而言&#…

《springboot使用篇》——只为使用,一篇就够

目录 环境&#xff1a; spring boot概述 一&#xff0c;springboot快速入门 1.创建maven项目 2.引入起步依赖 3.自定义controller 4.编写启动类 5.开始测试 二.快捷方式创建sprinboot工程 补充 三&#xff0c;配置文件 1.配置文件之间的关系 2.yml配置文件 1.基本…

【ROS】ROS+Gazebo强化学习:训练

1、安装ROS1 【ROS】Ubuntu20.04安装ROS1 2、安装Anaconda 【AI】PyTorch入门&#xff08;一&#xff09;&#xff1a;通过Anaconda安装PyTorch 【PyThon】Anaconda常用命令 3、源码下载 使用论文 Goal-Driven Autonomous Exploration Through Deep Reinforcement Learnin…

VMware虚拟机安装win10系统教程

执行本教程前请依次阅读以下2篇文章&#xff0c;完成环境准备&#xff1a; 1.VMware虚拟机下载安装教程【详细步骤 - 图文结合】 VMware虚拟机下载安装教程【详细步骤 - 图文结合】_西晋的no1的博客-CSDN博客 2.如何在微软官网下载win10镜像文件 如何在微软官网下载win10镜像文…

IDEA中Node.js环境下npm报错Error:0308010C:digital envelope routines:unsupported

Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境。 Node.js 是一个开源的、跨平台的 JavaScript 运行时环境。Node.js 使用了一个事件驱动、非阻塞式 I/O 的模型&#xff0c;使其轻量又高效。Node.js 的包管理器 npm,是目前最流行的Node.js 的包管理器。 一、安装nod…

快速幂(简单 C++)

快速幂&#xff1a;就是能够快速地计算出以 a 为底数&#xff0c;b 为指数的幂&#xff0c;相较于传统的求幂算法&#xff0c;当指数 b 非常大时&#xff0c;使用快速幂算法&#xff0c;可以大大地降低循环的次数。 以3 ^ 13 为例&#xff1a; 首先将 b 转换成二进制&#xff…

Kubernetes配置Jenkins Slave

Kubernetes配置Jenkins Slave 部署在kubernetes集群内 1、部署jenkins 1.1、命名空间 apiVersion: v1 kind: Namespace metadata:name: jenkinscilabels:app: jenkinsci1.2、Deployment apiVersion: apps/v1 kind: Deployment metadata:name: jenkinscinamespace: jenkinsci…

软考A计划-系统架构师-案例分析考前背诵-上篇

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例点击跳转>软考全系列 &#x1f449;关于作者 专注于Android/Unity和各种游戏开发技巧&#xff…