一、 数据库介绍
1.1 如何保存数据?
io操作,文件保存
数据库
-
通过io流操作文件的方式保存数据弊端:
- 效率低
- 一般只能保存小量数据
- 只能保存文本数据
1.2 什么是DB
- DataBase数据库,数据库实际上就是一个文件集合
1.3 什么是DBMS
- DataBaseManagementSystem 数据库管理系统,是用来管理数据库文件的软件
- 常见的DBMS:MySQL、Oracle、DB2、SqlServer...
1.4 数据库分类
- 关系型数据库
- 以表为单位保存数据,经过数学理论验证,关系型数据库可以保存现实生活中的任何关系
- 非关系型数据库
- 通常用于解决特定的问题
- 如:解决数据缓存问题,采用redis数据库(通过键值对保存数据)
1.5 主流关系型数据库介绍
- MySQL: 08年被Sun公司收购,09年Sun被Oracle收购,Oracle公司产品,市场占有率排名第一,开源数据库,因为oracle有可能将mysql闭源,原MySQL程序员创办了又MariaDB,该数据库拥有MySQL一样的源码,从MySQL发展而来,与MySQL完全兼容(可以理解为一样是东西)
- Oracle: 市场占有率第二,拉里.埃里森 性能最高价格最贵的数据库
- DB2:IBM公司产品 闭源收费 经常和IBM公司的其它产品结合使用
- SQLserver:微软产品 市场占有率第三,.net(C#)开发的网站一般使用SQLserver
- sqlite: 轻量级数据库,应用在嵌入式设备和手机上
1.6 开源和闭源
- 开源:公开源代码,盈利方式为卖服务,好处:技术大咖无偿维护升级
- 闭源:不公开源代码,盈利方式为卖产品和服务,劣势:技术大咖各种攻击
1.7 什么是SQL
- Structured结构化 Query查询 Language语言,用户程序员和数据库进行交互,通过sql对数据进行增删改查
- 数据库种类很多,但是一些基本的sql基本上没有太大差别。
1.8 数据库服务器
- 服务器指一台高配置的电脑
- 数据库服务器就是在一台高配置的电脑上安装了提供数据库服务的软件即数据库管理系统软件(DBMS),则这台电脑就称为数据库服务器,数据库软件本身支持网络访问功能,所以可以进行远程访问,远程增删改查
- 学习期间,为了方便测试,同时由于并不会出现生产环境中的高并发访问数据库,我们将数据库服务软件安装在自己电脑,即自己的电脑就成了一台数据库服务器(同样,类比我们学习web后端开发的时候,我们也是把web服务器搭建在自己电脑上测试)
1.9 在关系型数据库中如何存放数据的
- 一个关系型数据库管理系统中,存放着多个数据库以应对多个系统的业务,每个数据库中有多张存在着一定数据关系的表,每张表中有多个不同类型的字段。
- 一张表的数据通常可以表示生活中的一类事物数据,类似于Excel中的二维表格一样。
二、数据库操作
2.1 连接并登陆MySQL数据库
2.1.1 连接数据库有以下几种方式:
- 在命令行/终端中连接
- 数据库管理管理系统为我们提供了终端操作的最基本方式,使用难度较高,因为没有任何提示以及美好的界面
- 通过三方客户端
- 比如mysql自带的workbench、第三方Navicat,这些软件拥有良好的可视化界面,部分操作只需要点击鼠标即可,它们可以将本来需要大量命令进行的操作简化为鼠标、键盘的少量简单操作,同时也支持手动编写sql,拥有提示功能。
2.1.2 为了更熟练掌握数据库基本操作,我们学习时完全在命令行下操作
-
连接mysql数据库命令
mysql -uroot -p //表示开始mysql数据库的连接,用户是root密码是空
-
退出mysql数据库命令
exit;
2.2 数据库操作相关的SQL
- 查询并显示所有数据库
SHOW DATABASES; - 创建数据库
-
格式: CREATE DATABASE 数据库名称;
CREATE DATABASE db1;
-
- 查询数据库详情信息(这里可以看到数据库的字符集)
-
格式: SHOW CREATE DATABASE 数据库名称;
SHOW CREATE DATABASE db1;
-
- 创建数据库,同时指定字符集
-
格式: CREATE DATABASE 数据库名称 CHARACTER SET 字符编码集名;
CREATE DATABASE db2 CHARACTER SET gbk;
-
- 删除数据库
-
格式: DROP DATABASE 数据库名称;
DROP DATABASE db2;
-
- 选中数据库
-
格式:USE 数据库名;
USE db1;
-
2.3 表操作相关SQL(前提一定选中一个数据库,因为表存在于某个数据库中)
-
创建表
-
格式:CREATE TABLE 表名(字段1名 字段1类型, 字段2名 字段2类型....);
CREATE TABLE person(name VARCHAR(5), age INT);
-
-
查询所有表
SHOW TABLES;
-
查询表详情
-
格式:SHOW CREATE TABLE 表名
SHOW CREATE TABLE person;
-
-
创建表并指定引擎和字符集
-
格式:CREATE TABLE 表名(字段1名 字段1类型, 字段2名 字段2类型....) ENGINE=引擎名 CHARSET=字符编码集名;
CREATE TABLE t1(name VARCHAR(10),age INT) ENGINE=myisam CHARSET=gbk;
-
数据库表的引擎
- innodb(默认):支持数据库的高级操作如:事务,外键等
- myisam:不支持高级操作,只支持基础的增删改查操作
-
-
查看表结构(表字段)
-
格式: DESC 表名;
DESC person;
-
-
删除表
-
格式: DROP TABLE 表名;
DROP TABLE person;
-
小练习:
题目:创建三个数据库 mydb1 mydb2 mydb3 每一个数据库里面创建一个表 表名t1 t2 t3 表字段name字符串 age整数 创建完表后 再把表删除 最后把三个数据库删除
-
答案:
//创建三个数据库 mydb1 mydb2 mydb3 CREATE DATABASE mydb1; CREATE DATABASE mydb2; CREATE DATABASE mydb3; //每一个数据库里面创建一个表 表名t1 t2 t3 表字段name字符串 age整数 USE mydb1; CREATE TABLE t1(name VARCHAR(8),age INT); USE mydb2; CREATE TABLE t2(name VARCHAR(8),age INT); USE mydb3; CREATE TABLE t3(name VARCHAR(8),age INT); //删除表 USE mydb1; DROP TABLE t1; USE mydb2; DROP TABLE t2; USE mydb3; DROP TABLE t3; //删除数据库 DROP DATABASE mydb1; DROP DATABASE mydb2; DROP DATABASE mydb3;
2.4 修改表相关SQL
- 修改表名
-
格式:RENAME TABLE 原名 TO 新名;
RENAME TABLE student TO stu;
-
- 修改表引擎和字符集
-
格式:ALTER TABLE 表名 ENGINE=引擎名 CHARSET=字符编码集名;
ALTER TABLE stu ENGINE=myisam CHARSET=gbk;
-
- 添加表字段
- 在最后追加
-
格式: ALTER TABLE 表名 ADD 字段名 类型;
ALTER TABLE stu ADD chinese INT;
-
- 最前面插入添加
-
格式:ALTER TABLE 表名 ADD 字段名 类型 FIRST;
ALTER TABLE stu ADD math INT FIRST;
-
- 某个字段后面添加
-
格式:ALTER TABLE 表名 ADD 字段名 类型 AFTER 某个字段;
ALTER TABLE stu ADD english INT AFTER name;
-
- 注意:没有before用法!
- 在最后追加
- 删除表字段
-
格式:ALTER TABLE 表名 DROP 字段名;
ALTER TABLE stu DROP english;
-
- 修改表字段名称和类型
-
格式:ALTER TABLE 表名 CHANGE 原字段名 新字段名 新类型;
ALTER TABLE stu CHANGE math english INT;
-
- 修改字段类型和位置
-
格式:ALTER TABLE 表名 MODIFY 字段名 类型 FIRST/(AFTER 某字段);
ALTER TABLE stu MODIFY english INT AFTER chinese;
-
小练习:
-
题目
- 创建一个hero表 有 id 整数 name 字符串 type 字符串 三个字段
- 修改hero表的属性引擎为myisam 字符集为gbk
- 给hero表添加money字段 整数类型,添加在name的后面
- 修改type字段 名称改为 hero_type 类型改为 varchar(20)
- 修改表名hero为 heros
- 修改name字段到最后面
- 删除money字段
- 删除hero表
-
答案
1、CREATE TABLE hero(id INT,name VARCHAR(10),type VARCHAR(10)); 2、ALTER TABLE hero ENGINE=myisam CHARSET=gbk; 3、ALTER TABLE hero ADD money INT AFTER name; 4、ALTER TABLE hero CHANGE type hero_type VARCHAR(20); 5、RENAME TABLE hero TO heros; //因为目前hero_type处在最后面,所以移动到其后方即可 6、ALTER TABLE heros MODITY name VARCHAR(10) AFTER hero_type; 7、ALTER TABLE heros DROP money; 8、DROP TABLE heros;
2.5 数据相关SQL(增删改查)
CREATE TABLE hero(name VARCHAR(10),age INT);//先创建一张表用来在下面操作
-
插入数据(增)
-
全表插入(插入一整条所有字段都有值的数据)
-
格式:INSERT INTO 表名 VALUES(值1,值2...);
INSERT INTO hero VALUES('李白',30);
注意点:值的顺序必须和表中字段顺序一致
-
-
指定字段插入(插入一条数据,个别字段不添加数据)
-
格式:INSERT INTO 表名 (字段1名,字段2名...) VALUES(值1,值2...);
INSERT INTO hero (name) VALUES('关羽');
-
-
批量插入数据(一次插入多条数据):
-
全表:格式:INSERT INTO 表名 VALUES(一组值),(一组值),(一组值)...;
INSERT INTO hero VALUES('刘备',20),('关羽',19),('张飞',30)...;
-
指定字段:格式:INSERT INTO 表名 (字段名1,字段名2...) VALUES(值1.1,值1.2),(值2.1,值2.2);
INSERT INTO hero (name,age) values('悟空',11),('八戒',12),('沙僧',13)...;
-
-
-
查询数据(查)
格式:SELECT 字段名1,字段名2... FROM 表名 WHERE 条件 其他各类条件...
*
代表所有字段(*在计算机中普遍有全匹配的意思、任何事物的意思)-
代码举例:以上面的hero表为例
SELECT * FROM hero;//查询所有字段数据 SELECT name FROM hero;//仅查询name字段数据 SELECT name,age FROM hero;//查询name、age字段数据 SELECT * FROM hero WHERE age<20;//查询age值<20的所有字段数据 SELECT * FROM hero WHERE name='关羽';//查询name值为“关羽”的所有字段数据,
-
删除数据(删)一般是删除一整条数据,所以不存在删除某一字段数据的说法,字段数据可以通过修改实现
-
格式:DELETE FROM 表名 WHERE 各种条件;
DELETE FROM hero;//删除所有数据 DELETE FROM hero WHERE name='悟空';//删除hero表中name的值为“悟空”的一条数据
-
-
修改数据(改)
-
格式:UPDATE 表名 SET 字段名=值 WHERE 各种条件;
UPDATE hero SET age=100 WHERE name='八戒';//修改名字是八戒的该条数据的age字段值为100 UPDATE hero SET age=50;//把所有age字段的值修改为50
-
2.6 中文问题、出现乱码(使用命令时)
- 在命令行中执行 set names 正确的编码; 即可让终端中显示内容全部使用自己设置的正确编码
- 此操作是命令行层面的,包括其他在命令行中执行的程序若出现因为编码不对而导致的显示不正常都可以使用。
小练习:
-
题目
- 创建数据库db2 指定字符集为utf8
- 创建t_emp员工表 有id 整数,name 字符串, 两个字段
- 修改t_emp 为 emp
- 添加 工资(sal)字段类型为整数在最后面,在工资前面添加年龄字段
- 在emp表添加以下数据 1 诸葛亮 30 5000 ;2 刘备 25 8000;3 小乔 18 3000
- 修改年龄小于20岁的工资为800
- 修改诸葛亮为周瑜
- 删除工资高于5000的员工
- 删除所有数据
- 删除emp表
-
答案
CREATE DATABASE db2 CHARACTER SET utf8; USE db2; CREATE TABLE t_emp(id INT,name VARCHAR(10)); RENAME TABLE t_emp TO emp; ALTER TABLE emp ADD sal INT; //工资前面原来是name字段,所以添加在name后面即可 ALTER TABLE emp ADD age INT AFTER name; //非数字类型的数据要加''或者"" INSERT INTO emp VALUES(1,'诸葛亮',30,5000),(1,'刘备',25,8000),(3,'小乔',18,3000); UPDATE emp SET sal=800 WHERE age<20; UPDATE emp SET name='周瑜' WHERE name='诸葛亮'; DELETE FROM emp WHERE sal>5000; DELETE FROM emp; DROP TABLE emp;
三、 数据库常识
3.1 主键约束
1、 什么是主键:
- 表示表中数据唯一性的字段称为主键,唯一且非空,可以唯一区分并代表一条数据
- 举例:班级学生表中的学号,是唯一的而且不能为空,一个学号可以唯一区分并代表一个同学的一条数据。
2、 如何添加主键(主键的值必须是唯一且非空的)
在创建表的时候可以直接添加 PRIMARY KEY
-
格式: CREATE TABLE emp(主键字段 字段类型 PRIMARY KEY,字段2 字段2类型);
//建表并设置主键为id CREATE TABLE emp(id INT PRIMARY KEY,name VARCHAR(10)); //插入测试数据 INSERT INTO emp VALUES(1,'Tom');//成功 INSERT INTO emp VALUES(2,'Jerry');//成功 INSERT INTO emp VALUES(1,'abc');//失败 id不能重复 INSERT INTO emp VALUES(null,'abc');//失败 主键不能为null
3.2 主键+自增
对于我们没必要手动设置、但是又必须的用来唯一表示一条条数据的主键字段(一般是id),我们可以采用自增长的方式让数据库在添加数据时自动按照递增方式设置值。
通过添加AUTO_INCREMENT
-
格式:CREATE TABLE 表名(主键字段 字段类型 PRIMARY KEY AUTO_INCREMENT,字段2 字段2类型);
//建表并设置主键为id CREATE TABLE emp(id INT PRIMARY KEY AUTO_INCREMENT,name VARCHAR(10)); //插入测试数据 insert into t1 values(null,'刘备');//最终表中值为:1,刘备 insert into t1 (name) values('刘备');最终表中值为:2,刘备 insert into t1 values(10,'张飞');最终表中值为:10,张飞
-
知识点:
- 当自增字段的值为null、没写时会自动赋值并且数值+1
- 可以指定赋值
- 自增数值只增不减 不会因为删除数据而减少
- 在表中曾出现过的最大值的基础上+1
3.3 注释 COMMENT
对一些字段做注释,方便以后理解
-
格式:CREATE TABLE 表名(字段1 类型1 COMMENT '这是注释1', 字段2 类型2 COMMENT '这是注释2');
CREATE TABLE t2(id INT COMMENT '这是id', name VARCHAR(10) COIMMENT '这是名字');
查看方式: SHOW CREATE TABLE t2;可以查看表的设计详情里面看到注释。
3.4 `(esc下面的按键的符号)和'(单引号) 的区别
- `是用来修饰表名和字段名的 可以省略
- ' 是用来修饰字符串的, 比如再添加非数字型的值的时候,需要用它把值括起来
3.5 数据冗余
- 什么是冗余:如果表设计不够合理,随着数据量的增多,可能会出现大量的重复数据,这种重复数据成为数据冗余,可以通过拆分多个表的形式解决此问题
小练习1:请设计表保存以下数据
电视机分类下的康佳电视价格3000库存100个
电视机分类下的惠普电视价格8000库存800个
手机分类下的iphone x 价格6000 库存5个
-
手机分类下的华为 价格3000 库存10个
//设计:我们可以将这些数据保存在两张表中,一个是分类表,一个是具体商品表 //创建分类表category,字段id int,name varcher(10) CREATE TABLE category(ID INT PRIMARY KEY AUTO_INCREMENT,name VARCHER(10)); //创建商品表item,字段(id int,name 字符串,price int,num int,cid属于哪个分类/分类编号 INT) CREATE TABLE item(id INT PRIMARY KEY AUTO_INCREMENT,name VARCHER(10),price INT,num INT,cid INT); //插入数据 INSERT INTO category VALUES(null,'电视机'),(null,'手机'); INSERT INTO item VALUES(null,'康佳',3000,100,1),(null,'惠普',8000,800,1), (null,'iPhone X',6000,5,2),(null,'华为',3000,10,2);
小练习2:请设计表保存以下数据
java教学部的苍老师工资200年龄18
java教学部的传奇老师工资300年龄20
销售部的刘德华工资100年龄50
-
人事部的张学友工资80年龄30
//设计:可以将数据放入两个表:部门表、员工表 //创建部门表dept,字段 id部门编号 和 name部门名 CREATE TABLE dept(id INT PRIMARY KEY AUTO_INCREMENT,name VARCHAR(10)); - 员工表emp,字段 id员工号 name名字 sal工资 age年龄 deptid部门号 CREATE TABLE emp(id INT PRIMARY KEY AUTO_INCREMENT,name VARCHAR(10),sal INT,age INT,deptid INT); - 插入数据 INSERT INTO dept VALUES(null,'java教学部'),(null,'销售部'),(null,'人事部'); INSERT INTO emp VALUES(null,'苍老师',200,18,1),(null,'传奇老师',300,20,1),(null,'刘德华',100,50,2), (null,'张学友',80,30,3);
3.6 事务
3.6.1 案例引入:描述钱款流向,超人向钢铁侠借10000块钱
//创建人物表
CREATE TABLE person(id INT PRIMARY KEY AUTO_INCREMENT,name VARCHAR(10),money INT);
//插入两条数据,超人和钢铁侠
INSERT INTO person VALUES(null,'超人',500),(null,'钢铁侠',50000);
//超人和钢铁侠借10000块钱
//第一步 让钢铁侠-10000
UPDATE person SET money=money-10000 WHERE id=2;
//第二步 让超人+10000
UPDATE person SET money=money+10000 WHERE id=1;
- 产生问题:万一在第一步执行完以后,数据库意外推出、断电等,导致第二步未被执行,可能导致数据异常,一个钱少了,一个没拿到钱。
3.6.2 解决方案——事务
什么是事务:事务是数据库中执行SQL语句的工作单元,可以保证事务内的SQL语句要么全部成功,要么全部失败。即保证原子性。
通俗解释:将借钱归为一个事务,其中共两个操作:一方加钱一方减钱,当两个操作全部完成,事务才算完成并确认更改;否则撤回全部操作。
-
如何使用事务:
- 第一步:把数据库的自动提交改成手动提交,
- 第二步:执行多条sql语句。此时SQL会在内存中执行
- 第三步:当所有SQL在内存中执行完后 手动提交,把多次改动一次性提交到数据库文件中
-
查看数据库自动提交的状态
SHOW VARIABLES LIKE '%autocommit%';
-
关闭和打开自动提交 0或off:关闭 1或on:开启
SET AUTOCOMMIT=0或off;//关闭
-
验证事务的步骤(以案例为例):
-
在A命令窗口中执行让钢铁侠-10000
UPDATE person SET money=money-10000 WHERE id=2;
此时在A窗口中执行
SELECT * FROM person
验证数据是发现被改掉的,但是此时显示的内容是内存中的数据,真正数据库中的数据此时还未改变;(通过步骤3验证)打开新的B命令窗口执行
SELECT * FROM person
新窗口和A没有关系,此时数据查询的是数据库文件中的数据,此时可以看到数据并没有发生改变;-
在A窗口中执行让超人+10000
UPDATE person SET money=money+10000 WHERE id=1;
此时A窗口查询数据改变(内存中数据),B窗口数据还是没变(数据库文件中数据)
回到A窗口执行
COMMIT;
手动提交,此时再在B窗口查询数据,发现数据发生了改变,因为A窗口中已经把两次内存中的改动提交到了数据库文件中。
-
3.6.3 为什么使用事务?
- 当做某一件事需要执行多条SQL语句的时候(类似转账),如果不使用事务,则可能出现多条SQL部分成功部分失败,这样的结果会导致数据错乱,使用事务后可以保证多条SQL语句要么全部成功,要么全部失败。可以解决以上问题
3.6.4 事务的执行流程?
- 所谓开启事务实际上就是把数据库的自动提交关闭改成手动提交,在手动提交之前多次SQL语句的执行只会对内存中的数据进行更改,当提交的时候会把多次SQL的执行结果一次性提交到数据库文件中
3.6.5 事务回滚
- 事务回滚会把内存中的数据回滚到上次提交的点
- 设置回滚点:
SAVEPOINT 标识;
- 回滚到某个回滚点:
ROLLBACK TO 标识;
3.6.6 事务相关指令总结:
SHOW VARIABLES LIKE '%autocommit%';//查看数据库自动提交的状态
SET AUTOCOMMIT=0/off/1/on;//开关自动提交
COMMIT;//提交
SAVEPOINT s1;//设置回滚点s1
ROLLBACK TO s1;//回滚到s1
四、 SQL分类
1、DDL
- Data Definition Language 数据定义语言,包括:CREATE,ALTER,DROP,TRUNCATE。
- 不支持事务
2、DML
- Data Manipulation Language 数据操作语言,包括:INSERT DELETE UPDATE 和 SELECT(DQL)。
- 支持事务
3、DQL
- Data Query Language 数据查询语言,只有SELECT
4、TCL
- Transaction Control Language 事务控制语言,包括:COMMIT,ROLLBACK,SAVEPOINT,ROLLBACK TO xxx;
5、DCL
- Data Control Language 数据控制语言,分配用户权限相关的SQL
truncate用处
- 删除表并创建一个新表(空表)
- 格式: TRUNCATE TABLE 表名;
- TRUNCATE、DROP、DELETE区别:
- 执行效率DROP > TRUNCATE > DELETE, DROP只是删除表,TRUNCATE是删除表并创建一个空表,DELETE不能删除表、只能删除表中一条数据。
五、 数据库的数据类型
-
整数: INT(m)、BIGINT(m)。m代表显示长度,ZEROFILL(没有数字的位置上全部填充0)
CREATE TABLE t_int(num INT(10) ZEROFILL); INSERT INTO t_int VALUES(123);
-
浮点数:DOUBLE(m,d) m代表总长度,d代表小数长度。如:76.234中 m=5 d=3;
- DECIMAL(m,d) 超高精度浮点数,涉及超高精度运算时使用。
-
字符串
- CHAR(n):固定长度。n=10时,'abc' 占10,执行效率高,最大长度255
- VARCHAR(n):可变长度。n=10时,'abc' 实际占3,超过10时,会自动增加长度,更节省空间,最大长度65535,但超过255建议使用text。
- TEXT: 可变长度 最大65535。
-
日期类型
DATE: 仅保存年月日,默认值为null
TIME: 仅保存时分秒,默认值为null
DATETIME: 保存年月日和时分秒,默认值为null,最大值9999-12-31
TIMESTAMP(时间戳):保存年月日和时分秒,默认值为当前时间,最大值2038-1-19
-
练习:
CREATE TABLE t_date(d1 DATE,d2 TIME,d3 DATETIME,d4 TIMESTAMP);//建表 INSERT INTO t_date VALUES('2018-03-18',null,null,null); INSERT INTO t_date VALUES(null,'17:23:18','2018-05-15 12:18:33',null);
六、 函数
6.1 IFNULL(x,y)函数
- 例如:age=IFNULL(x,y) 如果x的值为null则赋值y 如果不为null则赋值x
- 案例:
-
将emp表中奖金为null的全部改成0
UPDATE emp SET comm=IFNULL(comm,0);
-
6.2 聚合函数(重要)
- 用来对多行数据进行统计、再计算等操作
6.2.1 求和 SUM(求和的字段名)
-
查询所有员工的工资总和
SELECT SUM(sal) FROM emp;
-
查询20号部门的工资总和
SELECT SUM(sal) FROM emp WHERE deptno=20;
6.2.2 平均值 AVG(需要取平均的字段名)
-
查询10号部门的平均工资
SELECT AVG(sal) FROM emp WHERE deptno=10;
6.2.3 最大值 MAX(字段名)
-
查询30号部门的最高工资
SELECT MAX(sal) FROM emp WHERE deptno=30;
6.2.4 最小值 MIN(字段名)
-
查询dell商品中最便宜的商品价格
SELECT MIN(price) FROM t_item WHERE title LIKE '%dell%';//用到前面模糊查询知识点
6.2.5 统计数量 COUNT(字段名/*
)
-
查询工资大于等于3000的员工数量
SELECT COUNT(*) FROM emp WHERE sal>=3000;
通常会使用
*
,因为第一,统计操作对字段没有特殊规定,第二为了避免在对字段没有规定时出现因为我们手动设置的字段出现空值而导致统计不到。
6.3 日期相关函数
-
获取当前的年月日时分秒
SELECT NOW();
-
获取当前年月日
SELECT CURDATE();
-
获取当前时分秒
SELECT CURTIME();
-
从年月日时分秒中 提取年月日 提取时分秒
//提取当前时间中的年月日、时分秒 SELECT DATE(NOW()); SELECT TIME(NOW());
-
提取时间分量 年 月 日 时 分 秒
SELECT EXTRACT(YEAR FROM NOW()); SELECT EXTRACT(MONTH FROM NOW()); SELECT EXTRACT(DAY FROM NOW()); SELECT EXTRACT(HOUR FROM NOW()); SELECT EXTRACT(MINUTE FROM NOW()); SELECT EXTRACT(SECOND FROM NOW());
-
日期格式化函数
- 格式: SELECT DATE_FORMAT(日期,format) FROM ......;
- 其中format为格式:
- %Y 四位年 %y 两位年
- %m 两位月 %c 一位月
- %d 日
- %H 24小时 %h 12小时
- %i 分
- %s 秒
- 案例:
-
把NOW()格式改成 年月日时分秒
SELECT DATE_FORMAT(NOW(),'%Y年%m月%d日%H时%i分%s秒');
-
把非标准格式转回标准格式——STR_TO_DATE(非标准时间,format)
-
14.08.2018 08:00:00 转回标准时间
SELECT STR_TO_DATE('14.08.2018 08:00:00','%d.%m.%Y %H:%i:%s');
-
-
6.4 字符串常用函数
1、 字符串拼接 CONCAT(字符串1,字符串2...);
-
拼接"你好","小白"
SELECT CONCAT('你好',‘小白’);
2、 统计字符串长度 CHAR_LENGTH(字符串);
-
统计"abc"的长度
SELECT CHAR_LENGTH('abc');
3、 字符搜索(这里的位置全部从1开始,而不是0)
- INSTR(C1,C2) 在一个字符串C1中搜索C2,返回发现C2第一次出现的位置,没有返回0
-
搜索'ab'在'ccbabc'中第一次出现的位置
SELECT INSTR('ccbabc','ab');
-
- LOCATE(C1,C2,pos) 在第二个字符串C2中搜索C1,返回发现C1第一次出现的位置,没有返回0;pos可选参数,表示从第几个位置开始找。
-
搜索'ab'在'ccbabc'中第一次出现的位置
SELECT LOCATE('ab','ccbabc');
-
4、 字符串替换
- INSERT(str1,pos,len,str2) 在str1中从第pos个字符开始删除len个字符,并从pos位置开始插入新字符串str2
-
在'aaaaa'中从第2个a开始使用'bbb'替换掉2个字符a
SELECT INSERT('aaaaa',2,2,'bbb');
-
- REPLACE(str,str1,str2) 把str中所有出现的str1替换成str2
-
把'aaabbbbccc'中的'bb'换成'dddd';
SELECT REPLACE('aaabbbbccc','bb','dddd');
-
5、 大小写全转换 UPPER(被转字符串);LOWER(被转字符串);
6、 字符串截取
- LEFT(str,len); RIGHT(str,len); 从左/y右开始截取str中len个字符;
- SUBSTRING(str,pos,len); 从str的第pos个字符开始截取len个字符,pos可选,pos过大、没写默认截取到最后。
- SUBSTRING(str FROM pos FOR len);同上
7、 去空格
- TRIM(str);去两头空格
- RTRIM(str);去右侧空格
- LTRIM(str); 去左侧空格
8、 自动重复叠加字符串 REPEAT(str,COUNT) 把str重复COUNT次并且拼接在一起返回
-
需要一个连续出现100次'aa'的字符串
SELECT REPEAR('aa',100);
9、 字符串反转 REVERSE(str);
-
把'abc'反转
SELECT REVERSE('abc');//得到cba
6.5 数学相关函数
-
FLOOR(num) 向下取整
SELECT FLOOR(3.84); 值为3
-
ROUND(num) 四舍五入
SELECT ROUND(23.8); 值为24
-
ROUND(num,m) 四舍五入 m代表小数位数
SELECT ROUND(23.869,2); 值为23.87
-
TRUNCATE(num,m) 和上面一样(非四舍五入)
SELECT TRUNCATE(23.8697,2); 值为23.86,直接砍掉后面多余位
-
RAND() 随机数 获取0-1的随机数,前闭后开
SELECT RAND();
-
获取 0-5的随机数
SELECT RAND()*5;
-
获取 3-8的随机数整数
SELECT FLOOR(RAND()*6)+3;
-
获取 8-10的随机整数
SELECT FLOOR(RAND()*3)+8;
-
七、 各类高级查询操作(重点)
7.1 is null和is not null
- 字段是不是空,用在条件中
-
查询没有上级领导mgr的员工编号empno姓名ename和工资sal
SELECT empno,ename,sal FROM emp WHERE mgr IS NULL;
-
查询emp表中有奖金comm的员工信息(全字段)
SELECT * FROM emp WHERE comm IS NOT NULL;
-
7.2 比较运算符<,>,<=,>=,!=/<>,=
-
<
、<=
小于、小于等于-
查询工资sal小于等于1600员工的姓名eneme工资sal
SELECT ename,sal FROM emp WHERE sal<=1600;
-
-
>
、>=
大于、大于等于-
查询工资sal大于1500的员工信息
SELECT * FROM emp WHERE sal>1500;
-
-
=
,!=
,<>
等于,不等于-
查询部门编号是20的员工姓名ename职位job工资sal
SELECT ename,job,sal FROM emp WHERE deptno=20;
-
查询职位是manager的所有员工姓名ename职位job部门编号deptno
SELECT ename,job,deptno FROM emp WHERE job='manager'
-
查询不是10号部门的员工姓名ename部门编号deptno
SELECT ename,deptno FROM emp WHERE deptno!=10; SELECT ename,deptno FROM emp WHERE deptno<>10;
-
7.3 别名
- 给字段取别名,显示在结果中
- 方式:
- SELECT ename as '姓名',sal as '工资' FROM emp;
- SELECT ename '姓名',sal '工资' FROM emp;
- SELECT ename 姓名,sal 工资 FROM emp;
7.4 去重 DISTINCT
- 比如员工表中,每个员工的工作会有重复,当我们只需要查看有哪些
-
查询员工从事的所有职业
SELECT DISTINCT job FROM emp;
-
查询有员工的部门编号
SELECT DISTINCT deptno FROM emp;
-
7.5 AND 和 OR
- 当有多个条件的时候使用
- AND和java中的&&效果一样
- or和java中的||效果一样
-
例一、查询10号部门工资高于3000块钱的员工信息
SELECT * FROM emp WHERE deptno=10 AND sal>3000;
-
例二、查询部门编号为30或者上级领导为7698的员工姓名,职位,上级领导和部门编号
SELECT ename,job,mgr,deptno FROM emp WHERE deptno=30 or mgr=7698;
-
7.6 IN
- 查询条件中,要求某个字段的值在规定的几个值范围中可以使用in
-
例、查询工资为5000或1500或3000的员工信息
SELECT * FROM emp WHERE sal=5000 OR sal=1500 OR sal=3000;//法1 SELECT * FROM emp WHERE sal IN (5000,1500,3000);//法2
-
7.7 (NOT) BETWEEN AND
- (NOT) BETWEEN x AND y——(不)在x和y之间 注意:包含xy
-
查询工资在2000到4000之间的员工信息
SELECT * FROM emp WHERE sal BETWEEN 2000 AND 4000;
-
查询工资不在2100到2800之间的员工姓名和工资
SELECT ename,sal FROM emp WHERE sal NOT BETWEEN 2100 AND 2800;
-
查询10号部门工资在2000到3000之间的员工信息
SELECT * FROM emp WHERE deptno=10 AND sal BETWEEN 2000 AND 3000;
-
7.8 模糊查询 (NOT) LIKE
- 用法:
- _ 代表单个未知字符
- % 代表0或多个未知字符
- 举例:
- 以a开头
LIKE 'a%'
- 以a结尾
LIKE '%a'
- 包含a
LIKE '%a%'
- 第二个字母是a
LIKE '_a%'
- 倒数第三个字符是a
LIKE '%a__'
- 第二个字母是a最后字母是b
LIKE '_a%b'
- 以a开头
- 案例:
-
查询标题title中包含记事本的商品标题
SELECT title FROM t_item WHERE title LIKE '%记事本%';
-
查询单价低于100的记事本 标题和单价price
SELECT title,price FROM t_item WHERE price<100 AND title LIKE '%记事本%';
-
查询单价在50到200之间的得力(title包含得力)商品标题和单价
SELECT title,price FROM t_item WHERE price BETWEEN 50 AND 200 AND title LIKE '%得力%';
-
查询有图片(image字段不等于null)的得力商品信息
SELECT * FROM t_item WHERE image IS NOT NULL AND title LIKE '%得力%';
-
查询有赠品的商品信息(sell_point字段包含赠字)
SELECT * FROM t_item WHERE sell_point LIKE '%赠%';
-
商品标题中不包含得力的商品
SELECT * FROM t_item WHERE title NOT LIKE '%得力%';
-
7.9 排序 ORDER BY
- 在查询完数据后若要按照某一个字段的值对数据重新排列,类似于excel里面依据某一列数值大小(就像按照考试分数排序),可以使用
ORDER BY 字段名
进行排序。 - 书写位置:
- 查询语句中,如果有条件,写在条件的后面——
SELECT * FROM 表 WHERE 条件 ORDER BY 字段
- 如果没条件,写在表名的后面——
SELECT * FROM 表 ORDER BY 字段
- 默认是升序ASC,若要降序排序使用DESC降序——
SELECT * FROM 表 ORDER BY 字段 ASC/DESC
- 查询语句中,如果有条件,写在条件的后面——
- 案例:
-
查询员工姓名和工资按照工资的降序排序
SELECT ename,sal FROM emp ORDER BY sal DESC;
-
查询所有的dell商品信息(title包含dell) 按照单价降序排序
SELECT * FROM t_item WHERE title LIKE '%dell%' ORDER BY price DESC;
-
- 多字段排序
类比excel中,先按照成绩排序,若成绩一样按照学号排序
格式:在ORDER BY后面加字段以及排序方式,用逗号隔开即可
-
案例:查询员工信息 按照部门编号降序如果编号相同则按照工资升序排序
SELECT * FROM emp ORDER BY deptno DESC,sal;
7.10 分页查询 LIMIT
- SELECT ......最后面 LIMIT x,y;
- 第一个参数x代表查询时跳过的n条数,即从满足条件的n+1条数据开始查找;
- 第二个参数y代表每页的数量,即一次查询得到的数据条数
- LIMIT 关键字通常写在sql语句的最后面
- 案例
-
查询所有商品按照单价升序排序 显示第二页 每页7条数据
SELECT * FROM t_item ORDER BY price LIMIT 7,7;
-
查询工资前三名的员工姓名和工资
SELECT ename,sal FROM emp ORDER BY sal DESC LIMIT 0,3;
-
7.11 数值计算 + - * / %
- +加 -减 *乘 /除
- x%y——x对y取余,还可以用mod(x,y),x对y取余
- 案例:
-
查询所有员工的姓名,工资及年终奖(工资的5倍)
SELECT ename,sal,sal*5 年终奖 FROM emp;//这里用到一个前面的知识点,起别名年终奖
-
查询商品表中商品单价,库存,及总金额(单价x库存)
SELECT price,num,price*num FROM t_item;
-
7.12 分组查询 GROUP BY
比如,我想统计每个部门有多少人,这时候我们应该按照部门号进行分组,分别统计COUNT多少人
分组查询通常和聚合函数结合使用
一般情况下 每个部门(职位、分类) 就以部门(职位、分类)作为分组的条件
可以有多个分组条件,比如每个部门中每个小组多少人,这时候要两次分组,在ORDER BY后面将字段隔开即可
-
一些案例:
-
查询每个部门的最高工资
SELECT deptno,MAX(sal) FROM emp GROUP BY deptno;
-
查询每个职位的平均工资
SELECT job,AVG(sal) FROM emp GROUP BY job;
-
查询每个部门下每个主管的手下人数
SELECT deptno,mgr,COUNT(*) FROM emp GROUP BY deptno,mgr;
-
-
GROUP BY 通常在查询语句中存在的位置:
SELECT * FROM emp WHERE ...... GROUP BY ...//在这里 ORDER BY ... LIMIT ...
-
提高练习
-
案例:查询emp表中每个部门的编号,人数,工资总和,最后根据人数进行升序排列,如果人数一致,根据工资总和降序排列。
SELECT deptno,COUNT(*) 人数,SUM(sal) 工资总和 FROM emp GROUP BY deptno ORDER BY 人数,工资总和 desc;
-
案例:在emp表中查询工资在1000~3000之间的员工所在的每个部门的编号,平均工资,最低工资,最高工资,根据平均工资进行升序排列。
SELECT deptno,AVG(sal),MIN(sal),MAX(sal) FROM emp WHERE sal BETWEEN 1000 AND 3000 GROUP BY deptno ORDER BY AVG(sal);
-
案例:查询含有上级领导的员工所工作的每个职业的人数,工资的总和,平均工资,最低工资,最后根据人数进行降序排列,如果人数一致,根据平均工资进行升序排列
SELECT job,COUNT(*) 人数,SUM(sal),AVG(sal) 平均工资,min(sal) FROM emp WHERE mgr IS NOT NULL GROUP BY job ORDER BY 人数 desc,平均工资;
-
7.13 HAVING
- 由于WHERE不可以使用在聚合函数上
- 使用HAVING 解决聚合函数的条件过滤问题,HAVING写在GROUP BY 后面并必须与GROUP BY结合使用
- 案例:查询每个部门的平均工资,要求平均工资大于2000。
-
以下为错误语法: WHERE后面不能写聚合函数
SELECT deptno,AVG(sal) a FROM emp WHERE a>2000 GROUP BY deptno;//错误的!!!
-
正确写法:
SELECT deptno,AVG(sal) a FROM emp GROUP BY deptno HAVING a>2000;
-
- WHERE后面只能写普通字段的过滤条件,HAVING后面写聚合函数的过滤条件
- HAVING一般也不能脱离GROUP BY单独使用,即使语法不报错,查询结果也会出错
- 一些习题:
查询每个分类下的商品的库存总量,要求分类下所有商品总库存大于199999
查询每个分类下的商品的平均单价,要求分类下的商品平均单价低于100
查询分类id为238和917两个分类的平均单价
查询emp表中每个部门的平均工资高于2000的部门编号,部门人数,平均工资,最后根据平均工资降序排序
查询emp表中每个部门中员工工资在1000-3000之间的每个部门的编号,工资总和,平均工资,过滤掉平均工资低于2000的部门,按照平均工资升序排序
查询emp表中员工名字不是以s开头的每个职位的名字,人数,工资总和,最高工资,过滤掉平均工资是3000的职位,根据人数升序排序,如果一致根据工资总和降序排序
-
查询emp表 每年入职的人数(提高题)
//1 SELECT category_id,SUM(num) total FROM t_item GROUP BY category_id HAVING total>199999; //2 SELECT category_id,AVG(price) p_avg FROM t_item GROUP BY category_id HAVING p_avg<100; //3 SELECT category_id,AVG(price) FROM t_item WHERE category_id IN(238,917) GROUP BY category_id; //4 SELECT deptno,COUNT(*),AVG(sal) sal_avg FROM emp GROUP BY deptno HAVING sal_avg>2000 ORDER BY sal_avg desc; //5 SELECT deptno,SUM(sal),AVG(sal) sal_avg FROM emp WHERE sal BETWEEN 1000 AND 3000 GROUP BY deptno HAVING sal_avg>=2000 ORDER BY sal_avg; //6 SELECT job,COUNT(*) a,SUM(sal) b,MAX(sal) FROM emp WHERE ename NOT LIKE 's%' GROUP BY job HAVING AVG(asl)!=3000 ORDER BY a,b DESC; //7 SELECT EXTRACT(YEAR FROM HIREdate) year,COUNT(*) FROM emp GROUP BY year
7.14 子查询(嵌套查询)
7.14.1 需求
- 当条件中的数据需要通过另一次查询得到,比如查询和小明分数相同的同学的分数,就需要先查小明的分数,再利用查出来的分数比较查询其他数据,这时候会出现查询语句的嵌套,即子查询。
7.14.2 概念
- 嵌套在SQL语句中查询语句称为子查询
- 子查询能嵌套n层
- 子查询可写的位置:
- 可以写在 WHERE/HAVING的后面作为查询条件的值
-
查询和JONES在同一部门的员工名字
SELECT ename FROM emp WHERE deptno=(SELECT deptno FROM emp WHERE ename='JONES');
-
- 可以写在 FROM 后面 当一张新表 新表必须有别名
-
查询工资大于1000的员工的所有名字,别名是newtable
SELECT ename FROM (SELECT * FROM emp WHERE sal>1000) newtable;
-
- 可以写在创建表的时候,即将查询出来的结果创建成新表并保存
-
创建一张名为t_emp_10的表,其数据是部门id为10的所有员工信息
CREATE TABLE t_emp_10 AS (SELECT * FROM emp WHERE deptno=10);
-
- 可以写在 WHERE/HAVING的后面作为查询条件的值
7.14.3 案例
-
题目
- 查询emp表中工资最高的员工信息
- 查询emp表中工资超过平均工资的所有员工信息
- 查询工资高于20号部门平均工资的员工信息
- 查询和JONES相同工作的其它员工信息
- 查询和工资最低的员工在相同部门的所有员工信息
- 查询最后入职的员工信息
- 查询姓名为king的部门编号和部门名称(需要使用dept表)
- 查询商品表(
t_item
)中所有商品的分类的商品分类id和分类名称,并要求这些分类存在于分类表t_item_category
中 - 查询所有员工的部门信息
- 扩展题(难度最高):查询平均工资最高的部门信息
-
答案
//1、查询emp表中工资最高的员工信息 //先查最高工资a SELECT MAX(sal) a FROM emp; //再查工资等于a的员工信息即可 SELECT * FROM emp WHERE sal=a; //最终答案,两条合并为一句: SELECT * FROM emp WHERE sal=( SELECT MAX(sal) FROM emp; ); //2、查询emp表中工资超过平均工资的所有员工信息 //先查平均工资a SELECT AVG(sal) a FROM emp; //再查工资>a的所有员工信息 SELECT * FROM emp WHERE sal=a; //最终答案,合并即可: SELECT * FROM emp WHERE sal=( SELECT AVG(sal) FROM emp ); //3、查询工资高于20号部门平均工资的员工信息 //查询20号部门平均工资a SELECT AVG(sal) a FROM emp WHERE deptno=20; //查询工资高于a的员工信息 SELECT * FROM emp WHERE sal>a; //合并: SELECT * FROM emp WHERE sal>( SELECT AVG(sal) FROM emp WHERE deptno=20 ); //4、查询和JONES相同工作的其它员工信息 SELECT * FROM emp WHERE job=( SELECT job FROM emp WHERE ename='JONES' ) AND ename!='JONES'; //5、查询和工资最低的员工在相同部门的所有员工信息 SELECT * FROM emp WHERE deptno=( SELECT deptno FROM emp WHERE sal=( SELECT MIN(sal) FROM emp ) ); //注意点再强调:这里部分人可能错误认为,既然having是用在聚合函数产生条件时候的,那么应该可以使用 SELECT deptno FROM emp HAVING sal=MIN(sal)。这里虽然语法不会报错但是,结果大错特错!是错误用法。 HAVING 要和 GROUP BY 结合使用 //6、查询最后入职的员工信息 SELECT * FROM emp WHERE hiredate=( SELECT MAX(hiredate) FROM emp ); //7、查询姓名为KING的员工的部门编号和部门名称(需要使用dept表) SELECT deptno,dname FROM dept WHERE deptno=( SELECT deptno FROM emp WHERE ename='KING' ); //8、查询商品表(t_item)中所有商品的分类的商品分类id和分类名称, 并要求这些分类存在于分类表t_item_category中 SELECT id,name FROM t_item_category WHERE id in( SELECT DISTINCT category_id FROM t_item_category ); //9、查询所有员工的部门信息 SELECT * FROM dept WHERE deptno in( SELECT DISTINCT deptno FROM emp; ); //10、扩展题(难度最高):查询平均工资最高的部门信息 //查询最高平均工资a SELECT AVG(sal) a FROM emp GROUP BY deptno ORDER BY a DESC LIMIT 0,1 //根据最高平均工资a,得到平均工资最高的部门id SELECT deptno id FROM emp GROUP BY deptno HAVING AVG(sal)=(a) //根据部门ID得到部门信息 SELECT * FROM dept WHERE deptno in(id); //最终答案,嵌套起来即可: SELECT * FROM dept WHERE deptno in( SELECT deptno FROM emp GROUP BY deptno HAVING AVG(sal)=( SELECT AVG(sal) a FROM emp GROUP BY deptno ORDER BY a DESC LIMIT 0,1 ) );
7.15 关联查询
- 同时查询多张表的查询方式称为关联查询
- 关联查询的查询方式: 等值连接、内连接、外连接
7.15.1案例需求:
- 查询每一个员工的名称和其对应的部门名称
- 查询在new york工作的所有员工的信息
-
查询的数据的分布在两张表中,我们人去找数据,肯定会先在emp里面找员工,然后根据员工对应的部门编号再到另一张dept表里面找出部门名称;同样数据库也是这样找的,所以这里的部门号就是成了两张表建立关联关系的必要字段,体现在查询语句中,就如下面的查询语句。
//1 SELECT emp.ename,dept.dname FROM emp,dept WHERE emp.deptno=dept.deptno; //2 SELECT e.* FROM emp e,dept d WHERE e.deptno=d.deptno AND d.loc='new york';
-
7.15.2 笛卡尔积
1、 通俗的抽象概念:一一配对
-
两张测试表如下:
- a表的aid字段和b表的bstr字段的笛卡尔积:
-
体现在查询上就是:
SELECT a.Aid,b.Bstr FROM a,b;
-
- a表和b表的笛卡尔积(a、b表全字段的笛卡尔积):
-
体现在查询上就是:
SELECT * FROM a,b;
-
- 如果关联查询不写关联关系则查询到的数据是两张表的乘积,这个乘积称为笛卡尔积,如上面的例子
- 笛卡尔积是一种错误查询方式的结果,工作中切记不要出现,为什么错误?如上面的查询,显然在实际生活中是没有意义的,查询出来的结果并没有条件过滤,仅仅是拼接,基本不会符合我们的需求。
7.15.3 内连接(等值连接、自然连接)
-
仅需要查询两张表被查字段的交集,可以使用内连接
-
查询每一个员工的名称和其对应的部门名称
SELECT emp.id,dept.name FROM emp JOIN dept ON emp.deptno=dept.deptno;
-
-
等值连接:也是内连接,当两个表中有相同意义的字段,即有关联关系的字段(但是字段名字不同)
-
查询每一个员工的名称和其对应的部门名称
SELECT emp.id,dept.name FROM emp,dept WHERE emp.deptno=dept.deptno;
-
自然连接:也是内连接,他要求关联关系的两个字段名相同。自然连接还会去掉重复的关联字段
7.15.4 外连接
- 使用外连接查询得到的数据是除了两张表的被查字段交集数据以外和另外一张主表的被查字段的全部数据,哪个表为主表通过 left/right控制 left是以join左边表为主表 right以join右边的表为主表(即左外连接和右外连接)
-
查询所有员工的名称和其对应的部门名字
SELECT emp.id,dept.name FROM emp RIGHT JOIN dept ON emp.deptno=dept.deptno; //由于采用右外连接,部门表是主表,部门表中的所有部门名都会被查出来(即使有部门并没有员工)
-
7.15.5 关联查询总结:
- 如果查询两张表的交集数据使用 等值连接或内连接(推荐)
- 如果要查询某一张表的被查字段的全部数据和另外一张表的被查字段的交集数据此时使用外连接
八、数据库设计
8.1 表设计——关联关系
8.1.1 主键和外键
- 主键: 用来表示数据唯一性的字段称为主键
- 外键: 用来建立关系的字段称为外键(此部分暂不详细讲解、使用外键)
- 通常,外键是另一张与自己有关联关系的表中的主键
8.1.2 一对一关系
- 有AB两张表,A表中的一条数据对应B表中的一条数据同时B表一条对应A表一条,这种关系称为一对一
- 举例:全国结婚登记丈夫表,全国结婚登记夫人表,一个丈夫只能有一个夫人,同样一位夫人只能对应一个丈夫
- 如何建立关系: 在从表中添加外键,外键的值指向主表的主键(此部分暂不详细讲解、使用外键)
- 练习:
- 请设计表保存以下数据(虽然可以保存在一张表中,但是为了演示,要求保存到两张表中)
用户名:wukong 密码:123456 昵称:齐天大圣 电话:13733666633 地址:花果山
用户名:bajie 密码:abcd 昵称:二师兄 电话:13833446622 地址:高老庄
-
用户名:libai 密码:aabbcc 昵称:李白 电话:13355668877 地址:语文书里
//创建用户表 CREATE TABLE user( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(10), password VARCHAR(10) ); //创建用户信息表 CREATE TABLE userinfo( userid INT, nick VARCHAR(10), tel VARCHAR(15), address VARCHAR(20) ); //插入数据 INSERT INTO user VALUES (null,'wukong','123456'), (null,'bajie','bacd'), (null,'libai','aabbcc'); INSERT INTO userinfo VALUES (1,'齐天大圣','13833446622','花果山'), (2,'二师兄','13833446622','高老庄'), (3,'李白','13833446622','语文书里');
- 完成以下查询:
-
查询李白的用户名和密码是什么
SELECT u.username,u.password FROM user u JOIN userinfo ui ON u.id=ui.userid WHERE ui.nick='李白';
-
查询每一个用户的所有信息
SELECT * FROM user u JOIN userinfo ui ON u.id=ui.userid;
-
查询用户名bajie 的昵称是什么
-
查法1,嵌套查询:
SELECT nick FROM userinfo WHERE userid=( SELECT id FROM user WHERE username='bajie' );
-
查法2,普通内连接:
SELECT userinfo.nick FROM user JOIN userinfo ON user.id=userinfo.userid WHERE user.username='bajie';
-
查法3,等值连接:
SELECT userinfo.nick FROM user,userinfo WHERE user.id=userinfo.userid AND user.username='bajie';
-
-
- 请设计表保存以下数据(虽然可以保存在一张表中,但是为了演示,要求保存到两张表中)
8.1.3 一对多关系
- AB两张表,A表中的一条数据对应B表中的多条数据,同时B表中的一条数据对应A表中的一条数据,称为一对多
- 应用举例: 员工表-部门表,一个员工只在一个部门工作,一个部门可以有多个员工
- 如何建立关系: 在多的一端添加外键指向另外一张表的主键 (此部分暂不详细讲解、使用外键)
- 练习:
- 创建表保存以下数据 (提示:Aemp员工表、Adept部门表)
名字:悟空 年龄:28岁 月薪:3000月薪 部门名:神仙部门 部门地址:花果山
名字:刘备 年龄:34岁 月薪:8000月薪 部门名:三国部门 部门地址:蜀国
名字:路飞 年龄:18岁 月薪:1000月薪 部门名:海贼部门 部门地址:日本
-
名字:八戒 年龄:30岁 月薪:4000月薪 部门名:神仙部门 部门地址:花果山
CREATE TABLE Aemp( eid INT PRIMIARY KEY AUTO_INCREMENT, ename VARCHAR(10), age INT, sal INT, deptno,INT ); CREATE TABLE Adept( deptno INT PRIMIARY KEY AUTO_INCREMENT, dname VARCHAR(10),dlocation VARCHAR(10) ); INSERT INTO Aemp VALUES (null,'悟空',28,3000,1), (null,'刘备',34,8000,2), (null,'路飞',18,1000,3), (null,'八戒',30,4000,1); INSERT INTO Adept VALUES (null,'神仙部','花果山'), (null,'三国部','蜀国'), (null,'海贼部','日本');
- 完成以下查询:
-
查询每个员工的姓名和部门名
SELECT e.ename,d.dname FROM Aemp e JOIN Adept d ON e.deptno=d.deptno;
-
查询工作在花果山的员工姓名及工资
SELECT e.ename,e.sal FROM Aemp e JOIN Adept d ON e.deptno=d.deptno WHERE d.loc='花果山';
-
- 创建表保存以下数据 (提示:Aemp员工表、Adept部门表)
8.1.4 多对多
- AB两张表,A表中一条数据对应B表中多条数据同时B表中一条数据对应A表中多条,称为多对多
- 应用场景: 老师-学生,一位老师可以带多个学生,一个学生可以上多个老师课
- 如何建立关系:需要创建新的关系表,表中添加两个外键,分别指向两个主表的主键(此部分暂不详细讲解、使用外键)
- 练习:
- 创建表保存以下数据
唐僧的学生有:悟空,传奇哥
-
苍老师的学生有: 传奇哥,克晶姐
//创建老师表 CREATE TABLE teacher( Tid INT PRIMARY KEY AUTO_INCREMENT, Tname VARCHAR(10) ); //创建学生表 CREATE TABLE student( Sid INT PRIMARY KEY AUTO_INCREMENT, Sname VARCHAR(10) ); //创建关联关系表 CREATE TABLE teac_stu(Tid INT,Sid INT); //插入数据 INSERT INTO teacher VALUES(null,'唐僧'),(null,'苍老师'); INSERT INTO student VALUES(null,'悟空'),(null,'传奇哥'),(null,'克晶姐'); INSERT INTO teac_stu VALUES(1,1),(1,2),(2,2),(2,3);
- 完成以下查询:
-
查询苍老师的学生姓名
SELECT s.Sname FROM student s JOIN teac_stu ts ON s.Sid=ts.Sid JOIN teacher t ON t.Tid=ts.tid WHERE t.Tname='苍老师';
-
查询传奇哥的老师姓名
SELECT t.Tname FROM teacher t JOIN teac_stu ts ON t.Tid=ts.Tid JOIN student s ON ts.Sid=s.Sid WHERE s.Sname='传奇哥';
-
- 创建表保存以下数据
8.2 表设计——权限管理案例
创建三张主表用户user(id,name)、角色role(id,name)、权限module(id,name)和两张关系表ur(uid,rid)(用户和角色)、rm(rid,mid)(角色和权限)
-
创建:
CREATE TABLE user(id INT PRIMARY KEY AUTO_INCREMENT,name VARCHAR(10)); CREATE TABLE role(id INT PRIMARY KEY AUTO_INCREMENT,name VARCHAR(10)); CREATE TABLE module(id INT PRIMARY KEY AUTO_INCREMENT,name VARCHAR(10)); CREATE TABLE ur(uid INT,rid INT); CREATE TABLE r_m(rid INT,mid INT);
-
保存以下数据:
用户表:刘德华,貂蝉
角色表:男游客,男管理员,女游客,女会员
权限表:男浏览,男发帖,男删帖,女浏览,女发帖
对应关系:男游客->男浏览;男管理员->男浏览,男发帖,男删帖;女游客->女浏览;女会员->女浏览,女发帖
-
刘德华是男管理员和女游客;貂蝉是女会员和男游客
INSERT INTO user VALUES(null,'刘德华'),(null,'貂蝉'); INSERT INTO role VALUES(null,'男游客'),(null,'男管理员'),(null,'女游客'),(null,'女会员'); INSERT INTO module VALUES(null,'男浏览'),(null,'男发帖'),(null,'男删帖'),(null,'女浏览'),(null,'女发帖'); INSERT INTO rm VALUES(1,1),(2,1),(2,2),(2,3),(3,4),(4,4),(4,5); INSERT INTO ur VALUES(1,2),(1,3),(2,4),(2,1);
-
查询练习:
-
查询每个用户对应的所有权限
SELECT u.name,m.name FROM user u JOIN u_r ur ON u.id=ur.uid JOIN r_m rm ON ur.rid=rm.rid JOIN module m ON rm.mid=m.id;
-
查询刘德华的所有权限
SELECT m.name FROM user u JOIN u_r ur ON u.id=ur.uid JOIN r_m rm ON ur.rid=rm.rid JOIN module m ON rm.mid=m.id WHERE u.name='刘德华';
-
查询拥有男浏览权限的用户都是谁
SELECT u.name FROM user u JOIN u_r ur ON u.id=ur.uid JOIN r_m rm ON ur.rid=rm.rid JOIN module m ON rm.mid=m.id WHERE m.name='男浏览';
-
九、 视图
9.1 概念
- 什么是视图: FROM
- 数据库中存在的表和视图都是其内部的对象,视图可以理解成是一个虚拟的表,数据来自原表,视图本质上就是取代了一段sql语句(通俗理解:视图的数据来自数据库表,而非自己保存数据;数据怎么来?通过查询表来;那么视图相当于封装了查询这些数据的语句,下次通过视图查到的数据实际上来自于视图本身所代表的查询语句从真实表中查来的数据)
- 为什么使用视图:
- 简化查询。因为有些数据查询的SQL语句比较长,每次书写比较麻烦,使用视图可以起到sql语句重用的作用,提高开发效率
- 更加安全。由于不存数据、数据来源是真实表中的部分数据,可以隐藏敏感信息
- 创建视图
- 格式: CREATE VIEW 视图名 as 子查询;
- 举例:
-
创建10号部门员工的视图
CREATE VIEW v_emp_10 AS (SELECT * FROM emp WHERE deptno=10);
-
创建没有工资的员工的信息
CREATE VIEW v_emp_nosal AS (SELECT ename,job,deptno,mgr FROM emp);
-
创建部门为20号部门并且工资小于3000的视图
CREATE VIEW v_emp_20 AS (SELECT * FROM emp WHERE deptno=20 AND sal<3000);
-
创建每个部门平均工资,工资总和,最大工资,最小工资的视图
CREATE VIEW v_emp_info AS (SELECT deptno,AVG(sal),SUM(sal),MAX(sal),MIN(sal) FROM emp GROUP BY deptno);
-
9.2 视图分类
- 简单视图: 创建视图的子查询中 不包含去重、函数、分组、关联查询的视图称为简单视图,简单视图可以对数据进行增删改查操作
- 复杂视图: 包含去重、函数、分组、关联查询的视图称为复杂视图,一般只进行查询操作。
9.3 操作视图中数据(对视图中的数据进行增删改查)
- 视图的操作方式和表的方式一样(操作视图会修改原表数据)
-
插入数据(以上面的10号部门员工的视图为例)
INSERT INTO v_emp_10 (empno,ename,deptno) VALUES (10001,'钢铁侠',10); //原表中有插入的数据 视图中也有插入的数据 INSERT INTO v_emp_10 (empno,ename,deptno) VALUES (10002,'葫芦娃',20); //原表中有插入的数据 视图中没有插入的数据(因为这个视图只显示10号部门员工,我们却插入了20号)
数据污染:往视图中插入一条视图中不可见但是在原表中已经存在的数据
解决数据污染: 在创建视图的时候添加
WITH CHECK OPTION
关键字-
例子:
CREATE VIEW v_emp_30 AS (SELECT * FROM emp WHERE deptno=30) WITH CHECK OPTION; INSERT INTO v_emp_30 (empno,ename,deptno) VALUES (10003,'蜘蛛侠',20);//报错,因为原表中已经存在 INSERT INTO v_emp_30 (empno,ename,deptno) VALUES (10004,'嘻哈侠',30);//成功
-
修改数据(只能修改视图中存在的数据)
-
例:
UPDATE v_emp_30 SET ename='煎饼侠' WHERE empno=10004; UPDATE v_emp_30 SET ename='葫芦娃' WHERE empno=10001;
-
-
删除数据(只能删除视图中存在的数据)
-
例:
DELETE FROM v_emp_30 WHERE empno=10004;//视图中存在,成功 DELETE FROM v_emp_30 WHERE empno=10001;//视图中不存在,不成功
-
-
9.4 修改视图(相当于把旧存在的视图迭代掉,没有则创建)
-
格式: CREATE OR REPLACE VIEW 视图名 AS 子查询;
CREATE OR REPLACE VIEW v_emp_20 AS (SELECT * FROM emp);
9.5 删除视图
-
格式:DROP VIEW 视图名;
DROP VIEW v_emp_20;
9.6 视图别名
-
如果创建视图的时候使用了别名 则后面的各种操作必须只能使用别名
CREATE VIEW v_emp_20 AS (SELECT ename n,sal FROM emp); UPDATE v_emp_20 SET n='aaaa' WHERE name='钢铁侠';//成功! UPDATE v_emp_20 SET ename='aaaa' WHERE ename='钢铁侠';//失败!(别名)
十、 约束
- 什么是约束: 约束是给表字段添加的限制条件
10.1 非空约束 NOT NULL
-
限制字段的值不能为null
CREATE TABLE t1(id INT,age INT NOT NULL); INSERT INTO t1 values (1,20);//成功! INSERT INTO t1 values (2,null);//失败
10.2 唯一约束 UNIQUE
-
限制字段的值不能重复
CREATE TABLE t2(id INT,age INT UNIQUE); INSERT INTO t2 VALUES(1,20);//成功! INSERT INTO t2 VALUES(2,20);//失败
10.3 主键约束 PRIMARY KEY
- 限制字段值唯一并且非空
-
创建表时添加:
CREATE TABLE t_pri(id INT PRIMARY KEY AUTO_INCREMENT);
-
创建表后添加主键约束:
//已经创建表 CREATE TABLE t_pri2(id INT); //添加主键 ALTER TABLE t_pri2 ADD PRIMARY KEY (id);
-
删除主键约束
//删除主键 ALTER table t_pri2 DROP PRIMARY KEY; //查看表详细属性信息 DESC t_pri2;
-
10.4 默认约束 DEFAULT(设置默认值的)
-
给字段添加默认值,当字段不赋值的时候 此约束的值生效
CREATE TABLE t3(id INT,age INT DEFAULT 20); INSERT INTO t3 VALUES (1,88); INSERT INTO t3 (id) VALUES (2);//这里age默认为20 INSERT INTO t3 VALUES (3,null);//这里age默认为20
10.5 检查约束 CHECK (mysql中没有效果 但是语法不报错)
CREATE TABLE t4(id INT,age INT CHECK(age>10));
10.6 外键约束
- 外键对应参照表/主表中的主键
- 字段的值可以为null,可以重复,但是不能是参照表/主表中不存在的值
- 被参照的数据(参照表/主表中的主键数据若在本表中的外键出现)不能先删除
- 被参照的表(参照表/主表)不能先删除
- 一句话概括:外键参照的表的任何一部分不能先消失
-
测试外键约束
-
创建部门表(员工表中的部门号参照了部门表中的部门号,即员工表中的部门号是外键)
CREATE TABLE dept(id INT PRIMARY KEY AUTO_INCREMENT,name VARCHAR(10)); CREATE TABLE emp( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(10), deptid INT, CONSTRAINT fk_dept FOREIGN KEY (deptid) REFRERENCES dept(id) );
- 创建表的时候设置外键的格式:在创建表最后一个字段后面添加:
CONSTRAINT 约束名(随便取) FOREIGN KEY (外键字段名称) REFRERENCES 被参照的表名(被参照的字段名)
- 创建表的时候设置外键的格式:在创建表最后一个字段后面添加:
-
增删数据测试
//现在被参照的部门表中插入一个部门 INSERT INTO dept VALUES(null,'神仙'),(null,'妖怪'); //在员工表中增删数据 INSERT INTO emp VALUES(null,'悟空',1); //成功! INSERT INTO emp VALUES(null,'赛亚人',3);//失败! 3号部门在被参照的部门表中根本没有,不存在数据 DELETE FROM dept WHERE id=1;//失败!部门号被员工表参照且1号部门正被关联着。
-
外键在实际开发中由于不利于进行测试,所以可能会不被加上使用
十一、 索引
11.1 概念
- 什么是索引:
- 索引是数据库中用来提高查询效率的技术,类似于目录。
- 为什么使用索引:
- 如果不使用索引,查询数据时会依次遍历每一个保存数据的磁盘块,直到找到目标数据为止,使用索引后,磁盘块会以树状结构保存,查询数据时会大大降低磁盘块的访问量,从而提高查询效率。
-
原理图
- 索引是越多越好吗?
- 因为索引会占用储存空间,只对常用的查询字段创建索引。
- 有索引就一定好吗?
- 如果数据量小的话 添加索引反而会降低查询效率。
- 索引的分类(了解即可)
聚集索引(聚簇索引): 通过主键创建的索引称为聚集索引,一个表只能有一个聚集索引,添加了主键约束的表会自动创建聚集索引,聚集索引的树状结构中保存了数据
非聚集索引: 通过非主键字段创建的索引称为非聚集索引,一个表可以有多个,树状结构中不保存数据只保存指针(磁盘块地址)
11.2 创建索引
- 格式: CREATE INDEX 索引名(随便取) ON 表名(字段名(长度));
-
给title字段添加索引
CREATE INDEX index_item2_title ON item2(title);
-
11.3 查看索引
- 格式:SHOW INDEX FROM 表名;
-
查看item2表的索引
SHOW INDEX FROM item2;
-
11.4 删除索引
- 格式:DROP INDEX 索引名称 ON 表名;
-
删除item2中名为index_item2_title的索引
DROP INDEX index_item2_title ON item2;
-
11.5 复合索引
- 通过多个字段创建的索引称为复合索引
- 应用场景:频繁使用某几个字段作为查询条件的时候,可以为这几个字段创建复合索引
- 例子:
-
创建 标题和价格的索引:
CREATE INDEX index_item2_title_price ON item2(title,price);
-
11.6 索引总结
- 索引是提高查询效率的技术
- 索引占存储空间不是越多越好
- 数据量小时使用索引会降低效率
- 聚集索引:只有一个,添加主键约束后会自动创建,保存数据
- 非聚集索引:多个,非主键字段创建,没有数据只保存指针
- 尽量不要在频繁修改的表上面添加索引
导入索引测试数据验证索引是否真的提高了效率(用到的测试文件可以向本资料编写者索要)
测试数据库文件中有一张170万条数据(未含有索引)的表
-
将sql数据库文件导入自己的数据库:
-
方法一:把文件解压到一个目录A,在终端中/命令行中连接数据库,并选中一个可用的数据库,然后执行:
source 目录A/item_backup.sql;
方法二:使用第三方可视化工具(不做演示)
-
-
测试:
SHOW TABLES; //查询是否有item2表 SELECT COUNT(*) FROM item2; //查看是否是172万多条数据 //查询所有数据,并记录消耗时间1.15秒 SELECT * FROM item2 WHERE title='100'; //给title字段添加索引 CREATE INDEX index_item2_title ON item2(title); //再次查询所有数据,记录消耗时间 0.04秒 提高近30倍 SELECT * FROM item2 WHERE title='100';
十二、 事务
- 事务是数据库中执行SQL语句的工作单元,不可拆分,可以保证同一业务中的所有SQL语句全部成功或全部失败
12.1 事务的ACID特性
- Atomicity:原子性,最小不可拆分,保证全部成功,全部失败
- Consistency:一致性,从一个一致状态到另一个一致状态
- Isolation:隔离性, 多个事务之间互不影响
- Durability:持久性,事务完成后数据提交到数据库文件中 持久保存
12.2 事务相关指令
show variables like '%autocommit%';//查看数据库自动提交的状态
set autocommit=0/1;//设置数据库自动提交的状态
commit;//提交
rollback;//回滚
savepoint s1;//设置回滚点
rollback to s1;//回滚到s1这个回滚点