一、数据库介绍
1.1 数据记录的流程
- 标记
- 用纸张记录
- 为了方便存储,提出用计算机硬盘存储
- 数据库存储
1.2 数据库的历史流程
- 层级模型
- 网状模型
- 关系模型(将世界全部看成对象及对象的联系组成)
- 关系-对象模型
1.3 关系模型(重点)
- 概念:将世界上所有的事物都可以抽取成单个实体,将实体与实体进行联系,称为关系型数据。
- 实体:用一些关键字对它进行描述,一般关系型数据都是用一张二维表进行表示
- 图示:
解释:
1)在一张二维表中,每行都代表是个独立的实体
2)在一张二维表中,每一列称为字段,字段的作用是用来描述实体的。
3)如果有关联的实体,可以新增一个字段,用来关联上另外一张表中的实体,也称为主外键关联
1.4 安装数据库
- 下载好数据库:https://dev.mysql.com/downloads/mysql/
- 下载好之后,放到硬盘中然后解压。
- 对数据库继续配置环境变量
- 在数据库的目录下,新建一个my.ini文件,并设置以下的内容
[mysql]
# 设置数据库的默认全局编码集
default-character-set=utf8
[mysqld]
# 设置数据库的端口
port=3306
# 设置数据库的文件夹(根据自己的文件进行修改)
basedir=D:\mysql\mysql-8.0.22-winx64
# 设置数据库中实际数据存储的文件夹(该文件夹用来存储数据的)(根据自己的文件进行修改)
datadir=D:\mysql\mysql-8.0.22-winx64\data
# 设置数据库的最大链接数
max_connections=20
# 设置服务端的编码
character-set-server=utf8
# 数据库的存储引擎设置
default-storage-engine=INNODB
安装数据库,在数据库的bin目录,然后进入cmd命令行,在命令上输入:mysqld -install
生成初始的data文件夹及初始的登录密码
输入命令:mysqld —initialize —console启动MySQL的服务,使用命令:net start mysql
使用登录名实现数据库的登录,使用命令:mysql -uroot -p2o-(llVL/Kem
使用之前的旧密码,修改一个新的密码,
使用:ALTER USER ‘root’@’localhost’ IDENTIFIED WITH mysql_native_password BY ‘root’;
二、数据库的使用
2.1 数据库的基本命令
安装数据库的命令:mysqld -install
初始化数据库并获取初始密码,并将其输出在控制台上:mysqld —initialize —console
卸载数据库的命名:mysqld -remove
登录数据库:mysql -uroot -p密码
登录数据库的完整命名:mysql -u用户名 -h主机名或者ip地址 -P端口 -p密码开启数据库服务:net start mysql
关闭数据库服务:net stop mysql
查看数据库管理系统中有多少数据库:show databases;
使用到指定的数据库,使用命令:use 数据库名;
查看所有数据库中有多少表:show tables;
推出数据库:exit;
2.2 结构化查询语句(SQL)
- DDL:数据定义语言,用于创建数据库,删除数据库,创建表结构,删除表结构和修改表结构
- DML:数据操作语言,用于数据库增、删、改
- DQL:数据查询语言,用于数据库的查询操作,简单查询、条件查询、模糊查询、子查询、连接查询、联合查询
- DCL:数据控制语言,用于控制数据库用户的权限,给用户权限和取消用户权限
2.3 SQL和Mysql
- SQL称为结构化查询语句,SQL的作用主要是定义规则,不存在实际的产物,停留在理论的阶段
- MySql称为数据库管理系统,可以提供给用户使用的管理系统,一般数据库管理系统分为两个端口:客户端和服务端
- Mysql的结构图:
三 数据定义语言DDL
3.1 数据库操作
- 创建数据库
-- 创建数据库,名字叫db_woniu,使用命令:create database 数据库名称;
create database db_woniu;
-- 一般在创建数据库的时候,需要指定数据库的编码集
create database db_school charset=utf8;
- 删除数据库(慎用)
-- 从数据库管理系统中删除指定的数据库命令:drop database 数据库名;
drop database db_woniu;
3.2 数据表操作
- 创建数据表
-- 创建数据表
使用命令:
create table 表名(
字段名 数据类型,
字段名 数据类型,
字段名 数据类型
);
举例:创建学生表t_student
create table t_student(
student_id int,
student_name varchar(30),
age int,
sex char,
address varchar(30)
);
- 删除表
-- 删除指定的表
drop table 表名;
-- 比如:
drop table t_student;
- 复制表(只复制结构不复制数据)
-- 复制一张新的表
create table 新表名 like 原表;
- 复制一张表(复制结构和数据)
create table 新表名 as (select * from 原表);
- 向已经存在的表中新增一个字段
alter table 表名 add column 字段名 数据类型;
比如:
alter table t_student add column height int;
- 向已经存在字段修改其字段对应的数据类型
alter table 表名 modify 字段名称 新数据类型;比如:alter table t_student modify height double;
四、数据操作语言(重点)
- 新增数据
-- 向指定表中新增数据(新增指的是新增实体)命令语法:insert into 表名(字段) value(字段对应的值);
-- 新增全部字段
insert into t_student(student_id,student_name,age,sex,address,height) value(1,"张三",18,"男","重庆",180);
-- 如果是新增全部字段,则表名括号里面的内容可以省略
insert into t_student value(2,"李四",19,"男","成都",170);
-- 新增的时候,只有部分字段有数据,表明后面的字段名必须写明,并且value的个数和表名后面的字段个数一致
insert into t_student(student_id,student_name) value(3,"王五");
-- values关键字
insert into t_student(student_id,student_name) values(5,"李雷"),(6,"李华"),(7,"小白");
- 修改数据
-- 修改语法:update 表名 set 字段 = 值 where 修改的条件;
-- 可以将已经存在的数据进行修改,下面的命令会导致该中age字段的值都修改成20,一般不采用
update t_student set age = 20;
-- 条件修改
update t_student set age = 25 where student_id = 7;
- 删除数据
-- 删除命令:
delete from t_student where student_id = 7;
-- 删除整张表的数据
delete from t_student;
-- 根据条件删除
delete from t_student where student_id = 7;
五、数据库数据类型
- 整型
1、tinyint 最小的整型(迷你整型)
2、smallint 短整型
3、middleint 中等整型
4、int 整型(一般都用int)
5、long 长整型
- 浮点型
1、float 单精度浮点型 保留7位小数,最后一位不保证正确
2、double 双精度浮点型 保留15位小数,最后以为不保证正确
3、decimal(M,D) 定点小数
- 字符
1、char 定长字符
2、varchar 变长字符
两者的区别(重点):
定长字符char,如果字段使用char类型,那么内存空间直接给255个内存大小,优点:存储速度快,缺点:会造成一定的空间浪费
变长字符varchar,如果字段使用varchar,那么数据库会先根据实际存入数据的字符个数进行计算,根据计算的空间大小开辟相应的内存大小,优点:节约内存空间,缺点:数据库操作两次,存储效率相对较低。
特别说明:varchar使用的时候,需要给定varchar(8),括号里面的值只是设置最大的长度
- 时间
1、date 时间,包括了年月日,时分秒
2、year 年份
3、datetime 瞬时时间,包括年月日 时分秒
- 二进制(了解)
1、BOLB
- 文本
1、text 文本数据类型
六、数据库设计
6.1 数据库的健
- 唯一键:在一张表中,可以用来识别唯一的一个实体(一行)的字段,比如:学号、身份证号、电话号码
- 主键:从唯一键中选出其中的字段作为主键,如果该字段设置为了主键,数据库会自动的判断,在新增数据的时候,不能出现重复的数据,可一般是一个字段,也肯能是组合主键
- 候选键:从唯一键中去掉主键字段,剩下的唯一字段都是候选键
- 外键:作用是将各个实体之间产生联系,一张表中的外键,是另外一张表中的主键数据,主外键关联
6.2 数据库相关约束
- 主键约束:主键的作用是为了保证每一行都是唯一的,因此被设置为主键的字段要求不能出现重复的数据,可以自动递增
- 外键约束:一个表中的外键是另外一张表中的主键,要求被设置为外键的数据必须是对应另外一张表主键中已经存在的数据,如果外键中已经使用了主键中的数据,那么主键中的该行数据不能删除。
- 非空约束:如果该字段设置为了非空约束,那么该字段必须给值,不能空着
- 默认值:当在新增数据的时候,如果没有给值,则直接使用默认值,如果已经给值了,则使用给定的值
6.3 数据库范式
- 第一范式:要求保证列的原子性,一张表中,每一个字段只能代表一个实体的属性,只要是关系型数据库,一定满足第一范式
- 第二范式:要求保证在表中每一个实体能够被唯一区分,实现第二范式的方式可以是设置主键。
- 第三范式:要求普通属性字段必须和主键直接相关而不是间接相关
6.4 表与表之间的关系
一对一的关系:一张表中一个实体的数据唯一对应另外一张表中的一个实体数据
图示:一对多的关系:一张表中的一个实体数据对应另外一张表中多个实体数据
图示:多对多的关系:一张表中的一个实体数据对应另外一张表中多个实体数据,反过来,另外一张表中一个实体数据也能对应第一张表中的多个实体数据。
图示:
七、数据查询语言DQL
- 数据库脚本内容:
/*
Navicat MySQL Data Transfer
Source Server : localhost
Source Server Version : 80022
Source Host : 127.0.0.1:3306
Source Database : db_school
Target Server Type : MYSQL
Target Server Version : 80022
File Encoding : 65001
Date: 2021-04-07 15:37:25
*/
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for course
-- ----------------------------
DROP TABLE IF EXISTS `course`;
CREATE TABLE `course` (
`c_id` varchar(20) NOT NULL,
`c_name` varchar(20) NOT NULL DEFAULT '',
`t_id` varchar(20) NOT NULL,
PRIMARY KEY (`c_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of course
-- ----------------------------
INSERT INTO `course` VALUES ('01', '语文', '02');
INSERT INTO `course` VALUES ('02', '数学', '01');
INSERT INTO `course` VALUES ('03', '英语', '03');
-- ----------------------------
-- Table structure for score
-- ----------------------------
DROP TABLE IF EXISTS `score`;
CREATE TABLE `score` (
`s_id` varchar(20) NOT NULL,
`c_id` varchar(20) NOT NULL,
`s_score` int DEFAULT NULL,
PRIMARY KEY (`s_id`,`c_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of score
-- ----------------------------
INSERT INTO `score` VALUES ('01', '01', '80');
INSERT INTO `score` VALUES ('01', '02', '90');
INSERT INTO `score` VALUES ('01', '03', '99');
INSERT INTO `score` VALUES ('02', '01', '70');
INSERT INTO `score` VALUES ('02', '02', '60');
INSERT INTO `score` VALUES ('02', '03', '80');
INSERT INTO `score` VALUES ('03', '01', '80');
INSERT INTO `score` VALUES ('03', '02', '80');
INSERT INTO `score` VALUES ('03', '03', '80');
INSERT INTO `score` VALUES ('04', '01', '50');
INSERT INTO `score` VALUES ('04', '02', '30');
INSERT INTO `score` VALUES ('04', '03', '20');
INSERT INTO `score` VALUES ('05', '01', '76');
INSERT INTO `score` VALUES ('05', '02', '87');
INSERT INTO `score` VALUES ('06', '01', '31');
INSERT INTO `score` VALUES ('06', '03', '34');
INSERT INTO `score` VALUES ('07', '02', '89');
INSERT INTO `score` VALUES ('07', '03', '98');
-- ----------------------------
-- Table structure for student
-- ----------------------------
DROP TABLE IF EXISTS `student`;
CREATE TABLE `student` (
`s_id` varchar(20) NOT NULL,
`s_name` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT '',
`s_birth` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT '',
`s_sex` varchar(10) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT '',
`age` int DEFAULT NULL,
`height` int DEFAULT NULL,
PRIMARY KEY (`s_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of student
-- ----------------------------
INSERT INTO `student` VALUES ('01', '赵雷', '1990-01-01', '男', '23', '160');
INSERT INTO `student` VALUES ('02', '钱电', '1990-12-21', '女', '26', '165');
INSERT INTO `student` VALUES ('03', '孙风', '1990-05-20', '男', '20', '155');
INSERT INTO `student` VALUES ('04', '李云', '1990-08-06', '保密', '19', '180');
INSERT INTO `student` VALUES ('05', '周梅', '1991-12-01', '女', '22', '175');
INSERT INTO `student` VALUES ('06', '吴兰', '1992-03-01', '女', '18', '167');
INSERT INTO `student` VALUES ('07', '郑竹', '1989-07-01', '男', '24', '158');
INSERT INTO `student` VALUES ('08', '王菊', '1990-01-20', '女', '25', '176');
INSERT INTO `student` VALUES ('09', '李云龙', '1990-01-20', '男', '26', '163');
-- ----------------------------
-- Table structure for teacher
-- ----------------------------
DROP TABLE IF EXISTS `teacher`;
CREATE TABLE `teacher` (
`t_id` varchar(20) NOT NULL,
`t_name` varchar(20) NOT NULL DEFAULT '',
PRIMARY KEY (`t_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of teacher
-- ----------------------------
INSERT INTO `teacher` VALUES ('01', '张三');
INSERT INTO `teacher` VALUES ('02', '李四');
INSERT INTO `teacher` VALUES ('03', '王五');
7.1 单表查询
- 简单查询
-- 最简单的查询命令
-- 命令语法:select 字段名称 from 表名;
select * from student; (真实开发,禁止使用)
-- 指定字段的查询命令
select s_id,s_name,s_birth,s_sex,age,height from student;
-- 如果只需要其中部分的字段数据,只用写有需要的字段名称即可
select s_id,s_name from student;
- 条件查询
-- 按照一些条件将符合要求的数据查询出来,命令语法:select 字段 from 表名 where 条件;
-- 比如:查询学号为01的学生信息
select s_id,s_name,s_birth,s_sex,age,height from student where s_id = "01";
-- 比如:查询出所有的男学生
select s_id,s_name,s_birth,s_sex,age,height from student where s_sex= "男";
-- 比如:查询出性别不是男的学生
select s_id,s_name,s_birth,s_sex,age,height from student where s_sex <> "男";
-- 多个条件查询
-- 比如:查询出身高低于160的男学生
select s_id,s_name,s_birth,s_sex,age,height from student where height < 160 and s_sex = "男";
-- 比如:查询出身高低于160或者男学生
select s_id,s_name,s_birth,s_sex,age,height from student where height < 160 or s_sex = "男";
-- 数值类型的范围查询
-- 比如:查询出身高在165到175之间的学生,包含165和175
select s_id,s_name,s_birth,s_sex,age,height from student where height >= 165 and height <= 175;
select s_id,s_name,s_birth,s_sex,age,height from student where height between 165 and 175;
select s_id,s_name,s_birth,s_sex,age,height from student where height < 165 or height > 175;
- 模糊查询
-- 只知道其中数据的部分内容,其他数据内容随意匹配,命令语法:select 字段 from 表名 where 字段 like 条件
-- 第一种占位符:下划线_ 一个下划线代表占用一位字符
-- 比如:查询出姓李的学生信息,只能查询出名字为两个字的学生信息
select s_id,s_name,s_birth,s_sex,age,height from student where s_name like "李_";
-- 第二种占位符:百分号%,如果在后面跟了%,可以同时匹配多个字符,%对个数没有要求
-- 查询出姓李的所有学生信息
select s_id,s_name,s_birth,s_sex,age,height from student where s_name like "%李%";
- 排序,在查询的结果中按照指定的字段数据进行排序,升序排列,降序排列
-- 命令语法:select 字段 from 表名 order by 排序字段 排序规则(ASC/DESC)
-- 比如:按照身高的升序排列
select s_id,s_name,s_birth,s_sex,age,height from student order by height ASC;
-- 比如:按照身高的降序排列
select s_id,s_name,s_birth,s_sex,age,height from student order by height DESC;
-- 比如:按照年龄的降序排列
select s_id,s_name,s_birth,s_sex,age,height from student order by age DESC;
-- 一条sql语句中,由两个排序规则
-- 比如:按照年龄的降序排列,按照身高的升序排列
select s_id,s_name,s_birth,s_sex,age,height from student order by age DESC, height DESC;
- 分组查询,在查询语句中,将某些字段为条件,分为一个组
-- 命令语法:select 字段 from 表名 group by 分组字段;
-- 在查询成绩表中的数据,按照学号分组
select s_id,c_id,s_score from score group by s_id;
-- 如果使用了分组查询,那么必须配合使用聚合函数
-- count(字段):查询出该分组一共有多少条数据
-- sum(字段):如果字段为数值,可以计算该分组中该字段的总和
-- avg(字段):如果字段为数值,可以计算该分组中该字段的平均数值
-- max(字段):如果字段为数值,可以找出该分组中该字段为最大值的数据
-- min(字段):如果字段为数值,可以找出该分组中该字段为最小值的数据
-- 1、举例:查询出每个学生学习的课程数
select s_id 学号,count(c_id) 选课数 from score group by s_id;
-- 2、举例:查询出每个学生的总成绩
select s_id 学号,sum(s_score) 总成绩 from score group by s_id order by sum(s_score) desc;
-- 3、举例:查询出每个学生的平均成绩
select s_id 学号,avg(s_score) avg from score group by s_id order by avg desc;
-- 4、举例:查询出每个学生的最高成绩
select s_id 学号,max(s_score) 最高成绩 from score group by s_id;
-- 5、举例:查询出每个学生的最低成绩
select s_id 学号,min(s_score) 最低成绩 from score group by s_id;
- having关键字,在分组之后,可以将聚合函数作为条件进行筛选,查询出符合条件的结果集
-- 语法:select 字段 from 表名 group by 分组字段 having 聚合函数作为条件筛选
-- 举例:查询出平均成绩不及格的学生信息
select s_id 学号,avg(s_score) avg from score group by s_id having avg < 60;select s_id from score where s_id = "01";
-- where和having的区别:where:作为条件筛选,一般where后面条件跟普通字段作为条件判断having:作为条件筛选,一般having后面跟聚合函数作为条件判断,用having的前提是用了group by
- limit关键字,用于在结果集中筛选个数,一般用在分页
-- 命令语法:select 字段 from 表名 limit 起始位置,显示个数;
-- 比如:查询学生信息的前5个数据
select s_id,s_name,s_birth,s_sex,age,height from student limit 2,5;
- 去重,在查询的时候,可以通过去重关键字(distinct)将一个字段中相同的数据去掉只留下第一个
-- 命令语法:select distinct 字段 from 表名;
-- 比如:查询出成绩表中有哪些学生
select distinct s_idfrom score;
- 将所有的知识点融合成一条完整的sql查询语句
select distinct 字段 from 表名 where 字段条件 group by 分组字段 having 聚合函数条件 order by 排序字段 排序规则 limit 起始位置,显示条数;
1、from:将数据库中数据加载到内存中
2、where:从内存中筛选出符合条件的数据
3、group by:如果查询语句中有分组,则按照字段进行分组,如果没有,将剩下的数据分为一个组
4、having:如果有聚合函数,执行聚合函数中的内容,如果还有having,则根据聚合函数的条件进行筛选
5、select:从内存中找到哪些字段的数据是需要显示的
6、distinct:根据去重的字段,将该字段中相同的数据去掉,只留第一个
7、order by:根据排序规则进行排序
8、limit:根据给定的起始位置和显示的条数,进行数据显示的筛选
7.2 子查询
- 简单子查询,作用:将一个查询的结果集再次作为条件或者作为新的表,再次查询
-- 举例:查询姓名叫做李云的学生所有成绩
-- 第一条查询语句
select s_id from student where s_name = "李云";---->04
-- 第二条查询语句
select s_id,c_id,s_score from score where s_id = "04";
-- 将上面的两条语句合并成一条查询语句
select s_id,c_id,s_score from score where s_id = (select s_id from student where s_name = "李云");
-- 举例:查询张三老师授课的所有成绩
-- 第一条语句,根据老师姓名张三找出老师的id值
select t_id from teacher where t_name = "张三"; --->01
-- 第二条查询语句,根据老师的id号,查询出对应的课程id号
select c_id from course where t_id = "01";--->02
-- 第三条查询语句,根据课程号,在成绩表中找出对应的数据
select s_id,c_id,s_score from score where c_id = "02";
-- 将上面三条综合成一条子查询
select s_id,c_id,s_score from score where c_id = (select c_id from course where t_id = (select t_id from teacher where t_name = "张三"));
-- 将上面的查询语句写成以下形式:
select s_id,c_id,s_score from score where c_id = (select c_id,c_name,t_id from course where t_id = (select t_name,t_id from teacher where t_name = "张三"));----会报错。
-- 完成子查询的命令语法:
select 字段 from 表名 where 条件字段 = (select 子查询字段 from 表名 where 条件判断);
要求:子查询字段只能有一个,并且条件字段和子查询字段应该是主外键关联的字段
- in子查询
-- 子查询举例:查询出姓李的学生的所有成绩
-- 第一条查询语句,查询出姓李的学生学号
select s_id from student where s_name like "李%";--->04,09
-- 第二条查询语句,查询出学号对应的成绩
select s_id,c_id,s_score from score where s_id = "04,09";
-- 将上面的两句合并成一句子查询
select s_id,c_id,s_score from score where s_id = (select s_id from student where s_name like "李%");
-- 特别注意:如果子查询用的是=判断,那么要求子查询语句的结果集只能是一行一列的数据,如果子查询语句结果集存在一列多行的数据,用=要报错(Subquery returns more than 1 row)
-- 如果子查询结果集存在一列多行的结果,只能使用in子查询
-- in的命令语法:select 字段 from 表名 where 条件判断 in (值1,值2,值3,值4...值n);
-- 将上面的例子进行改造
select s_id,c_id,s_score from score where s_id in (select s_id from student where s_name like "李%");
-- not in,不在子查询结果集里面的其他数据
select s_id,c_id,s_score from score where s_id not in (select s_id from student where s_name like "李%");
- exists子查询,表示是否存在结果集
select s_id,s_name s_sex,age,height from student where exists (select t_id,t_name from teacher where t_name = "小明");
-- 命令语法:select 字段 from 表名 where exists (select 子查询字段 from 表名 where 条件判断);
-- exists的作用:当子查询结果集中有数据,则正常的执行主查询语句,如果子查询语句没有结果集,主查询不执行
- any和all子查询
-- any子查询命令语法:select 字段 from 表名 字段 > any (select 字段 from where 条件判断);
-- > any() 表示大于最小值
-- < any() 表示小于最大值
select s_id,c_id,s_score from score where s_score > any (select s_score from score where s_id = "02");
select s_id,c_id,s_score from score where s_score < any (select s_score from score where s_id = "02");
-- all子查询命令语法:select 字段 from 表名 字段 > all (select 字段 from where 条件判断);
-- > all() 表示大于最大值
-- < all() 表示小于最小值
select s_id,c_id,s_score from score where s_score > all (select s_score from score where s_id = "02");
select s_id,c_id,s_score from score where s_score < all (select s_score from score where s_id = "02");
7.3 连接查询
- 自然连接(开发禁止使用)
-- 将学生表和成绩表连接起来
select s1.s_id,s_name,s_birth,s_sex,age,height,s2.s_id,c_id,s_score from student s1,score s2 where s1.s_id = s2.s_id;
-- 命令语法:select 字段 from 表1,表2 where 主外键条件判断;
- 内连接(重点)
-- 命令语法:select 字段 from 表1 inner join 表2 on 主外键条件;
-- 将学生表和成绩表关联
select s1.s_id,s_name,s_sex,s_birth,age,height,s2.s_id,c_id,s_score from student s1 inner join score s2 on s1.s_id = s2.s_id;
-- 将学生表、成绩表和课程表关联
select s1.s_id,s_name,s_sex,s_birth,age,height,s2.c_id,s_score,c_name,t_id from student s1 inner join score s2 on s1.s_id = s2.s_id inner join course c on s2.c_id = c.c_id;
-- 将学生表、成绩表、课程表和教师表关联
select s1.s_id,s_name,s_sex,s_birth,age,height,s2.c_id,s_score,c_name,c.t_id,t_name from student s1 inner join score s2 on s1.s_id = s2.s_id inner join course c on s2.c_id = c.c_id inner join teacher t on c.t_id = t.t_id;
-- 举例:查询姓名叫做李云的学生所有成绩
select s1.s_id,s_name,s_sex,s_birth,age,height,s2.s_id,c_id,s_score from student s1 join score s2 on s1.s_id = s2.s_id where s_name = "李云";
- 外连接(重点)
-- 1、左外连接:以左边的表为准,如果左边表的数据较多,右边表没有数据,以null显示
-- 命令语法:select 字段 from 表1 left join 表2 on 关联条件;
-- 将学生表和成绩表进行左外连接关联,左边表为学生表
select s1.s_id,s_name,s_sex,s_birth,age,s2.s_id,c_id,s_score from student s1 left join score s2 on s1.s_id = s2.s_id;
-- 2、右外连接:以右边的表为准,如果右边的表较多,左边表没有数据,以null显示
-- 命令语法:select 字段 from 表1 right join 表2 on 关联条件;
7.4 联合查询
-- 将多个结果集整合成一张表结果
-- 比如:查询所有老师和学生的编号和姓名
select s_id as id,s_name as name from student union select t_id as id,t_name as name from teacher;
-- union关键字会自动的将相同的数据去掉重复的,只留一个
-- nuion all 关键字,全部展示,不会去重
-- 比如:在上面的基础上,进行统计所有的师生个数
select count(t1.id) from (select s_id as id,s_name as name from student union select t_id as id,t_name as name from teacher) as t1;
-- 比如:查询出师生中有叫做张三的编号和姓名
select t1.id,t1.name from (select s_id as id,s_name as name from student union select t_id as id,t_name as name from teacher) t1 where t1.name = "张三";
八、SQL查询练习
8.1 上半部分
-- 1、查询出平均成绩大于60分的学号、姓名和平均成绩分析:最终结果集需要有:学号、姓名和平均成绩(聚合函数),需要用到分组,对平均成绩进行判断
select s1.s_id,s_name,avg(s_score) avg from student s1 join score s2 on s1.s_id = s2.s_id group by s1.s_id having avg > 60;
-- 2、查询出平均成绩小于60分的学号、姓名和平均成绩,但是要包含没有成绩的学生分析:结果集在学生表和成绩表中,为了保留没有成绩的学生,用外连接,根据学号进行分组再计算平均成绩,筛选出平均成绩小于60分的学生信息
select s1.s_id,s_name,avg(s_score) avg from student s1 left join score s2 on s1.s_id = s2.s_id group by s1.s_id having avg < 60 or avg is null;
-- 3、查询出所有学生的学生编号、姓名、选课总数、每个学生的总成绩分析:结果集在学生表和成绩表中,将两张表连接,通过分组和聚合找到结果集
select s1.s_id,s_name,count(c_id) 选课总数,sum(s_score) 总成绩 from student s1 join score s2 on s1.s_id = s2.s_id group by s1.s_id;
-- 4、查询出学过张三老师课程的学生信息分析:已知条件:t_name="张三",结果集在学生表方式1:将四张表全部连接,在通过t_name进行筛选
-- 方式1
select s1.s_id,s_name,s_sex,s_birth,age,height from student s1 join score s2 on s1.s_id = s2.s_id join course c on s2.c_id = c.c_id join teacher t on c.t_id = t.t_id where t_name = "张三";
-- 方式2:子查询
-- sql1:
select t_id from teacher where t_name = "张三";
-- sql2:
select c_id from course where t_id = ?;
-- sql3:
select s_id from score where c_id = ?;
-- sql4:
select s_id,s_name,s_sex,s_birth,age,height from student where s_id in (?)
-- 综合成一句:
select s_id,s_name,s_sex,s_birth,age,height from student where s_id in (select s_id from score where c_id = (select c_id from course where t_id = (select t_id from teacher where t_name = "张三")));
-- 5、查询出没有学过张三老师课程的学生信息,包括没有成绩的学生
select s_id,s_name,s_sex,s_birth,age,height from student where s_id not in (select s_id from score where c_id = (select c_id from course where t_id = (select t_id from teacher where t_name = "张三")));
-- 6、学习过编号为01课程号并且也学过编号为02课程号的学生信息分析:先找出学过01课程号的学生id,再找出学过02课程的学号
-- 方案1:
select s_id,s_name,s_sex,age,height from student where s_id in (select s_id from score where c_id = "01" and s_id in(select s_id from score where c_id = "02"));
-- 方案2:
SELECT s_id,s_name,s_sex,age from student where s_id in (SELECT s.s_id FROM score LEFT JOIN score s on score.s_id = s.s_id WHERE score.c_id = '01' AND s.c_id = '02');
-- 方案3:
SELECT s1.s_id,s_name,c_id FROM student s1 JOIN score s2 ON s1.s_id = s2.s_id WHERE s1.s_id in (SELECT s_id FROM score c_id = "02" ) AND c_id = "01";
-- 7、查询出没有学全所有课程的学生编号,包括没有学过任何课程的学生分析:根据学生分组,统计出每个学生的课程数,再统计课程表中有多少总课程数,判断两个课程数是否相等。
select s1.s_id,s_name,count(c_id) total from student s1 left join score s2 on s1.s_id = s2.s_id group by s1.s_id having total = (select count(c_id) from course);
-- 8、查询出至少有一门课程与学号为01学生所学课程相同的学生信息分析:先找出01学生学过的课程数,再从成绩中找出和01学生有相同的学号
select s_id,s_name,s_sex,age,height from student where s_id in (select distinct s_id from score where c_id in (select c_id from score where s_id = "01")) and s_id <> "01";
-- 9、查询出成绩有两门以上不及格的学生信息分析:
-- 1)先找出不及格的数据
select s_id,c_id,s_score from score where s_score < 60;
-- 2)在上面结果集的基础之上,对学生进行分组,并统计出课程数
select s_id,count(c_id) 课程数 from score where s_score < 60 group by s_id;
-- 3)用having进行过滤
select s_id from score where s_score < 60 group by s_id having count(c_id) >= 2;
-- 4)综合成一句话
select s_id,s_name,s_sex,age,height from student where s_id in(select s_id from score where s_score < 60 group by s_id having count(c_id) >= 2);
-- 10、查询出同一个学生学习课程01成绩比课程02成绩高的学生信息。
-- 方案1:
SELECT st1.s_id,s_name FROM student st1JOIN score sc1 on st1.s_id = sc1.s_id AND sc1.c_id = "01"JOIN score sc2 on st1.s_id = sc2.s_id AND sc2.c_id = "02"WHERE sc1.s_score > sc2.s_score;
-- 方案2:
SELECT s_id,s_name FROM student WHERE s_id in ( SELECT s1.s_id as sid FROM score s1 JOIN score s2 on s1.s_id =s2.s_id WHERE s1.c_id="01" and s1.s_score > s2.s_score AND s2.c_id= "02");
-- 方案3:
select s.s_id from student s left join score con s.s_id=c.s_id where c.c_id=01 and EXISTS(select 1 from score c1 where c1.c_id=02 and c1.s_id=s.s_id and c.s_score>c1.s_score);
8.2 下半部分
-- 1、查询出和学号为"01"学生学习课程完全相同的学生信息
select s_id,count(c_id) from score where s_id not in(select s_id from score where c_id not in(select c_id from score where s_id = "01")) group by s_id having count(c_id) = (select count(c_id) from score where s_id = "01") and s_id <> "01";
-- 2、查询出每个老师所授课程的总成绩,并按照降序排列
select s1.c_id,sum(s_score) sum from score s1 join course c1 on s1.c_id = c1.c_id group by s1.c_id order by sum desc;
-- 3、查询出每门课程的学习学生数,查询出课程名称
select count(s_id) 学习人数,c_name from score s1 join course c1 on s1.c_id = c1.c_id group by s1.c_id;
-- 4、查询出1990年出生的学生信息(提示:可以使用year函数,比如:year(1992-03-01)可以直接获取年份)
select s_id,s_name,s_sex,s_birth,age,height from student where year(s_birth) = 1990;
-- 5、查询学习课程名为“数学”,并且分数低于60分的学生信息
select s_id,s_name,s_sex,s_birth,age,height from student where s_id in (select s_id from score s1 join course c1 on s1.c_id = c1.c_id where s_score < 60 and c_name = "数学");
-- 6、查询学习过张三老师课程的学生中,成绩最高的学生信息
select s2.s_id,s_name,s_sex,s_birth,age,height from student s2 join score s3 on s2.s_id = s3.s_id join course c2 on s3.c_id = c2.c_id join teacher t2 on c2.t_id = t2.t_id where s3.s_score = (select max(s_score) from score s1 join course c1 on s1.c_id = c1.c_id join teacher t1 on c1.t_id = t1.t_id where t1.t_name = "张三") and t2.t_name = "张三";
-- 7、查询和学号为01学生同年的学生信息
select s_id,s_name,age,height,s_birth from student where year(s_birth) = (select year(s_birth) from student where s_id = "01");
-- 8、查询每个课程的及格率
select t1.c_id,t1.count1/t2.count2 * 100 及格率 from (select c_id,count(s_id) count1 from score where s_score >= 60 group by c_id ) t1 join (select c_id,count(s_id) count2 from score group by c_id) t2 on t1.c_id = t2.c_id;
-- 9、查询出每门课程的学生人数、总成绩、平均成绩及授课老师
select count(s_id),sum(s_score),avg(s_score),t_name from score s join course c on s.c_id = c.c_id join teacher t on c.t_id = t.t_id group by c.c_id;
九、数据库视图
概念:将有关联的多张表生成为一张虚拟表,只为了展示关联后的结果集,视图只能做查询、修改和新增,不能实现删除操作。
视图的优点:可以将复杂的多表查询语句转换成简单的单表查询
视图的缺点:虚拟表依赖真实表,如果真实表结构做了改动,视图直接损坏,需要重新维护,增加数据库维护成本
会将一些指令保存在数据库中,占用一定的内存。如何创建视图
十、事务管理
10.1 事务的基础概念
- 事务的图示
- 在之前使用的数据库只要做增、删、改操作的时候,实际上每次发送完指令,MySQL的客户端在自动的发送提交事务的指令,才能将真实的数据提交到数据库中,实现持久化存储,控制自动提交事务的全局变量为autocommit,默认值为on。如果想要关闭,将值设置为off。
10.2 手动事务
- 手动事务,如果在业务中需要有事务的操作,则用命令开启手动事务,如果手动事务开启,自动提交事务将失效,如果继续发送commit或者是rollback指令,手动事务结束,自动提交事务又开始起作用。
- 开启手动事务的命令:start transaction;或者begin;
- 结束事务:commit或者rollback;
10.3 回滚点
- 在一个事务期间,可以对每一句sql指令设置一个回滚点,如果发现需要回滚的数据,则回滚到回滚点即可
- 设置回滚点的指令:savepoint 回滚点名称;
- 回滚到回滚点的指令:rollback to 回滚点名称;如果使用回滚到指定位置,则事务不会结束
10.4 事务的特性ACID
- 原子性:一个事务开启之后,会将该事务中的所有sql指令当成不可分割的整体,处于最小单元
- 隔离性:一个事务开启之后,事务与事务之间是独立的,相互之间没有影响
- 一致性:一个事务的提交前后,数据的状态变化保持一致。
- 持久性:当一个事务完成之后,数据将永久保存,值到下一次对数据的更改。
十一、JDBC开发
11.1 JDBC概念
概念:JDBC是用来连接数据库,并向数据库发送SQL指令,获取结果集的驱动开发
JDBC本身是Java的应用程序,作用是有Java代码向数据库发送指令并获取结果集的应用程序。
11.2 JDBC涉及的类
Driver:驱动相关类,在Java的应用程序中需要对该驱动进行注册,使用反射技术进行注册Class.forName();
DriverManager:驱动管理,用来注册驱动之后,获取连接数据库的对象。
Connection:获取数据库连接的类,通过该类的对象,可以连接到指定的数据库,并且获取可以发送sql指令的对象
Statement:该类由连接对象获取,向数据库发送sql指令,让数据库执行sql指令。会的到结果集
PreparedStatement:是Statement的子类,在开发中推荐使用的类, 预编译对象,可以解决SQL注入的问题,作用和父类一致,用来发送sql指令并获取结果集。
ResultSet:该类属于结果集的类,该类创建的对象用来接受结果集,里面包含了遍历的方法和获取数据库字段的数据方法。
11.3 JDBC开发步骤
第一步:注册驱动
Class.forName(“com.mysql.jdbc.Driver”);第二步:通过驱动管理获取数据库的连接
String url = "jdbc:mysql://127.0.0.1:3306/db_bank";
String username = "root";
String password = "root";
Connection conn = DriverManager.getConnection(url,username,password);
- 获取可以发送sql指令的对象Statement
Statement statement = conn.createStatement();
- 编写要执行的SQL指令
String sql = "select user_id,user_name,money from t_user";
- 发送sql指令,并获取结果集
ResultSet rs = statement.executeQuery(sql);
从结果集中解析出所有的数据
while(rs.next()){
//获取user_id的值
int userId = rs.getInt("user_id");
//获取user_name的值
String userName = rs.getString("user_name");
//获取金额
int money = rs.getInt("money");
System.out.println("编号:"+userId+",姓名:"+userName+",余额:"+money);
}
- 关闭资源
rs.close();
statement.close();
conn.close();
11.4 JDBC的CRUD方法
- 新增数据
public class InsertDemo {
public static void main(String[] args) throws Exception{
//1、注册驱动
Class.forName(Config.DRIVER);
//2、由驱动管理获取连接对象
Connection conn = DriverManager.getConnection(Config.URL,Config.USER_NAME,Config.PASSWORD);
//3、编写sql语句
String sql = "insert into t_user value(?,?,?)";
//4、获取预编译对象
PreparedStatement ps = conn.prepareStatement(sql);
//5、替换占位符?
ps.setInt(1,10);
ps.setString(2,"李思思");
ps.setInt(3,10000);
//6、调用方法
int rows = ps.executeUpdate();
System.out.println("rows = "+rows);
//关闭资源
ps.close();
conn.close();
}
}
- 删除操作
public class DeleteDemo {
public static void main(String[] args) throws Exception{
//注册驱动
Class.forName(Config.DRIVER);
//获取连接
Connection conn = DriverManager.getConnection(Config.URL,Config.USER_NAME,Config.PASSWORD);
//编写sql语句
String sql = "delete from t_user where user_id = ?";
//获取预编译对象
PreparedStatement ps = conn.prepareStatement(sql);
//填充占位符?
ps.setInt(1,10);
//调用方法
int rows = ps.executeUpdate();
System.out.println("rows = "+rows);
//关闭资源
ps.close();
conn.close();
}
}
- 更新操作
public class UpdateDemo {
public static void main(String[] args) throws Exception{
//注册驱动
Class.forName(Config.DRIVER);
//获取连接
Connection conn = DriverManager.getConnection(Config.URL,Config.USER_NAME,Config.PASSWORD);
//编写sql语句
String sql = "update t_user set user_name = ? where user_id = ?";
//获取预编译对象
PreparedStatement preparedStatement = conn.prepareStatement(sql);
//填充占位符?
preparedStatement.setString(1,"张三");
preparedStatement.setInt(2,9);
//执行
int rows = preparedStatement.executeUpdate();
System.out.println("rows = "+rows);
//关闭资源
preparedStatement.close();
conn.close();
}
}
- 查询的方法
public class SelectDemo {
public static void main(String[] args) throws Exception{
//注册驱动
Class.forName(Config.DRIVER);
//获取连接
Connection connection = DriverManager.getConnection(Config.URL,Config.USER_NAME,Config.PASSWORD);
//编写sql
String sql = "select user_id,user_name,money from t_user";
//获取预编译对象
PreparedStatement preparedStatement = connection.prepareStatement(sql);
//执行方法
ResultSet resultSet = preparedStatement.executeQuery();
List<User> userList = new ArrayList<>();
//循环遍历
while(resultSet.next()){
int userId = resultSet.getInt("user_id");
String userName = resultSet.getString("user_name");
int money = resultSet.getInt("money");
//将数据填充到对象中
User user = new User();
user.setMoney(money);
user.setUserId(userId);
user.setUserName(userName);
userList.add(user);
}
for(ListIterator<User> it = userList.listIterator();it.hasNext();){
System.out.println(it.next());
}
//关闭资源
resultSet.close();
preparedStatement.close();
connection.close();
}
}
11.5 浅封装JDBC
public class JDBCUtils {
//定义四个参数,供后续注册驱动和建立连接使用
private static final String DRIVER = "com.mysql.jdbc.Driver";
private static final String URL = "jdbc:mysql://127.0.0.1:3306/db_book?useUnicode=true&characterEncoding=utf-8";
private static final String USER_NAME = "root";
private static final String PASSWORD = "root";
static{
try {
//注册驱动
Class.forName(DRIVER);
}catch (Exception e){
e.printStackTrace();
}
}
//获取连接
public static Connection getConn(){
try {
//获取连接
Connection conn = DriverManager.getConnection(URL,USER_NAME,PASSWORD);
return conn;
}catch (Exception e){
e.printStackTrace();
} return null;
}
//关闭资源
public static void close(Connection conn, PreparedStatement ps){
try {
if(ps != null) {
ps.close();
}
if(conn != null){
conn.close();
}
}catch (Exception e){
e.printStackTrace();
}
}
//关闭资源三个参数重载形式
public static void close(Connection conn, PreparedStatement ps, ResultSet rs){
try {
if(rs != null) {
rs.close();
}
}catch (Exception e){
e.printStackTrace();
}
//调用重载
close(conn,ps);
}
}
11.6 读取properties配置文件
public class ReadDemo{
public static void main(String[] args)throws Exception{
//用来读取properties文件
//读取字节流
InputStream in = new FileInputStream(new File("D:\\Code\\56Code\\Demo\\db.properties"));
//创建Properties对象
Properties prop = new Properties();
//将输入流读取到properties文件中
prop.load(in);
//根据key获取value值
String username = prop.getProperty("username");
String password = prop.getProperty("password");
System.out.println(username+","+password);
}
}
11.7 深度封装JDBC工具类
public class JDBCTools {
//声明四个参数
private static String driver;
private static String url;
private static String username;
private static String password;
//使用静态代码块实现读取properties文件,并且注册驱动
static{
try {
//读取properties
//获取输入流:
InputStream in = JDBCTools.class.getClassLoader().getResourceAsStream("db.properties"); //创建Properties文件
Properties prop = new Properties();
//读取输入流
prop.load(in);
//解析数据
driver = prop.getProperty("driver");
url = prop.getProperty("url");
username = prop.getProperty("username");
password = prop.getProperty("password");
//注册驱动
Class.forName(driver);
}catch (Exception e){
e.printStackTrace();
}
}
//获取连接
public static Connection getConn(){
try {
//获取连接
return DriverManager.getConnection(url,username,password);
}catch (Exception e){
e.printStackTrace();
}
return null;
}
//关闭资源
public static void close(Connection conn, PreparedStatement ps){
try {
if(ps != null){
ps.close();
}
if(conn != null){
conn.close();
}
}catch (Exception e){
e.printStackTrace();
}
}
//关闭资源重载形式
public static void close(Connection conn, PreparedStatement ps, ResultSet rs){
try {
if(rs != null){
rs.close();
}
}catch (Exception e){
e.printStackTrace();
}
close(conn,ps);
}
}