Mysql数据库 易懂

MySql

  • MySQL所使用的 SQL 语言是用于访问数据库的最常用du标准化语言。MySQL 软件采用了双授权政策,分为社区版和商业版,由于其体积小、速度快、总体拥有成本低,尤其是开放源码这一特点,一般中小型网站的开发都选择 MySQL 作为网站数据库。
  • MySQL是一个关系型数据库管理系统
  • 由瑞典MySQL AB 公司开发
  • 现属于 Oracle 旗下产品
  • MySQL是最好的 RDBMS (Relational Database Management System,关系数据库管理系统) 应用软件之一
  • 开源的数据库软件
  • 体积小、速度快、总体拥有成本低,招人成本比较低,所有人必须会
  • 官网:https://www.mysql.com/

安装Mysql

  1. 解压

  2. 把这个包放到自己的电脑环境目录下~

  3. 配置环境变量

    1. 电脑环境Path新建一个 然后复制安装路径D:\mysql\mysql-8.0.21\bin
  4. 新建mysql配置文件

    [mysqld]
    # 目录要换成自己的	
    basedir=D:\mysql\mysql-8.0.21\
    datadir=D:\mysql\mysql-8.0.21\data\
    port=3306
    skip-grant-tables
    
  5. 启动管理员模式下的CMD,进入到mysql的bin目录下

  6. 安装mysql服务

    • mysqld -install
  7. 初始化数据库文件

    • mysqld --initialize-insecure
  8. 启动

    • net start mysql
  9. 默认密码为空,登录命令

    • mysql -uroot -p
  10. 密码修改 由于是Mysql8.0,所以不支持password函数的使用

    • 选择数据库 use mysql
    • 修改密码 alter user ‘root’@‘localhost’ identified by ‘123456’;
  11. 刷新:flush privileges; 刷新后退出:exit; 退出再重新登录即可

命令行

--连接数据库
mysql -u root -p123456
--修改用户密码
update mysql.user set authentication_string=password('123456') where user='root' and Host = 'localhost';
-- 查看时区,显示 SYSTEM 就是没有设置时区啦。
show variables like'%time_zone';
--设置时区 设置完需要重新连接sql重新查看
set global time_zone = '+8:00';
--刷新权限
flush privileges;

--所有的sql语句都使用;结尾

--查看所有的数据库
show databases;
--切换数据库
use 数据库名
--查看数据库中所有的表
show tables;
--显示表中所有的信息
describe 表名

---------------------
--创建一个数据库
create database 数据库名
--退出连接
exit;
--单行注释
/*多行注释*/

数据库xxx语言

DDL 数据库控制语言

DML 数据库操作语言

DQL 数据库查询语言

DCL 数据库控制语言

操作数据库

操作数据库>操作数据库中的表>操作数据库中表的数据

mysql关键字不区分大小写 [ ]表示可选

**如果你的表明或数据库名是一个特殊字符的话要加`` **

操作数据库

  1. 创建数据库

    CREATE DATABASE [IF NOT EXISTS] 数据库名
    
  2. 删除数据库

    DROP DATABASE IF EXISTS 数据库名
    
  3. 使用数据库

    USE `数据库名`
    
  4. 查看数据库

    SHOW DATABASE
    

数据库的列类型

数值

  • tinyint 十分小的数据 1个字节
  • smallint 较小的数据 2个字节
  • mediumint 中等大小的数据 3个字节
  • int 标准的整数 4个字节 常用的
  • bigint 较大的数据 8个字节
  • float 浮点数 4个字节
  • double 浮点数 8个字节(精度问题)
  • decimal 字符串形式的浮点数 金融计算的时候,一般使用decimal

字符串

  • char 字符串固定大小 0~255
  • varchar 可变字符串 0~65535 常用的变量 String
  • tinytext 微型文本 2^8-1
  • text 文本串 2^16-1 保存大文本

时间日期

  • date YYYY-MM-DD 日期格式
  • time HH:mm:ss 时间格式
  • datetime YYYY-MM-DD HH:mm:ss 最常用的时间格式
  • timestamp 时间戳 1970.1.1到现在的毫秒数 也较为常用
  • year 年份表示

null

  • 没有值 未知
  • 不要使用null进行运算,结果为null

数据库的字段属性(重点)

Unsigned:

  • 无符号的整数
  • 声明了该列不能声明为负数

zerofill:

  • 0填充的
  • 不足的位数,使用0来填充,int(3) ,5 — 005

自增

  • 通常理解为自增,自动在上一条记录的基础上+1 (默认)
  • 通常用来设计唯一的主键,必须是整数类型
  • 可以自定义设计主键自增的起始值和步长

非空NULL not null

  • 设置默认的值!
  • sex,默认值为男,如果不指定该列的值,则会有默认的值

扩展

-- 每一个表都必须存在以下五个字段,未来做项目用的,表示一个表记录存在的意义
id 主键
version 乐观锁
is_delete 伪删除
gmt_create 创建时间
gmt_update 修改时间

创建数据库表(重点)

CREATE TABLE IF NOT EXISTS `student`(
  `id` INT(4) NOT NULL AUTO_INCREMENT COMMENT '学号',
  `name` VARCHAR(30) NOT NULL DEFAULT '匿名' COMMENT '姓名',
  `pwd` VARCHAR(20) NOT NULL DEFAULT '123456' COMMENT '密码',
  `set` VARCHAR(2) NOT NULL DEFAULT '男' COMMENT '性别',
  `birthday` DATETIME DEFAULT NULL COMMENT '出生日期',
  `address` VARCHAR(100) DEFAULT NULL COMMENT '家庭地址',
  `email` VARCHAR(50) DEFAULT NULL COMMENT '邮箱',
  PRIMARY KEY(`id`)
)ENGINE=INNODB DEFAULT CHARSET=utf8

格式: [ ] 表示可选的 IF NOT EXISTS判断是否存在

CREATE TABLE [IF NOT EXISTS] `表名`(
    `字段名` 列类型 [非空] [自增] [注释] '学号',
    `字段名` 列类型 [非空] [默认值] '匿名' [注释] '姓名'.....(注意最后一行不要加,)
    )[引擎]ENGINE=INNODB[字符集]CHARSET=utf8)

常用命令

 -- 查看数据库的语句
SHOW CREATE DATABASE 数据库名;

-- 查看表中数据的定义语句
SHOW CREATE TABLE 表名; 

-- 显示表的结构
DESC 表名; 

修改删除表

修改

-- 修改表名 ALTER TABLE 旧表名 RENAME AS 新表名;
ALTER TABLE student RENAME AS student1;

-- 增加表的字段  ALTER TABLE 表名 ADD 字段名 列属性;
ALTER TABLE student1 ADD age INT(11);

-- 修改表的字段约束 ALTER TABLE 表名 MODIFY 字段名 列属性;
ALTER TABLE student1 MODIFY age VARCHAR(20);

-- 修改表字段名 ALTER TABLE 表名 CHANGE 旧字段 新字段 列属性;
ALTER TABLE student1 CHANGE age age1 INT(1);

-- 删除表字段 ALTER TABLE 表名 DROP 字段名;
ALTER TABLE student1 DROP age1;

删除

-- 删除表 (判断表存在 在删除)
DROP TABLE IF EXISTS student1;

MySQL数据管理

外键(了解即可)

方式一、在创建表的时候,增加约束(麻烦,比较复杂)

CREATE TABLE IF NOT EXISTS `grade`(
  `gradeid` INT(10) NOT NULL AUTO_INCREMENT COMMENT '年级id',
  `gradename` VARCHAR(50) NOT NULL COMMENT '年级名称',
  PRIMARY KEY(`gradeid`)
)ENGINE=INNODB DEFAULT CHARSET=utf8

-- 学生表的gradeid字段要去引用年级表的gradeid
-- 定义外键 key
-- 给这个外键添加约束(执行引用) references 引用

CREATE TABLE IF NOT EXISTS `student`(
  `id` INT(4) NOT NULL AUTO_INCREMENT COMMENT '学号',
  `name` VARCHAR(30) NOT NULL DEFAULT '匿名' COMMENT '姓名',
  `pwd` VARCHAR(20) NOT NULL DEFAULT '123456' COMMENT '密码',
  `set` VARCHAR(2) NOT NULL DEFAULT '男' COMMENT '性别',
  `birthday` DATETIME DEFAULT NULL COMMENT '出生日期',
  `gradeid` INT(10) NOT NULL COMMENT '学生的年级',
  `address` VARCHAR(100) DEFAULT NULL COMMENT '家庭地址',
  `email` VARCHAR(50) DEFAULT NULL COMMENT '邮箱',
  PRIMARY KEY(`id`),
  KEY `FK_gradeid` (`gradeid`), --定义外键 key
  CONSTRAINT `FK_gradeid` FOREIGN KEY (`gradeid`) REFERENCES `grade` (`gradeid`) -- 
)ENGINE=INNODB DEFAULT CHARSET=utf8

删除有外键关系的表时,必须先删除引用别人的表(从表),在删除被引用的表(主表)

方式二、创建表成功后,添加外键约束

CREATE TABLE IF NOT EXISTS `grade`(
  `gradeid` INT(10) NOT NULL AUTO_INCREMENT COMMENT '年级id',
  `gradename` VARCHAR(50) NOT NULL COMMENT '年级名称',
  PRIMARY KEY(`gradeid`)
)ENGINE=INNODB DEFAULT CHARSET=utf8

CREATE TABLE IF NOT EXISTS `student`(
  `id` INT(4) NOT NULL AUTO_INCREMENT COMMENT '学号',
  `name` VARCHAR(30) NOT NULL DEFAULT '匿名' COMMENT '姓名',
  `pwd` VARCHAR(20) NOT NULL DEFAULT '123456' COMMENT '密码',
  `set` VARCHAR(2) NOT NULL DEFAULT '男' COMMENT '性别',
  `birthday` DATETIME DEFAULT NULL COMMENT '出生日期',
  `gradeid` INT(10) NOT NULL COMMENT '学生的年级',
  `address` VARCHAR(100) DEFAULT NULL COMMENT '家庭地址',
  `email` VARCHAR(50) DEFAULT NULL COMMENT '邮箱',
  PRIMARY KEY(`id`)
)ENGINE=INNODB DEFAULT CHARSET=utf8

-- ALTER TABLE 表名 ADD CONSTRAINT 约束名 FOREIGN KEY(作为外键的列) REFERENCES 哪个表(哪个字段);
ALTER TABLE `student` ADD CONSTRAINT `FK_gradeid` FOREIGN KEY(`gradeid`) REFERENCES `grade`(`gradeid`);

DML语言(全部记住)

数据库的意义 :数据存储,数据管理

DML语言:数据操作语言

  • insert
  • update
  • delete

添加

insert

-- insert into 表名(字段1,字段2,...)values(值1),(值2)
-- 一般写插入语句,我们一定要数据和字段一一对应
 
INSERT INTO `grade`(`gradeid`,`gradename`)VALUES(1,'大一'),(2,'大二');

语法:insert into 表名(字段1,字段2,…)values(值1),(值2)…

注意事项:

  1. 字段和字段之间使用英文逗号隔开
  2. 字段是可以省略的,但是后面的值必须要一 一对应,不能少
    例:insert into 表名 values(值…) 里面的值要和表里面的字段全部一一对应
  3. 可以同时插入多条数据,values后面的值,需要使用,隔开

修改

apdate

UPDATE `grade` SET `gradename` = '大1' WHERE `gradeid` = 1;

-- 不指定条件的情况下,会改动表里的这个字段全部的值
UPDATE `grade` SET `gradename` = '大2';

-- 修改多个属性 逗号隔开
UPDATE `grade` SET `gradeid` = 11,`gradename` = '大11' WHERE `gradeid` = 1;

-- 语法
update 表名 set 字段(需要修改的哪个字段) =,.... where = 

条件:where 子句 运算符 id 等于某个值 ,大于某个值,在某个区间内修改…

操作符会返回布尔值

操作符含义范围结果
=等于5=6false
<>或!=不等于5<>6true
>大于5>4true
<小于5<6true
>=大于等于5>=4true
<=小于等于5<=6true
BETWEEN…AND…在某个范围内比如2~5[2,5]
AND我和你&&5>1 and 1>2false
OR我或你||5>1 or 1>2true

语法:UPDATE 表名SET colnum_name=value,… WHERE [条件]

注意:

  • colnum_name是数据库的列,尽量带上
  • 条件,筛选的条件,如果没有指定,则会修改所有的列
  • value,是一个具体的值,也可以是一个变量
  • 多个设置的属性之间,使用英文逗号隔开

删除

delete 命令

语法:delete from 表名 [where 条件]

-- 删除数据(避免这样写,会全部删除)
DELETE FROM 数据库名;

-- 删除指定数据
DELETE FROM 数据库名 WHERE 条件;

truncate命令

作用:完全清空一个数据库表,表的结构和索引约束不会变!

-- 清空表
TRUNCATE 表名;

delete跟truncate区别

  • 相同点:都能删除数据,都不会删除表结构
  • 不同:
    • turncate 重新设置自增列 计数器会归零
    • turncate 不会影响事物

了解即可:delete删除问题,重启数据库现象

  • innoDB 自增列会从1开始(存在内存当中的,断电即失)
  • MylSAM 继续从上一个自增量开始(存在文件中,不会丢失)

DQL查询数据(最重点)

DQL (数据查询语言:)

  • 所有的查询操作都用它 Select
  • 简单的查询,复杂的查询它都能做
  • 数据库中最核心的语言,最重要的语句
  • 使用频率最高的语句

select 语法 [ ] 代表可选的 { } 代表必选的

select [all | distinct] -- 去重
{* | table.* | [table.field1[as alias1],[,table.field2[as alias2]][....]} -- 要查询的字段
from table_name [as table_alias] -- (from 表名  as取别名)
   [left | right | inner join table_name2] -- 联合查询 (... join 要连接的表  on 等值判断)
   [where ...]  -- 指定结果需满足的条件(具体的值,子查询语句)
   [group by ...] -- 指定结果按照哪几个字段来分组(通过哪个字段来进行分组)
   [having]  -- 过滤分组的记录必须满足的次要条件 (过滤分组后的信息,条件和where是一样的,就是上下位置不同)
   [order by ...]  -- 指定查询记录按一个或多个条件排序 (通过哪个字段排序)[升序/降序]
   [limit {[offset,]row_count | row_countOFFSET offset}]; -- 指定查询的记录从哪条至哪条 (起始下标值,页面有多少条记录)
               
-- 查询表中全部信息   
-- SELECT * FROM 表名;
SELECT * FROM `student`;

-- 查询指定字段 
-- SELECT 字段1,字段2... FROM 表名;
SELECT `StudentName`,`Address` FROM `student`;

-- 别名 给结果起一个别名 
-- SELECT 字段1 AS 别名,字段2 AS 别名 FROM 表名;
SELECT `StudentName` AS 姓名,`Address`AS 地区 FROM `student`;

-- 函数 concat(a,b)拼接
SELECT CONCAT('姓名:',StudentName) AS 名字 FROM `student`;

语法:select 字段,…from 表名

有的时候,列名字不是那么的见名知意。就起别名 AS 字段名 as 别名

去重复 distinct

作用:去除select查询出来的结果中重复的数据,重复的数据只显示一条

-- 发现重复数据,去重 
-- SELECT DISTINCT 字段名 FROM 表名;
SELECT DISTINCT `StudentNo` FROM `result`;

数据库的列(表达式)

-- 查询系统版本
SELECT VERSION();
-- 用来计算
SELECT 100*3-1 AS 计算结果
-- 查询自增的步长
SELECT @@auto_increment_increment;

where条件子句

作用:检索数据中符合条件的值

搜索的条件由一个或多个表达式组成!结果 布尔值

逻辑运算符
运算符语法描述
and &&a and b 或 a&&b逻辑与
or ||a or b 或 a||b逻辑或
Not !not a 或 !a逻辑非

语法:

and

select 字段名 from 表名 where 字段名=5 and 字段名=6;

or

select 字段名 from 表名 where 字段名=6 or 字段名=7;

not

select 字段名 from 表名 where not 字段名=5;

模糊查询 :比较运算符
运算符语法描述
is nulla is null如果操作符为null,结果为真
is not nulla is not null如果操作符不为null,结果为真
betweena between b and c若 a 在 b 和 c 之间,则结果为真
likea like bSQL匹配,如果a匹配b,结果为真
ina in (a1,a2,a3…)假设a在a1,或者a2…其中的某一个值中,结果为真
-- Like结合 %(代表0到任意一个字符) _ (表示一个字符)
SELECT `SubjectNo`,`SubjectName` FROM`subject`
WHERE `SubjectName` LIKE '高%';

SELECT `SubjectNo`,`SubjectName` FROM`subject`
WHERE `SubjectName` LIKE 'C#__';

SELECT `SubjectNo`,`SubjectName` FROM`subject`
WHERE SubjectName LIKE '%数%';

-- in (具体的一个或多个值)
SELECT `SubjectNo`,`SubjectName` FROM`subject`
WHERE SubjectNo IN (3,5,2,13);

-- not null 相当于不为空
SELECT `SubjectNo`,`SubjectName` FROM`subject`
WHERE SubjectNo IS NOT NULL;

联表查询

JOIN 对比

思路是必看的,很有用

-- 查询参加了考试的同学(学号,姓名,科目编号,分数)
SELECT * FROM `student`;
SELECT * FROM `result`;
/* 思路
1.分析需求,分析查询的字段来自哪张表,如果超过一张表就使用(连接查询)
2.确定使用哪种连接查询? 有7种
确定交叉点(这两个表中哪个数据是相同的)如果没有交叉点是查不了东西的
判断的条件就是, 学生表中 StudentNo = 成绩表中 StudentNo
就是学生表拥有StudentNo  成绩表也拥有 StudentNo 共同字段
*/

-- join(连接的表) on(判断的条件)  连接查询
-- where 等值查询

-- inner join 内连接--
SELECT s.StudentNo,StudentName,SubjectNo,StudentResult -- 因为有两个同样的字段,所以需要表名区引用,要不然不知道是哪个表的字段
FROM student AS s
INNER JOIN result AS r   -- 定义了别名,就可以用别名区访问东西了,当然表名也可以,只是会稍微繁琐
WHERE s.StudentNo = r.StudentNo  -- 判两张表是否都有这个字段

-- right join 右连接查询--
SELECT student.StudentNo,StudentName,SubjectNo,StudentResult
FROM student 
RIGHT JOIN result   -- 这是不用别名,用表名的操作,比较繁琐
ON student.StudentNo = result.StudentNo 

-- left join 左连接查询--
SELECT s.StudentNo,StudentName,SubjectNo,StudentResult
FROM student s
LEFT JOIN result r   -- 别名的 AS 是可以省略的
ON s.StudentNo = r.StudentNo
操作描述
inner join并集 ,只要有一张表有这个数据就可以拿到,就是多表中有一个匹配就行了
left join左连接,会从左表中返回所有的值,即使右表中没有匹配
right join右连接,会从右表中返回所有的值,即使左表中没有匹配
-- (查询了参加考试同学的信息:学号,学生姓名,科目名,分数)  (StudentNo StudentName SubjectName StudentResult)
/* 思路
1.分析需求,分析查询的字段来自哪张表,如果超过一张表就使用(连接查询)  来自( student , result , subject )表
2.确定使用哪种连接查询? 有7种
确定交叉点(这两个表中哪个数据是相同的)如果没有交叉点是查不了东西的
*/
SELECT s.`StudentNo`,`StudentName`,`SubjectName`,`StudentResult` 
FROM `student` AS s
RIGHT JOIN `result` AS r
ON s.`StudentNo` = r.`StudentNo`
INNER JOIN `subject` AS sub
ON r.`SubjectNo` = sub.`SubjectNo`


-- 步骤
/*
我要查询哪些数据 select ....
从哪几个表中查  frominner(right,left) join 连接的表 on 交叉条件 

假设存在多张表查询,先查询两张表然后再慢慢增加

自连接

自己的表和自己的表连接,核心:一张表拆成两张一样的表即可

-- ==========自连接==============

CREATE TABLE categorg (
	categoryid INT(10) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主题id',
	pid INT(10) NOT NULL COMMENT '父id',
	categoryName VARCHAR(50) NOT NULL COMMENT '主题名字',
	PRIMARY KEY (categoryid)
)ENGINE=INNODB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8

INSERT INTO categorg(categoryid,pid,categoryName)
VALUES(2,1,'信息技术'),
(3,1,'软件开发'),
(4,3,'数据库'),
(5,1,'美术设计'),
(6,3,'web开发'),
(7,5,'ps技术'),
(8,2,'办公信息');
-- 查询父子信息
SELECT f.categoryName AS '父栏目',z.categoryName AS '子栏目'
FROM categorg AS f,categorg AS z
WHERE f.categoryid = z.pid  
-- 就相当于 categoryid的值 = pid的值
-- 比如 办公信息的2=信息技术的2 因为1不匹配 所以是单独的

在这里插入图片描述

在这里插入图片描述

分页和排序

排序 语法:ORDER BY 排序的字段 ASC 或 DESC

-- order by排序:升序asc,降序desc
-- 语法 ORDER BY 排序的字段  ASC(DESC)
-- 根据查询的结果进行排序
SELECT s.StudentNo,StudentName,SubjectName,StudentResult
FROM student s
INNER JOIN `result` r
ON s.StudentNo=r.StudentNo
INNER JOIN `subject` sub
ON r.`SubjectNo` = sub.`SubjectNo`
ORDER BY StudentResult ASC

分页 语法: limit 起始下标值,页面有多少条记录

-- 为什么要分页?
-- 为了缓解数据库压力,给人的体验更好,  还有一种不分页的叫做瀑布流

SELECT s.StudentNo,StudentName,SubjectName,StudentResult
FROM student s
INNER JOIN `result` r
ON s.StudentNo=r.StudentNo
INNER JOIN `subject` sub
ON r.`SubjectNo` = sub.`SubjectNo`
ORDER BY StudentResult ASC
LIMIT 0,5  -- 语法  limit  起始下标值,页面有多少条记录

子查询

where (这个值是计算出来的)

使用子查询 (由里及外) 适用于,后面还加条件的

本质:在 where 语句中嵌套一个子查询语句

-- 查询高等数学的所有考试结果(学号 科目名称 成绩),降序查询
 -- 方式一: 使用连接查询
 SELECT `StudentNo`,SubjectName,StudentResult
 FROM `result` AS r
 INNER JOIN `subject` sub
 ON r.`SubjectNo` = sub.`SubjectNo`
 WHERE  SubjectName LIKE '高等数学%'
 ORDER BY StudentResult DESC
 
 
 -- 方式二:使用子查询 (由里及外)  适用于,后面还加条件的
 -- 查询所有考试结果(学号 科目名称 成绩)>70分的,
 SELECT `StudentNo`,SubjectName,StudentResult
 FROM `result` AS r
 INNER JOIN `subject` sub
 ON r.`SubjectNo` = sub.`SubjectNo`
 WHERE StudentResult>=70 
 -- 在基础上增加一个 且是高等数学的
 AND SubjectName = (
	SELECT SubjectName FROM `subject`
	WHERE SubjectName = '高等数学-1'
 )
  
 -- 改造方式二:查询学生学号,学生姓名  成绩大于70分的
 SELECT StudentNo,StudentName FROM student WHERE StudentNo IN (
	SELECT StudentNo FROM result WHERE StudentResult>70 AND SubjectNo =(
		SELECT SubjectNo FROM `subject` WHERE SubjectName = '高等数学-1'
	)
 )
 

分组和过滤

-- 查询不同课程的平均分,最高分,最低分,平均分大于90
-- 核心:根据不同的课程分组    不分组的话只有一个,按照最高的 最低的来排   因为每个课程都有最高分最低分
SELECT SubjectName,AVG(StudentResult)AS 平均分,MAX(StudentResult)AS 最大值,MIN(StudentResult)AS 最小值
FROM `result` AS r 
INNER JOIN `subject` AS sub
ON r.`SubjectNo` = sub.`SubjectNo`
GROUP BY r.SubjectNo  -- 指定结果按照哪几个字段来分组
HAVING 平均分>=80  -- 过滤分组的记录必须满足的次要条件

MySQL函数

官网:https://dev.mysql.com/doc/refman/5.7/en/func-op-summary-ref.html

常用函数

-- 数学运算
SELECT ABS(-8)  -- abs 绝对值
SELECT CEILING(9.4)  -- ceiling 向上取整
SELECT FLOOR(9.4)  -- floor 向下取整
SELECT RAND()  -- rand 返回一个0~1之间的随机数
SELECT SIGN(7)  -- sign 判断一个数的符号 负数返回-1 正数返回1


-- 字符串函数
SELECT CHAR_LENGTH('dffhf附件四')  -- char_length 字符串长度
SELECT CONCAT('热','爱','编程')  -- concat 拼接字符串
SELECT INSERT('我热爱编程helloword',2,3,'我超级喜欢')  -- insert 查询 从某个位置开始 替换某个长度
SELECT LOWER('ASDDlele')  -- lower 小写字母
SELECT UPPER('asddLELE')  -- upper 大写字母
SELECT INSTR('abcdeh','h')  -- instr 返回第一次出现的字串的索引
SELECT REPLACE('坚持就一定能成功','坚持','努力')  -- replace 替换出现的指定字符串
SELECT SUBSTR('坚持就一定能成功',4,6)  -- substr 返回指定的子字符串 从哪里开始,到哪里结束
SELECT REVERSE('坚持就一定能成功')  -- reverse 反转

		-- 查询性李的  然后替换名字 利
		SELECT REPLACE(StudentName,'李','利') FROM student
		WHERE StudentName LIKE '李%'
 
 
-- 时间和日期函数(记住)
SELECT CURRENT_DATE()  -- current_date 获取当前日期
SELECT CURDATE()  -- curdate 获取当前日期
SELECT NOW()  -- now 获取当前的时间
SELECT LOCALTIME()  -- localtime 本地时间
SELECT SYSDATE()  -- sysdate 系统时间

SELECT YEAR(NOW())  -- year 年
SELECT MONTH(NOW())  -- month 月
SELECT DAY(NOW())  -- day 日
SELECT HOUR(NOW())  -- hour 时
SELECT MINUTE(NOW()) -- minute 分
SELECT SECOND(NOW())  -- second 秒
 

-- 系统
SELECT SYSTEM_USER() -- system_user 获取当前用户
SELECT USER()	-- user 获取当前用户
SELECT VERSION()  -- version 获取当前版本

聚合函数(常用)

函数名称描述
count()计数
sum()求和
avg()平均值
max()最大值
min()最小值
-- ========聚合函数========
-- 都能够统计表中的数据
SELECT COUNT(`StudentNo`)FROM student;  -- count(字段) 会忽略所有的null值
SELECT COUNT(*) FROM student  -- count(*) 不会忽略null值,本质 计算行数
SELECT COUNT(1) FROM student  -- count(1) 不会忽略所有的null值,本质 计算行数

SELECT SUM(StudentResult)AS 总和 FROM result; -- sum 总和
SELECT AVG(StudentResult)AS 平均分 FROM result; -- avg 平均分 
SELECT MAX(StudentResult)AS 最高分 FROM result; -- max 最大值
SELECT MIN(StudentResult)AS 最低分 FROM result; -- min 最小值

数据库级别的MD5加密(扩展)

什么是MD5?

主要增强算法复杂度和不可逆转。

MD5不可逆,具体的值的md5是一样的

MD5破解网站的原理,背后有一个字典,MD5加密后的值,加密的前值

--  ============测试MD5 加密=========
CREATE TABLE `testmd5`(
   `id` INT(4) NOT NULL,
   `name` VARCHAR(20) NOT NULL,
   `pwd` VARCHAR(50) NOT NULL,
   PRIMARY KEY(`id`)
)

-- 明文密码
INSERT INTO `testmd5` VALUES(1,'zhangsan','123456'),(2,'lishi','123456'),(3,'wangwu','123456');
-- 加密
UPDATE testmd5 SET pwd = MD5(pwd) WHERE id = 1;
-- 加密全部的密码
UPDATE testmd5 SET pwd = MD5(pwd);
-- 插入的时候加密
INSERT INTO testmd5 VALUES(4,'xiaoming',MD5('123456')) ;
-- 如何校验:将用户传递进来的密码,进行md5加密,然后比对加密后的值
SELECT * FROM testmd5 WHERE `name` = 'xiaoming' AND pwd = MD5('123456')

事物

什么是事物

要么都成功,要么都失败

将一组SQL放在一批次中去执行~

事物原则:DCID原则 原子性,一致性,隔离性,持久性 (脏读,幻读…)

原子性(atomicity)

要么都成功,要么都失败

一致性(consistency)

事物前后的数据完整性要保证一致

隔离性(isolation)

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

持久性(durability)

事物一旦提交则不可逆,被持久化到数据库中

隔离所导致的一些问题

脏读

指一个事物读取了另外一个事务未提交的数据。

不可重复读

在一个事务内读取表中的某一行数据,多次读取结果不同。(这个不一定是错误,只是某些场合不对)

虚读(幻读)

是指在一个事务内读取到了别的事物插入的数据,导致前后读取不一致。

-- =============事物
-- mysql 是默认开启事务自动提交的
SET autocommit = 0 -- 关闭
SET autocommit = 1 -- 开启(默认的)

-- 手动处理事物
 SET autocommit = 0 -- 关闭自动提交
 -- 事物开启
 START TRANSACTION  -- 标记一个事务的开始,从这个之后的sql都在同一个事物内
 

  -- 提交:持久化(成功)
  COMMIT
  -- 回滚:回到原来的样子(失败)
  ROLLBACK
  
  -- 事务结束
  SET autocommit = 1 -- 开启自动提交

-- 了解
SAVEPOINT 保存点名 -- 设置一个事务的保存点
ROLLBACK TO SAVEPOINT 保存点名 -- 回滚到保存点
RELEASE SAVEPOINT 保存点名 -- 撤销保存点


模拟场景

-- 转账例子
CREATE DATABASE shop CHARACTER SET utf8 COLLATE utf8_general_ci
USE shop

CREATE TABLE `account`(
  `id` INT(3) NOT NULL AUTO_INCREMENT,
  `name` VARCHAR(30) NOT NULL,
  `money` DECIMAL(9,2) NOT NULL,
  PRIMARY KEY(`id`)
)ENGINE=INNODB DEFAULT CHARSET=utf8

INSERT INTO `account`(`name`,`money`)
VALUES('A',2000.00),('B',10000.00)

-- 模拟转账:事务
SET autocommit = 0; -- 关闭自动提交
START TRANSACTION -- 开启一个事物

 UPDATE `account` SET `money` = `money`-500 WHERE `name` = 'A';  -- A-500
 UPDATE `account` SET `money` = `money`+500 WHERE `name` = 'B';  -- B+500
 
 COMMIT;  -- 提交
 ROLLBACK; -- 回滚
 
 SET autocommit = 1;  -- 恢复默认值,开启自动提交

索引

mysql官方对索引的定义为:**索引(index)是帮助mysql高效获取数据的数据结构。

提取句子主干,就可以得到索引的本质:索引是数据结构

索引的分类

在一个表中,主键索引只能有一个,唯一索引可以有多个

  • 主键索引(primary key)
    • 唯一的标识,主键不可重复,只能有一个列作为主键
  • 唯一索引(unique key)
    • 避免重复的列出现,唯一索引可以重复,多个列都可以标识为唯一索引
  • 常规索引(key)
    • 默认的,index、key关键字来设置
  • 全文索引(FullText)
    • 在特定的数据库引擎下才有,MyISAM
    • 快速定位数据 没加索引就是遍历,加了索引就是定位
-- 索引的使用
 -- 1. 在创建表的时候给字段增加索引
 -- 2. 创建完毕后,增加索引
-- 显示所有的索引信息 SHOW INDEX FROM 表名
SHOW INDEX FROM student
 
-- 增加一个全文索引(索引名) 列名
ALTER TABLE `school`.`student` ADD FULLTEXT INDEX `StudentName`(`StudentName`);

-- explain 分析sql执行的状况
EXPLAIN SELECT * FROM student;  -- 非全文索引

EXPLAIN SELECT * FROM student WHERE MATCH(studentName)AGAINST('李')

测试索引

 
CREATE TABLE `app_user` (
  `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
  `name` VARCHAR(50) DEFAULT '' COMMENT '用户名称',
  `email` VARCHAR(50) NOT NULL COMMENT '用户邮箱',
  `phone` VARCHAR(20) DEFAULT '' COMMENT '手机号',
  `gender` TINYINT(4) UNSIGNED DEFAULT '0' COMMENT '性别(0:男 1:女)',
  `password` VARCHAR(100) NOT NULL COMMENT '密码',
  `age` TINYINT(4) DEFAULT '0' COMMENT '年龄',
  `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP,
  `update_time` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY(`id`)
)ENGINE=INNODB DEFAULT CHARSET=utf8mb4 COMMENT='app用户表'

SHOW CREATE DATABASE `school`
SHOW  TABLES;

-- 插入100万数据
DELIMITER $$ -- 写函数之前必须要写,标志

CREATE FUNCTION mock_data()
RETURNS INT
BEGIN
  DECLARE num INT DEFAULT 1000000;
  DECLARE i INT DEFAULT 0;
  
  WHILE i<num DO
	-- 插入语句
	INSERT INTO app_user(`name`,`email`,`phone`,`gender`,`password`,`age`)
	VALUES(CONCAT('用户',i),'254635478@qq.com',CONCAT('18',FLOOR(RAND()*((999999999-100000000)+100000000))),	
	FLOOR(RAND()*2),UUID(),FLOOR(RAND()*100));
	SET i = i+1;
  END WHILE;
  RETURN i;
END;

SELECT mock_data();

SELECT * FROM app_user WHERE `name` = '用户500000'; -- 没创建索引之前执行6秒

-- id_表名_字段名
-- create index 索引名 no 表(字段)
CREATE INDEX id_app_user_name ON app_user(`name`);
SELECT * FROM app_user WHERE `id` = 500000; -- 创建索引之后执行0.007秒

索引在小数据量的时候,用处不大,但是在大数据的时候,区别十分明显

索引原则

  • 索引不是越多越好
  • 不要对进程变动数据加索引
  • 小数据量的表不需要加索引
  • 索引一般加在常用来查询的字段上

索引的数据结构

Hash类型的索引

Btree:InnoDB的默认数据结构

阅读:http://blog.codinglabs.org/articles/theory-of-mysql-index.html

权限管理和数据库备份

用户管理

SQL命令操作

用户表:mysql.user

本质:读这张表进行增删改查

-- 创建用户 CREATE USER 用户名 IDENTIFIED BY '密码';
CREATE USER xun IDENTIFIED BY '123456';

-- 修改密码 (修改当前用户密码)
SET PASSWORD = PASSWORD('111111');

-- 修改密码(修改指定用户密码)
SET PASSWORD FOR xun = PASSWORD('111111');

-- 重命名 RENAME USER 原来名字 TO 新的名字;
RENAME USER xun TO xun2;

-- 用户授权 ALL PRIVILEGES 全部的权限 ,库.表
-- ALL PRIVILEGES 除了给别人权限,其他的都能干
GRANT ALL PRIVILEGES ON *.* TO xun2

-- 查询权限
SHOW GRANTS FOR xun2;  -- 查看指定用户的权限
SHOW GRANTS FOR root@localhost

-- 撤销权限
REVOKE ALL PRIVILEGES ON *.* FROM xun2

-- 删除用户
DROP USER xun2

Mysql备份

为什么要备份:

  • 保证重要的数据不丢失
  • 数据转移

MySQL数据库备份方式

  • 直接拷贝物理文件 data文件夹
  • 在Sqlyog可视化工具手动导出
    • 在想要导出的表过着库中,右键选择备份 然后选择SQL转储
  • 使用命令行导出 mysqldump 命令行使用
# mysqldump -h主机 -u用户名 -p密码 数据库 表名 > 物理磁盘位置/文件名
mysqldump -hlocalhost -uroot -p123456 shop account >E:/aa.sql

# 导出多个表
# mysqldump -h主机 -u用户名 -p密码 数据库 表1 表2 表3 > 物理磁盘位置/文件名

# 导出数据库
mysqldump -h主机 -u用户名 -p密码 数据库 > 物理磁盘位置/文件名

#导入
#登录的情况下,切换到指定的数据库
# source 备份文件
source e:aa.sql

# 或
mysql -u用户名 -p密码 库名<备份文件

规范数据库设计

为什么需要设计

当数据库比较复杂的时候,我们就需要设计了

糟糕的数据库设计:

  • 数据冗余,浪费空间
  • 数据库插入和删除都会麻烦,异常【屏蔽使用物理外键】
  • 程序的性能差

良好的数据库设计:

  • 节省内存空间
  • 保证数据库的完整性
  • 方便我们开发系统

软件开发中,关于数据库的设计

  • 分析需求:分析业务和需要处理的数据库的需求

  • 概要设计:设计关系图E-R图

设计数据库步骤:(个人博客)

  • 手机信息,分析需求
    • 用户表(用户登录注销,用户的个人信息,写博客,创建分类)
    • 分类表(文章分类,谁创建的)
    • 文章表(文章的信息)
    • 评论表
    • 友链表(友链信息)
    • 自定义表(系统信息,某个关键的字,或者一些主字段) key:value
  • 标识实体(把需求落地到每个字段)
  • 标识实体之间的关系
    • 写博客:user -> blog
    • 创建分类:user->category
    • 关注:user->user
    • 友链:links
    • 评论:user-user-blog

三大规范

为什么需要数据规范化

  • 信息重复
  • 更新异常
  • 插入异常
    • 无法正常显示信息
  • 删除异常
    • 丢失有效的信息

三大范式

第一范式(1NF)

原子性:保证每一列不可再分

第二范式(2NF)

前提:满足第一范式

每张表只描述一件事情

第三范式(3NF)

前提:满足第一范式和第二范式

第三范式需要确保数据表中的每一列数据都和主键直接相关,而不能间接相关

(规范数据库的设计)

规范性 和 性能的问题

关联查询的表不得超过三张表

  • 考虑商业化的的需求和目标,(成本,用户体验!)数据库的性能更加重要
  • 在规范性能的问题的时候,需要适当的考虑一下 规范性!
  • 故意给某些表增加一些冗余的字段。(从多表查询中变为单表查询)
  • 故意增加一些计算列(从大数据量降低为小数据量的查询 :索引)

JDBC(重点)

数据库驱动

驱动:声卡、显卡、数据库 都是需要驱动才能运行的

应用程序 会先连接数据库的驱动 后 再从驱动连接数据库 而不是能直接连接数据库

JDBC

SUN公司为了简化开发人员的(对数据库的统一) 操作,提供了一个(Java操作数据库的)规范,俗称JDBC

这些规范的实现由具体的厂商去做

对于开发人员来说,我们只需要掌握JDBC接口的操作即可!

需要导入 java.sql javax.sql 还需要导入一个数据库驱动包 mysql-connector-java-5.1.47.jar

第一个JDBC程序

先创建测试数据库

CREATE DATABASE jdbcStudy CHARACTER SET utf8 COLLATE utf8_general_ci;

USE jdbcStudy;

CREATE TABLE users(
  `id` INT PRIMARY KEY,
  `name` VARCHAR(40),
  `password` VARCHAR(60),
  `email` VARCHAR(60),
  `birthday` DATE
  );
  
 INSERT INTO users(id,`name`,`password`,email,birthday) 
 VALUES(1,'zhangsan','123456','hwaw@qq.com','1980-12-04'),
(2,'lishi','123456','awdf@qq.com','1985-12-04'),
(3,'wangwu','123456','gtre@qq.com','1981-12-04');
  1. 创建一个项目

  2. 导入数据库驱动,自己下载jar包导入也行 用maven导入也行(推荐) mysql-connector-java-5.1.47.jar

  3. 编写测试代码

    package com.kuang.jdbc01;
    
    import java.sql.*;
    
    // 第一个JDBC程序
    public class test01 {
        public static void main(String[] args) throws ClassNotFoundException, SQLException {
            //1.加载驱动
            Class.forName("com.mysql.jdbc.Driver");//固定写法,加载驱动
    
            //2.用户信息和url
            //"jdbc:mysql://localhost:3306/数据库名?支持中文编码&设置中文字符集=utf8&use使用安全的一个连接";
            String url = "jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=true";
            //用户名
            String username = "root";
            //密码
            String password = "123456";
    
            //3.连接成功  后,会返回 数据库对象
            //驱动管理DriverManager  获得连接getConnection   代表数据库connection
            Connection connection = DriverManager.getConnection(url, username, password);
    
            //4.执行SQL的对象     用statement执行sql对象
            Statement statement = connection.createStatement();
    
            //5.执行SQL对象 去 执行SQL, 可能存在结果,查看返回结果
            String sql = "SELECT * FROM users";
            // 查询 sql
            ResultSet resultSet = statement.executeQuery(sql); // 返回的结果集,结果集中封装了我们全部的查询出来的结果
            while(resultSet.next()){
                System.out.println("id"+ resultSet.getObject("id"));
                System.out.println("name"+ resultSet.getObject("name"));
                System.out.println("password"+ resultSet.getObject("password"));
                System.out.println("email"+ resultSet.getObject("email"));
                System.out.println("birthday"+ resultSet.getObject("birthday"));
                System.out.println("============================");
            }
            //6.释放连接
            resultSet.close();
            statement.close();
            connection.close();
        }
    }
    

    步骤总结: 只有查询才有结果集

    1. 加载驱动 Class.forName()
    2. 连接数据库 DriverManager
    3. 获得执行sql的对象 Statement
    4. 获得返回的结果集
    5. 释放连接

    DriverManager

     // DriverManager.registerDriver(new com.mysql.jdbc.Driver());
     Class.forName("com.mysql.jdbc.Driver");//固定写法,加载驱动
    
     // connection 代表数据库
    //数据库设计自动提交
    //事务提交
    //事物回滚
            connection.rollback();
            connection.commit();
            connection.setAutoCommit();
    

    URL

    String url = "jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=true";
    //mysql  -- 3306
    // sTRING URL = 协议://主机地址+端口号/数据库名?参数1&参数2&参数3
    
    // oralce --1521
    // jdbc:oracle:thin:@localhost:1521:sid
    

    Statement执行SQL的对象

    String sql = "SELECT * FROM users"; //编写SQL
    
    statement.executeQuery();  //查询操作 返回ResultSet(结果集)
    statement.execute();        //执行任何sql
    statement.executeUpdate();  //更新、插入、删除、都使用这个,返回一个受影响的行数
    

    ResultSet 查询的结果集:封装了所有的查询结果

    获得指定的数据类型

            resultSet.getObject();// 在不知道列类型的情况下使用
            //如果知道列的类型就使用指定的类型
            resultSet.getString();
            resultSet.getInt();
            resultSet.getFloat();
            resultSet.getTime();
    		...
    

    遍历,指针

            resultSet.beforeFirst();//移动到最前面
            resultSet.afterLast();//移动到最后面
            resultSet.next();//移动到下一个数据
            resultSet.previous();//移动到下一行
            resultSet.absolute(row);//指定到移动行
    

    释放资源

    resultSet.close();
    statement.close();
    connection.close();
    //耗资源 ,用完关掉
    

statement对象

jdbc中的 statement 对象用于向数据库发送SQL语句,想完成对数据库的增删改查,只需要通过这个对象向数据库发送增删改查语句即可

Statement 对象的 executeUpdate 方法,用于向数据库发送增、删、改的sql语句,executeUpdate 执行完后,将会返回一个整数(即增删改语句导致了数据库几行数据发生了变化)。

Statement.executeQuery 方法用于向数据库发送查询语句,executeQuery 方法返回代表查询结果的ResultSet对象

CRUD操作-create

使用 executeUpdate (String sql)方法完成数据添加操作,示例操作:

Statement statement = connection.createStatement();
String sql = "insert into users(...)values(...)";
int num = statement.executeUpdate(sql);
if(num>0){
    System.out.println("插入成功!!!");
}

CRUD操作-delete

使用 executeUpdate(String sql)方法完成数据删除操作,示例操作:

Statement statement = connection.createStatement();
String sql = "delete from users where id=1";
int sum = statement.executeUpdate(sql);
if(sum>0){
    System.out.println("删除成功!!!");
}

CRUD操作-update

使用executeUpdate(String sql)方法完成数据修改操作,示例操作:

Statement statement = connection.createStatement();
String sql = "update users set name = '' where name = ''";
int sum = statement.executeUpdate(sql);
if(sum>0){
    System.out.println("修改成功");
}

CRUD操作-read

使用 executeQuery(String sql)方法完成数据查询操作,实例操作:

Statement statement = connection.createStatement();
String sql = "select * from users where id=1";
ResultSet rs = statement.executeQuery(sql);
while(rs.next()){
    //根据获取列的数据类型,分别调用rs的相应方法映射到java对象中
}
代码实现

1、创建配置文件

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=true
username=root
password=123456

2、提取工具类

package com.kuang.jdbc02.utils;

import java.io.InputStream;
import java.sql.*;
import java.util.Properties;

public class JdbcUtils {
    private static String driver=null;
    private static String url=null;
    private static String username=null;
    private static String password=null;
    static{
        try{     //反射获取类的装载器,然后在获取资源(db.properties)
            InputStream in = JdbcUtils.class.getClassLoader().getResourceAsStream("db.properties");
            //属性
            Properties properties = new Properties();
            //加载获取到的属性
            properties.load(in);
//            从文件把这4个信息读取出来
            driver = properties.getProperty("driver");
            url = properties.getProperty("url");
            username = properties.getProperty("username");
            password = properties.getProperty("password");

            //1.驱动只用加载一次
            Class.forName(driver);
        }catch (Exception e) {
            e.printStackTrace();
        }
    }
    //获取连接
    public static Connection getConnection() throws SQLException {
        return  DriverManager.getConnection(url, username, password);
    }
    //释放资源
    public static void release(Connection conn, Statement sta, ResultSet res) throws SQLException {
        if (res!=null){
            res.close();
        }
        if (sta!=null){
            sta.close();
        }
        if (conn!=null){
            conn.close();
        }
    }
}

3、编写增删改

增 insert

package com.kuang.jdbc02;

import com.kuang.jdbc02.utils.JdbcUtils;

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;

public class TestInsert {

    public static void main(String[] args) throws SQLException {

            Connection connection = JdbcUtils.getConnection(); //通过工具类获取链接

            Statement statement = connection.createStatement();//获得SQL的执行对象
            String sql = "INSERT INTO users(id,`name`,`password`,`email`,`birthday`) VALUES(5,'fenqi','123456','hfghs@qq.com','1985-12-04')";
            int i = statement.executeUpdate(sql);
            if (i>0){
                System.out.println("插入成功");
            }
            JdbcUtils.release(null,null,null); //释放资源
    }
}


删 Delete

package com.kuang.jdbc02;

import com.kuang.jdbc02.utils.JdbcUtils;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class TestSelete {
    public static void main(String[] args) throws SQLException {
        //通过工具类获取数据库连接
        Connection connection = JdbcUtils.getConnection();
        //获得SQL的执行对象
        Statement statement = connection.createStatement();
        //编写SQL语句
        String sql = "select  * from users";
        //执行
        ResultSet resultSet = statement.executeQuery(sql);

        while(resultSet.next()){
            System.out.println("id"+resultSet.getInt("id"));
            System.out.println("name"+resultSet.getString("name"));
            System.out.println("password"+resultSet.getString("password"));
            System.out.println("email"+resultSet.getString("email"));
            System.out.println("birthday"+resultSet.getDate("birthday"));
            System.out.println("=================");
        }
        JdbcUtils.release(null,null,null);
    }
}

改 Update

package com.kuang.jdbc02;

import com.kuang.jdbc02.utils.JdbcUtils;

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;

public class TestUpdate {
    public static void main(String[] args) throws SQLException {
        //通过工具类获取连接
        Connection connection = JdbcUtils.getConnection();
        //获得SQL的执行对象
        Statement statement = connection.createStatement();
        //编写SQL语句
        String sql = "update users set name='xiugai' where id = 1";
        //执行
        int i = statement.executeUpdate(sql);

        if (i>0){
            System.out.println("更新成功");
        }
        JdbcUtils.release(null,null,null);
    }
}

查 Select

package com.kuang.jdbc02;

import com.kuang.jdbc02.utils.JdbcUtils;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class TestSelete {
    public static void main(String[] args) throws SQLException {
        //通过工具类获取数据库连接
        Connection connection = JdbcUtils.getConnection();
        //获得SQL的执行对象
        Statement statement = connection.createStatement();
        //编写SQL语句
        String sql = "select  * from users";
        //执行
        ResultSet resultSet = statement.executeQuery(sql);

        while(resultSet.next()){
            System.out.println("id"+resultSet.getInt("id"));
            System.out.println("name"+resultSet.getString("name"));
            System.out.println("password"+resultSet.getString("password"));
            System.out.println("email"+resultSet.getString("email"));
            System.out.println("birthday"+resultSet.getDate("birthday"));
            System.out.println("=================");
        }
        JdbcUtils.release(null,null,null);
    }
}

sql注入问题

sql存在漏洞,会被攻击导致数据泄露,SQL会被拼接or

package com.kuang.jdbc02;

import com.kuang.jdbc02.utils.JdbcUtils;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class SQL注入 {
    public static void main(String[] args) throws SQLException {
        //正常登录
        // login("lishi","123456");
        login(" 'or' 1=1"," 'or' 1=1");

    }
    public static void login(String username,String password) throws SQLException {
        //通过工具类获取数据库连接
        Connection connection = JdbcUtils.getConnection();
        //获得SQL的执行对象
        Statement statement = connection.createStatement();
        //编写SQL语句
        String sql = "select * from users where `name` = '"+username+"' and password='"+password+"'";
        //执行
        ResultSet resultSet = statement.executeQuery(sql);
        while(resultSet.next()){
            System.out.println(resultSet.getString("name"));
            System.out.println(resultSet.getString("password"));
            System.out.println("======================");
        }
    }
}

PreparedStatement对象

PreparedStatement 可以防止SQL注入。效果更好

1、新增

package com.kuang.jdbc03;

import com.kuang.jdbc02.utils.JdbcUtils;

import java.sql.Connection;
import java.util.Date;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class TestInsert {
    public static void main(String[] args) throws SQLException {
        //调用工具类获取数据库连接
        Connection connection = JdbcUtils.getConnection();
        //使用? 占位符代表参数
        String sql = "insert into users(id,name,password,email,birthday)values(?,?,?,?,?)";
        //预编译SQL,先写sql,然后不执行
        PreparedStatement pr = connection.prepareStatement(sql);
        //手动给参数赋值
        pr.setInt(1,5); //第一个值为参数下标,第二个为参数 id
        pr.setString(2,"王胜");
        pr.setString(3,"123456");
        pr.setString(4,"jukk@qq.com");
        // 注意点:sql.Date     数据库 java.sql.Date()
        //       util.Date     Java   new Date().getTime() 获得时间戳
        pr.setDate(5,new java.sql.Date(new Date().getTime()));

        //执行
        int i = pr.executeUpdate();

        if (i>0){
            System.out.println("插入成功");
        }
        JdbcUtils.release(null,null,null);
    }
}

2、删除

package com.kuang.jdbc03;

import com.kuang.jdbc02.utils.JdbcUtils;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class TestDelete {
    public static void main(String[] args) throws SQLException {
        //通过工具类获取数据库的连接
        Connection connection = JdbcUtils.getConnection();
        //使用占位符?   代表参数
        String sql = "delete from users where id = ?";
        //预编译SQL  先写sql 然后不执行
        PreparedStatement pre = connection.prepareStatement(sql);
        //手动给参数赋值
        pre.setInt(1,5);
        //执行
        int i = pre.executeUpdate();

        if (i>0){
            System.out.println("删除成功");
        }
        //释放资源
        JdbcUtils.release(null,null,null);
    }
}

3、更新

package com.kuang.jdbc03;

import com.kuang.jdbc02.utils.JdbcUtils;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class TestUpdate {
    public static void main(String[] args) throws SQLException {
        //通过工具类获取数据库的连接
        Connection connection = JdbcUtils.getConnection();
        //使用占位符?  代表参数
        String sql = "update users set name = ? where  name = ?";
        //预编译SQL  先写SQL 然后不执行
        PreparedStatement pre = connection.prepareStatement(sql);
        //手动给参数赋值
        pre.setString(1,"zhangsan");
        pre.setString(2,"xiugai");
        //执行
        int i = pre.executeUpdate();

        if (i>0){
            System.out.println("修改成功");
        }
        //关闭资源
        JdbcUtils.release(null,null,null);
    }
}

4、查询

package com.kuang.jdbc03;

import com.kuang.jdbc02.utils.JdbcUtils;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class TestSelete {
    public static void main(String[] args) throws SQLException {
        //通过工具类获取数据库的连接
        Connection connection = JdbcUtils.getConnection();
        //使用占位符?  代表参数
        String sql = "select * from users where id=?";
        //预编译SQL  先写sql  然后不执行
        PreparedStatement pre = connection.prepareStatement(sql);
        //手动传递参数
        pre.setObject(1,2);
        //执行
        ResultSet resultSet = pre.executeQuery();

        while (resultSet.next()){
            System.out.println(resultSet.getInt("id"));
            System.out.println(resultSet.getString("name"));
            System.out.println(resultSet.getString("password"));
        }
        //释放资源
        JdbcUtils.release(null,null,null);
    }
}

5、防止Sql注入

package com.kuang.jdbc03;

import com.kuang.jdbc02.utils.JdbcUtils;

import java.sql.*;

public class SQL注入 {
    public static void main(String[] args) throws SQLException {
        //正常登录
        // login("lishi","123456");
        login("'' or 1=1","123456");

    }
    public static void login(String username,String password) throws SQLException {
        //通过工具类获取数据库连接
        Connection connection = JdbcUtils.getConnection();
        //使用占位符? 传递参数
        //PreparedStatement放置SQL注入的本质,把值传递进来的参数当作字符
        //假设其中存在转义字符,比如说 ‘ 会被直接转义
        String sql = "select * from users where name = ? and password = ?";
        //预编译Sql  先写sql 然后不执行
        PreparedStatement pre = connection.prepareStatement(sql);
        //手动传递参数
        pre.setString(1,username);
        pre.setString(2,password);
        //执行
        ResultSet resultSet = pre.executeQuery();

        while(resultSet.next()){
            System.out.println(resultSet.getString("name"));
            System.out.println(resultSet.getString("password"));
            System.out.println("======================");
        }
        //释放资源
        JdbcUtils.release(null,null,null);
    }
}

idea连接数据库

  1. 找到右侧的Database 点击进去

  2. 点击 + 号 → 选择Data Source →选择MySql

  3. 然后输入用户名 和 密码 → 点击 Test Connection

    • 成功的话会显示✔

      • 然后 点击Apply →点击上面一点的Schemas → 然后选择要添加的数据库就可以了
    • 不成功的话,要么是时区异常要么就是版本没选好

      • 左侧NySQL点击进去

      • Class选择 com.musql.jdbc.Driver

      • Driver files 选择你对应的SQL版本 → 然后就可以点击Apply → 然后就去重新输入账号密码连接

      • 时区的话就需要打开命令行的sql然后设计时区了

        • -- 查看时区,显示 SYSTEM 就是没有设置时区啦。
          show variables like'%time_zone';
          
        • --设置时区 设置完需要重新连接sql重新查看
          set global time_zone = '+8:00';
          

事物

要么都成功,要么都失败

ACID原则

原子性:

​ 要么全部完成,要么都不完成

一致性:

​ 总数不变

隔离性:

​ 多个进程互不干扰

持久性:

​ 一旦提交不可逆,持久化到数据库

隔离性问题:

脏读:一个事务读取了另一个没有提交的事物

不可重复读:在同一个事物内,重复读取表中的数据,表数据发生了改变

虚读(幻读):在一个事务内,读取到了别人插入的数据,导致前后读出来结果不一致

代码实现

​ 1、开启事务 conn.setAutoCommit(false);

​ 2、一组业务执行完毕,提交事务

​ 3、可以在catch语句中显示的定义 回滚语句,但默认失败就会回滚

package com.kuang.jdbc04;

import com.kuang.jdbc02.utils.JdbcUtils;
import com.sun.org.apache.xpath.internal.objects.XNull;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class TestTransation1 {
    public static void main(String[] args) throws SQLException {
         Connection conn = null;
        PreparedStatement st = null;
        try {
            //通过工具类连接数据库
             conn = JdbcUtils.getConnection();
            //关闭数据库的自动提交,会自动开启事物
            conn.setAutoCommit(false);

            //编写SQL语句
            String sql1 = "update account set miney = miney-100 where name = 'A'";
            //预编译SQL
            st = conn.prepareStatement(sql1);
            st.executeUpdate();

            //编写SQL语句
            String sql2 = "update account set miney = miney+100 where name = 'B'";
            //预编译SQL
            st= conn.prepareStatement(sql2);
            st.executeUpdate();

            //业务完毕,提交事务
            conn.commit();
            System.out.println("成功");
        }catch (Exception e){
            try{
                conn.rollback(); //如果失败则回滚事物
            } catch (SQLException throwables)
            {
                throwables.printStackTrace();
            }
            e.printStackTrace();
        }finally {
            //释放资源
            JdbcUtils.release(conn,st,null);
        }

    }
}

数据库连接池

数据库连接 - 执行完毕 - 释放

连接-释放 十分浪费系统资源

池化技术:准备一些预先的资源,过来就连接预先准备好的

例子(银行)

比如去银行办理业务,开门-办理-关门 下一个 开门-办理-关门, 这是平常数据库的操作,很繁琐麻烦

使用连接池 开门-安排业务员-等待-办理-不使用了就彻底关门

常用连接数10个

最小连接数:10(按照常用连接数设计最小连接数)

最大连接数:(业务最高承载上线,超过了就需要等待排队)

等待超时:(超过等待的时间就不等了)

编写连接池,只需要实现一个接口 DataSource

开源数据源实现(拿来即用)

​ DBCP

​ C3P0

​ Druid:阿里巴巴

使用了这些数据库连接池之后我们在项目开发中就不需要编写连接数据库的代码了!

DBCP

​ 需要用到的jar包

​ commons-dbcp-1.4、commons-pool-1.6

C3P0

需要用到的jar包

c3p0-0.9.5.5、mchange-commons-java-0.2.19

结论

无论使用什么数据源,本质还是一样的 ,DataSource接口不会变,方法就不会变# MySql

  • MySQL所使用的 SQL 语言是用于访问数据库的最常用du标准化语言。MySQL 软件采用了双授权政策,分为社区版和商业版,由于其体积小、速度快、总体拥有成本低,尤其是开放源码这一特点,一般中小型网站的开发都选择 MySQL 作为网站数据库。
  • MySQL是一个关系型数据库管理系统
  • 由瑞典MySQL AB 公司开发
  • 现属于 Oracle 旗下产品
  • MySQL是最好的 RDBMS (Relational Database Management System,关系数据库管理系统) 应用软件之一
  • 开源的数据库软件
  • 体积小、速度快、总体拥有成本低,招人成本比较低,所有人必须会
  • 官网:https://www.mysql.com/

安装Mysql

  1. 解压

  2. 把这个包放到自己的电脑环境目录下~

  3. 配置环境变量

    1. 电脑环境Path新建一个 然后复制安装路径D:\mysql\mysql-8.0.21\bin
  4. 新建mysql配置文件

    [mysqld]
    # 目录要换成自己的	
    basedir=D:\mysql\mysql-8.0.21\
    datadir=D:\mysql\mysql-8.0.21\data\
    port=3306
    skip-grant-tables
    
  5. 启动管理员模式下的CMD,进入到mysql的bin目录下

  6. 安装mysql服务

    • mysqld -install
  7. 初始化数据库文件

    • mysqld --initialize-insecure
  8. 启动

    • net start mysql
  9. 默认密码为空,登录命令

    • mysql -uroot -p
  10. 密码修改 由于是Mysql8.0,所以不支持password函数的使用

    • 选择数据库 use mysql
    • 修改密码 alter user ‘root’@‘localhost’ identified by ‘123456’;
  11. 刷新:flush privileges; 刷新后退出:exit; 退出再重新登录即可

命令行

--连接数据库
mysql -u root -p123456
--修改用户密码
update mysql.user set authentication_string=password('123456') where user='root' and Host = 'localhost';
-- 查看时区,显示 SYSTEM 就是没有设置时区啦。
show variables like'%time_zone';
--设置时区 设置完需要重新连接sql重新查看
set global time_zone = '+8:00';
--刷新权限
flush privileges;

--所有的sql语句都使用;结尾

--查看所有的数据库
show databases;
--切换数据库
use 数据库名
--查看数据库中所有的表
show tables;
--显示表中所有的信息
describe 表名

---------------------
--创建一个数据库
create database 数据库名
--退出连接
exit;
--单行注释
/*多行注释*/

数据库xxx语言

DDL 数据库控制语言

DML 数据库操作语言

DQL 数据库查询语言

DCL 数据库控制语言

操作数据库

操作数据库>操作数据库中的表>操作数据库中表的数据

mysql关键字不区分大小写 [ ]表示可选

**如果你的表明或数据库名是一个特殊字符的话要加`` **

操作数据库

  1. 创建数据库

    CREATE DATABASE [IF NOT EXISTS] 数据库名
    
  2. 删除数据库

    DROP DATABASE IF EXISTS 数据库名
    
  3. 使用数据库

    USE `数据库名`
    
  4. 查看数据库

    SHOW DATABASE
    

数据库的列类型

数值

  • tinyint 十分小的数据 1个字节
  • smallint 较小的数据 2个字节
  • mediumint 中等大小的数据 3个字节
  • int 标准的整数 4个字节 常用的
  • bigint 较大的数据 8个字节
  • float 浮点数 4个字节
  • double 浮点数 8个字节(精度问题)
  • decimal 字符串形式的浮点数 金融计算的时候,一般使用decimal

字符串

  • char 字符串固定大小 0~255
  • varchar 可变字符串 0~65535 常用的变量 String
  • tinytext 微型文本 2^8-1
  • text 文本串 2^16-1 保存大文本

时间日期

  • date YYYY-MM-DD 日期格式
  • time HH:mm:ss 时间格式
  • datetime YYYY-MM-DD HH:mm:ss 最常用的时间格式
  • timestamp 时间戳 1970.1.1到现在的毫秒数 也较为常用
  • year 年份表示

null

  • 没有值 未知
  • 不要使用null进行运算,结果为null

数据库的字段属性(重点)

Unsigned:

  • 无符号的整数
  • 声明了该列不能声明为负数

zerofill:

  • 0填充的
  • 不足的位数,使用0来填充,int(3) ,5 — 005

自增

  • 通常理解为自增,自动在上一条记录的基础上+1 (默认)
  • 通常用来设计唯一的主键,必须是整数类型
  • 可以自定义设计主键自增的起始值和步长

非空NULL not null

  • 设置默认的值!
  • sex,默认值为男,如果不指定该列的值,则会有默认的值

扩展

-- 每一个表都必须存在以下五个字段,未来做项目用的,表示一个表记录存在的意义
id 主键
version 乐观锁
is_delete 伪删除
gmt_create 创建时间
gmt_update 修改时间

创建数据库表(重点)

CREATE TABLE IF NOT EXISTS `student`(
  `id` INT(4) NOT NULL AUTO_INCREMENT COMMENT '学号',
  `name` VARCHAR(30) NOT NULL DEFAULT '匿名' COMMENT '姓名',
  `pwd` VARCHAR(20) NOT NULL DEFAULT '123456' COMMENT '密码',
  `set` VARCHAR(2) NOT NULL DEFAULT '男' COMMENT '性别',
  `birthday` DATETIME DEFAULT NULL COMMENT '出生日期',
  `address` VARCHAR(100) DEFAULT NULL COMMENT '家庭地址',
  `email` VARCHAR(50) DEFAULT NULL COMMENT '邮箱',
  PRIMARY KEY(`id`)
)ENGINE=INNODB DEFAULT CHARSET=utf8

格式: [ ] 表示可选的 IF NOT EXISTS判断是否存在

CREATE TABLE [IF NOT EXISTS] `表名`(
    `字段名` 列类型 [非空] [自增] [注释] '学号',
    `字段名` 列类型 [非空] [默认值] '匿名' [注释] '姓名'.....(注意最后一行不要加,)
    )[引擎]ENGINE=INNODB[字符集]CHARSET=utf8)

常用命令

 -- 查看数据库的语句
SHOW CREATE DATABASE 数据库名;

-- 查看表中数据的定义语句
SHOW CREATE TABLE 表名; 

-- 显示表的结构
DESC 表名; 

修改删除表

修改

-- 修改表名 ALTER TABLE 旧表名 RENAME AS 新表名;
ALTER TABLE student RENAME AS student1;

-- 增加表的字段  ALTER TABLE 表名 ADD 字段名 列属性;
ALTER TABLE student1 ADD age INT(11);

-- 修改表的字段约束 ALTER TABLE 表名 MODIFY 字段名 列属性;
ALTER TABLE student1 MODIFY age VARCHAR(20);

-- 修改表字段名 ALTER TABLE 表名 CHANGE 旧字段 新字段 列属性;
ALTER TABLE student1 CHANGE age age1 INT(1);

-- 删除表字段 ALTER TABLE 表名 DROP 字段名;
ALTER TABLE student1 DROP age1;

删除

-- 删除表 (判断表存在 在删除)
DROP TABLE IF EXISTS student1;

MySQL数据管理

外键(了解即可)

方式一、在创建表的时候,增加约束(麻烦,比较复杂)

CREATE TABLE IF NOT EXISTS `grade`(
  `gradeid` INT(10) NOT NULL AUTO_INCREMENT COMMENT '年级id',
  `gradename` VARCHAR(50) NOT NULL COMMENT '年级名称',
  PRIMARY KEY(`gradeid`)
)ENGINE=INNODB DEFAULT CHARSET=utf8

-- 学生表的gradeid字段要去引用年级表的gradeid
-- 定义外键 key
-- 给这个外键添加约束(执行引用) references 引用

CREATE TABLE IF NOT EXISTS `student`(
  `id` INT(4) NOT NULL AUTO_INCREMENT COMMENT '学号',
  `name` VARCHAR(30) NOT NULL DEFAULT '匿名' COMMENT '姓名',
  `pwd` VARCHAR(20) NOT NULL DEFAULT '123456' COMMENT '密码',
  `set` VARCHAR(2) NOT NULL DEFAULT '男' COMMENT '性别',
  `birthday` DATETIME DEFAULT NULL COMMENT '出生日期',
  `gradeid` INT(10) NOT NULL COMMENT '学生的年级',
  `address` VARCHAR(100) DEFAULT NULL COMMENT '家庭地址',
  `email` VARCHAR(50) DEFAULT NULL COMMENT '邮箱',
  PRIMARY KEY(`id`),
  KEY `FK_gradeid` (`gradeid`), --定义外键 key
  CONSTRAINT `FK_gradeid` FOREIGN KEY (`gradeid`) REFERENCES `grade` (`gradeid`) -- 
)ENGINE=INNODB DEFAULT CHARSET=utf8

删除有外键关系的表时,必须先删除引用别人的表(从表),在删除被引用的表(主表)

方式二、创建表成功后,添加外键约束

CREATE TABLE IF NOT EXISTS `grade`(
  `gradeid` INT(10) NOT NULL AUTO_INCREMENT COMMENT '年级id',
  `gradename` VARCHAR(50) NOT NULL COMMENT '年级名称',
  PRIMARY KEY(`gradeid`)
)ENGINE=INNODB DEFAULT CHARSET=utf8

CREATE TABLE IF NOT EXISTS `student`(
  `id` INT(4) NOT NULL AUTO_INCREMENT COMMENT '学号',
  `name` VARCHAR(30) NOT NULL DEFAULT '匿名' COMMENT '姓名',
  `pwd` VARCHAR(20) NOT NULL DEFAULT '123456' COMMENT '密码',
  `set` VARCHAR(2) NOT NULL DEFAULT '男' COMMENT '性别',
  `birthday` DATETIME DEFAULT NULL COMMENT '出生日期',
  `gradeid` INT(10) NOT NULL COMMENT '学生的年级',
  `address` VARCHAR(100) DEFAULT NULL COMMENT '家庭地址',
  `email` VARCHAR(50) DEFAULT NULL COMMENT '邮箱',
  PRIMARY KEY(`id`)
)ENGINE=INNODB DEFAULT CHARSET=utf8

-- ALTER TABLE 表名 ADD CONSTRAINT 约束名 FOREIGN KEY(作为外键的列) REFERENCES 哪个表(哪个字段);
ALTER TABLE `student` ADD CONSTRAINT `FK_gradeid` FOREIGN KEY(`gradeid`) REFERENCES `grade`(`gradeid`);

DML语言(全部记住)

数据库的意义 :数据存储,数据管理

DML语言:数据操作语言

  • insert
  • update
  • delete

添加

insert

-- insert into 表名(字段1,字段2,...)values(值1),(值2)
-- 一般写插入语句,我们一定要数据和字段一一对应
 
INSERT INTO `grade`(`gradeid`,`gradename`)VALUES(1,'大一'),(2,'大二');

语法:insert into 表名(字段1,字段2,…)values(值1),(值2)…

注意事项:

  1. 字段和字段之间使用英文逗号隔开
  2. 字段是可以省略的,但是后面的值必须要一 一对应,不能少
    例:insert into 表名 values(值…) 里面的值要和表里面的字段全部一一对应
  3. 可以同时插入多条数据,values后面的值,需要使用,隔开

修改

apdate

UPDATE `grade` SET `gradename` = '大1' WHERE `gradeid` = 1;

-- 不指定条件的情况下,会改动表里的这个字段全部的值
UPDATE `grade` SET `gradename` = '大2';

-- 修改多个属性 逗号隔开
UPDATE `grade` SET `gradeid` = 11,`gradename` = '大11' WHERE `gradeid` = 1;

-- 语法
update 表名 set 字段(需要修改的哪个字段) =,.... where = 

条件:where 子句 运算符 id 等于某个值 ,大于某个值,在某个区间内修改…

操作符会返回布尔值

操作符含义范围结果
=等于5=6false
<>或!=不等于5<>6true
>大于5>4true
<小于5<6true
>=大于等于5>=4true
<=小于等于5<=6true
BETWEEN…AND…在某个范围内比如2~5[2,5]
AND我和你&&5>1 and 1>2false
OR我或你||5>1 or 1>2true

语法:UPDATE 表名SET colnum_name=value,… WHERE [条件]

注意:

  • colnum_name是数据库的列,尽量带上
  • 条件,筛选的条件,如果没有指定,则会修改所有的列
  • value,是一个具体的值,也可以是一个变量
  • 多个设置的属性之间,使用英文逗号隔开

删除

delete 命令

语法:delete from 表名 [where 条件]

-- 删除数据(避免这样写,会全部删除)
DELETE FROM 数据库名;

-- 删除指定数据
DELETE FROM 数据库名 WHERE 条件;

truncate命令

作用:完全清空一个数据库表,表的结构和索引约束不会变!

-- 清空表
TRUNCATE 表名;

delete跟truncate区别

  • 相同点:都能删除数据,都不会删除表结构
  • 不同:
    • turncate 重新设置自增列 计数器会归零
    • turncate 不会影响事物

了解即可:delete删除问题,重启数据库现象

  • innoDB 自增列会从1开始(存在内存当中的,断电即失)
  • MylSAM 继续从上一个自增量开始(存在文件中,不会丢失)

DQL查询数据(最重点)

DQL (数据查询语言:)

  • 所有的查询操作都用它 Select
  • 简单的查询,复杂的查询它都能做
  • 数据库中最核心的语言,最重要的语句
  • 使用频率最高的语句

select 语法 [ ] 代表可选的 { } 代表必选的

select [all | distinct] -- 去重
{* | table.* | [table.field1[as alias1],[,table.field2[as alias2]][....]} -- 要查询的字段
from table_name [as table_alias] -- (from 表名  as取别名)
   [left | right | inner join table_name2] -- 联合查询 (... join 要连接的表  on 等值判断)
   [where ...]  -- 指定结果需满足的条件(具体的值,子查询语句)
   [group by ...] -- 指定结果按照哪几个字段来分组(通过哪个字段来进行分组)
   [having]  -- 过滤分组的记录必须满足的次要条件 (过滤分组后的信息,条件和where是一样的,就是上下位置不同)
   [order by ...]  -- 指定查询记录按一个或多个条件排序 (通过哪个字段排序)[升序/降序]
   [limit {[offset,]row_count | row_countOFFSET offset}]; -- 指定查询的记录从哪条至哪条 (起始下标值,页面有多少条记录)
               
-- 查询表中全部信息   
-- SELECT * FROM 表名;
SELECT * FROM `student`;

-- 查询指定字段 
-- SELECT 字段1,字段2... FROM 表名;
SELECT `StudentName`,`Address` FROM `student`;

-- 别名 给结果起一个别名 
-- SELECT 字段1 AS 别名,字段2 AS 别名 FROM 表名;
SELECT `StudentName` AS 姓名,`Address`AS 地区 FROM `student`;

-- 函数 concat(a,b)拼接
SELECT CONCAT('姓名:',StudentName) AS 名字 FROM `student`;

语法:select 字段,…from 表名

有的时候,列名字不是那么的见名知意。就起别名 AS 字段名 as 别名

去重复 distinct

作用:去除select查询出来的结果中重复的数据,重复的数据只显示一条

-- 发现重复数据,去重 
-- SELECT DISTINCT 字段名 FROM 表名;
SELECT DISTINCT `StudentNo` FROM `result`;

数据库的列(表达式)

-- 查询系统版本
SELECT VERSION();
-- 用来计算
SELECT 100*3-1 AS 计算结果
-- 查询自增的步长
SELECT @@auto_increment_increment;

where条件子句

作用:检索数据中符合条件的值

搜索的条件由一个或多个表达式组成!结果 布尔值

逻辑运算符
运算符语法描述
and &&a and b 或 a&&b逻辑与
or ||a or b 或 a||b逻辑或
Not !not a 或 !a逻辑非

语法:

and

select 字段名 from 表名 where 字段名=5 and 字段名=6;

or

select 字段名 from 表名 where 字段名=6 or 字段名=7;

not

select 字段名 from 表名 where not 字段名=5;

模糊查询 :比较运算符
运算符语法描述
is nulla is null如果操作符为null,结果为真
is not nulla is not null如果操作符不为null,结果为真
betweena between b and c若 a 在 b 和 c 之间,则结果为真
likea like bSQL匹配,如果a匹配b,结果为真
ina in (a1,a2,a3…)假设a在a1,或者a2…其中的某一个值中,结果为真
-- Like结合 %(代表0到任意一个字符) _ (表示一个字符)
SELECT `SubjectNo`,`SubjectName` FROM`subject`
WHERE `SubjectName` LIKE '高%';

SELECT `SubjectNo`,`SubjectName` FROM`subject`
WHERE `SubjectName` LIKE 'C#__';

SELECT `SubjectNo`,`SubjectName` FROM`subject`
WHERE SubjectName LIKE '%数%';

-- in (具体的一个或多个值)
SELECT `SubjectNo`,`SubjectName` FROM`subject`
WHERE SubjectNo IN (3,5,2,13);

-- not null 相当于不为空
SELECT `SubjectNo`,`SubjectName` FROM`subject`
WHERE SubjectNo IS NOT NULL;

联表查询

JOIN 对比

思路是必看的,很有用

-- 查询参加了考试的同学(学号,姓名,科目编号,分数)
SELECT * FROM `student`;
SELECT * FROM `result`;
/* 思路
1.分析需求,分析查询的字段来自哪张表,如果超过一张表就使用(连接查询)
2.确定使用哪种连接查询? 有7种
确定交叉点(这两个表中哪个数据是相同的)如果没有交叉点是查不了东西的
判断的条件就是, 学生表中 StudentNo = 成绩表中 StudentNo
就是学生表拥有StudentNo  成绩表也拥有 StudentNo 共同字段
*/

-- join(连接的表) on(判断的条件)  连接查询
-- where 等值查询

-- inner join 内连接--
SELECT s.StudentNo,StudentName,SubjectNo,StudentResult -- 因为有两个同样的字段,所以需要表名区引用,要不然不知道是哪个表的字段
FROM student AS s
INNER JOIN result AS r   -- 定义了别名,就可以用别名区访问东西了,当然表名也可以,只是会稍微繁琐
WHERE s.StudentNo = r.StudentNo  -- 判两张表是否都有这个字段

-- right join 右连接查询--
SELECT student.StudentNo,StudentName,SubjectNo,StudentResult
FROM student 
RIGHT JOIN result   -- 这是不用别名,用表名的操作,比较繁琐
ON student.StudentNo = result.StudentNo 

-- left join 左连接查询--
SELECT s.StudentNo,StudentName,SubjectNo,StudentResult
FROM student s
LEFT JOIN result r   -- 别名的 AS 是可以省略的
ON s.StudentNo = r.StudentNo
操作描述
inner join并集 ,只要有一张表有这个数据就可以拿到,就是多表中有一个匹配就行了
left join左连接,会从左表中返回所有的值,即使右表中没有匹配
right join右连接,会从右表中返回所有的值,即使左表中没有匹配
-- (查询了参加考试同学的信息:学号,学生姓名,科目名,分数)  (StudentNo StudentName SubjectName StudentResult)
/* 思路
1.分析需求,分析查询的字段来自哪张表,如果超过一张表就使用(连接查询)  来自( student , result , subject )表
2.确定使用哪种连接查询? 有7种
确定交叉点(这两个表中哪个数据是相同的)如果没有交叉点是查不了东西的
*/
SELECT s.`StudentNo`,`StudentName`,`SubjectName`,`StudentResult` 
FROM `student` AS s
RIGHT JOIN `result` AS r
ON s.`StudentNo` = r.`StudentNo`
INNER JOIN `subject` AS sub
ON r.`SubjectNo` = sub.`SubjectNo`


-- 步骤
/*
我要查询哪些数据 select ....
从哪几个表中查  frominner(right,left) join 连接的表 on 交叉条件 

假设存在多张表查询,先查询两张表然后再慢慢增加

自连接

自己的表和自己的表连接,核心:一张表拆成两张一样的表即可

-- ==========自连接==============

CREATE TABLE categorg (
	categoryid INT(10) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主题id',
	pid INT(10) NOT NULL COMMENT '父id',
	categoryName VARCHAR(50) NOT NULL COMMENT '主题名字',
	PRIMARY KEY (categoryid)
)ENGINE=INNODB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8

INSERT INTO categorg(categoryid,pid,categoryName)
VALUES(2,1,'信息技术'),
(3,1,'软件开发'),
(4,3,'数据库'),
(5,1,'美术设计'),
(6,3,'web开发'),
(7,5,'ps技术'),
(8,2,'办公信息');
-- 查询父子信息
SELECT f.categoryName AS '父栏目',z.categoryName AS '子栏目'
FROM categorg AS f,categorg AS z
WHERE f.categoryid = z.pid  
-- 就相当于 categoryid的值 = pid的值
-- 比如 办公信息的2=信息技术的2 因为1不匹配 所以是单独的

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KHFF2bTB-1597917601699)(C:\Users\黄潮旬\AppData\Roaming\Typora\typora-user-images\image-20200814100757157.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JoTsi5x5-1597917601702)(C:\Users\黄潮旬\AppData\Roaming\Typora\typora-user-images\image-20200814100827000.png)]

分页和排序

排序 语法:ORDER BY 排序的字段 ASC 或 DESC

-- order by排序:升序asc,降序desc
-- 语法 ORDER BY 排序的字段  ASC(DESC)
-- 根据查询的结果进行排序
SELECT s.StudentNo,StudentName,SubjectName,StudentResult
FROM student s
INNER JOIN `result` r
ON s.StudentNo=r.StudentNo
INNER JOIN `subject` sub
ON r.`SubjectNo` = sub.`SubjectNo`
ORDER BY StudentResult ASC

分页 语法: limit 起始下标值,页面有多少条记录

-- 为什么要分页?
-- 为了缓解数据库压力,给人的体验更好,  还有一种不分页的叫做瀑布流

SELECT s.StudentNo,StudentName,SubjectName,StudentResult
FROM student s
INNER JOIN `result` r
ON s.StudentNo=r.StudentNo
INNER JOIN `subject` sub
ON r.`SubjectNo` = sub.`SubjectNo`
ORDER BY StudentResult ASC
LIMIT 0,5  -- 语法  limit  起始下标值,页面有多少条记录

子查询

where (这个值是计算出来的)

使用子查询 (由里及外) 适用于,后面还加条件的

本质:在 where 语句中嵌套一个子查询语句

-- 查询高等数学的所有考试结果(学号 科目名称 成绩),降序查询
 -- 方式一: 使用连接查询
 SELECT `StudentNo`,SubjectName,StudentResult
 FROM `result` AS r
 INNER JOIN `subject` sub
 ON r.`SubjectNo` = sub.`SubjectNo`
 WHERE  SubjectName LIKE '高等数学%'
 ORDER BY StudentResult DESC
 
 
 -- 方式二:使用子查询 (由里及外)  适用于,后面还加条件的
 -- 查询所有考试结果(学号 科目名称 成绩)>70分的,
 SELECT `StudentNo`,SubjectName,StudentResult
 FROM `result` AS r
 INNER JOIN `subject` sub
 ON r.`SubjectNo` = sub.`SubjectNo`
 WHERE StudentResult>=70 
 -- 在基础上增加一个 且是高等数学的
 AND SubjectName = (
	SELECT SubjectName FROM `subject`
	WHERE SubjectName = '高等数学-1'
 )
  
 -- 改造方式二:查询学生学号,学生姓名  成绩大于70分的
 SELECT StudentNo,StudentName FROM student WHERE StudentNo IN (
	SELECT StudentNo FROM result WHERE StudentResult>70 AND SubjectNo =(
		SELECT SubjectNo FROM `subject` WHERE SubjectName = '高等数学-1'
	)
 )
 

分组和过滤

-- 查询不同课程的平均分,最高分,最低分,平均分大于90
-- 核心:根据不同的课程分组    不分组的话只有一个,按照最高的 最低的来排   因为每个课程都有最高分最低分
SELECT SubjectName,AVG(StudentResult)AS 平均分,MAX(StudentResult)AS 最大值,MIN(StudentResult)AS 最小值
FROM `result` AS r 
INNER JOIN `subject` AS sub
ON r.`SubjectNo` = sub.`SubjectNo`
GROUP BY r.SubjectNo  -- 指定结果按照哪几个字段来分组
HAVING 平均分>=80  -- 过滤分组的记录必须满足的次要条件

MySQL函数

官网:https://dev.mysql.com/doc/refman/5.7/en/func-op-summary-ref.html

常用函数

-- 数学运算
SELECT ABS(-8)  -- abs 绝对值
SELECT CEILING(9.4)  -- ceiling 向上取整
SELECT FLOOR(9.4)  -- floor 向下取整
SELECT RAND()  -- rand 返回一个0~1之间的随机数
SELECT SIGN(7)  -- sign 判断一个数的符号 负数返回-1 正数返回1


-- 字符串函数
SELECT CHAR_LENGTH('dffhf附件四')  -- char_length 字符串长度
SELECT CONCAT('热','爱','编程')  -- concat 拼接字符串
SELECT INSERT('我热爱编程helloword',2,3,'我超级喜欢')  -- insert 查询 从某个位置开始 替换某个长度
SELECT LOWER('ASDDlele')  -- lower 小写字母
SELECT UPPER('asddLELE')  -- upper 大写字母
SELECT INSTR('abcdeh','h')  -- instr 返回第一次出现的字串的索引
SELECT REPLACE('坚持就一定能成功','坚持','努力')  -- replace 替换出现的指定字符串
SELECT SUBSTR('坚持就一定能成功',4,6)  -- substr 返回指定的子字符串 从哪里开始,到哪里结束
SELECT REVERSE('坚持就一定能成功')  -- reverse 反转

		-- 查询性李的  然后替换名字 利
		SELECT REPLACE(StudentName,'李','利') FROM student
		WHERE StudentName LIKE '李%'
 
 
-- 时间和日期函数(记住)
SELECT CURRENT_DATE()  -- current_date 获取当前日期
SELECT CURDATE()  -- curdate 获取当前日期
SELECT NOW()  -- now 获取当前的时间
SELECT LOCALTIME()  -- localtime 本地时间
SELECT SYSDATE()  -- sysdate 系统时间

SELECT YEAR(NOW())  -- year 年
SELECT MONTH(NOW())  -- month 月
SELECT DAY(NOW())  -- day 日
SELECT HOUR(NOW())  -- hour 时
SELECT MINUTE(NOW()) -- minute 分
SELECT SECOND(NOW())  -- second 秒
 

-- 系统
SELECT SYSTEM_USER() -- system_user 获取当前用户
SELECT USER()	-- user 获取当前用户
SELECT VERSION()  -- version 获取当前版本

聚合函数(常用)

函数名称描述
count()计数
sum()求和
avg()平均值
max()最大值
min()最小值
-- ========聚合函数========
-- 都能够统计表中的数据
SELECT COUNT(`StudentNo`)FROM student;  -- count(字段) 会忽略所有的null值
SELECT COUNT(*) FROM student  -- count(*) 不会忽略null值,本质 计算行数
SELECT COUNT(1) FROM student  -- count(1) 不会忽略所有的null值,本质 计算行数

SELECT SUM(StudentResult)AS 总和 FROM result; -- sum 总和
SELECT AVG(StudentResult)AS 平均分 FROM result; -- avg 平均分 
SELECT MAX(StudentResult)AS 最高分 FROM result; -- max 最大值
SELECT MIN(StudentResult)AS 最低分 FROM result; -- min 最小值

数据库级别的MD5加密(扩展)

什么是MD5?

主要增强算法复杂度和不可逆转。

MD5不可逆,具体的值的md5是一样的

MD5破解网站的原理,背后有一个字典,MD5加密后的值,加密的前值

--  ============测试MD5 加密=========
CREATE TABLE `testmd5`(
   `id` INT(4) NOT NULL,
   `name` VARCHAR(20) NOT NULL,
   `pwd` VARCHAR(50) NOT NULL,
   PRIMARY KEY(`id`)
)

-- 明文密码
INSERT INTO `testmd5` VALUES(1,'zhangsan','123456'),(2,'lishi','123456'),(3,'wangwu','123456');
-- 加密
UPDATE testmd5 SET pwd = MD5(pwd) WHERE id = 1;
-- 加密全部的密码
UPDATE testmd5 SET pwd = MD5(pwd);
-- 插入的时候加密
INSERT INTO testmd5 VALUES(4,'xiaoming',MD5('123456')) ;
-- 如何校验:将用户传递进来的密码,进行md5加密,然后比对加密后的值
SELECT * FROM testmd5 WHERE `name` = 'xiaoming' AND pwd = MD5('123456')

事物

什么是事物

要么都成功,要么都失败

将一组SQL放在一批次中去执行~

事物原则:DCID原则 原子性,一致性,隔离性,持久性 (脏读,幻读…)

原子性(atomicity)

要么都成功,要么都失败

一致性(consistency)

事物前后的数据完整性要保证一致

隔离性(isolation)

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

持久性(durability)

事物一旦提交则不可逆,被持久化到数据库中

隔离所导致的一些问题

脏读

指一个事物读取了另外一个事务未提交的数据。

不可重复读

在一个事务内读取表中的某一行数据,多次读取结果不同。(这个不一定是错误,只是某些场合不对)

虚读(幻读)

是指在一个事务内读取到了别的事物插入的数据,导致前后读取不一致。

-- =============事物
-- mysql 是默认开启事务自动提交的
SET autocommit = 0 -- 关闭
SET autocommit = 1 -- 开启(默认的)

-- 手动处理事物
 SET autocommit = 0 -- 关闭自动提交
 -- 事物开启
 START TRANSACTION  -- 标记一个事务的开始,从这个之后的sql都在同一个事物内
 

  -- 提交:持久化(成功)
  COMMIT
  -- 回滚:回到原来的样子(失败)
  ROLLBACK
  
  -- 事务结束
  SET autocommit = 1 -- 开启自动提交

-- 了解
SAVEPOINT 保存点名 -- 设置一个事务的保存点
ROLLBACK TO SAVEPOINT 保存点名 -- 回滚到保存点
RELEASE SAVEPOINT 保存点名 -- 撤销保存点


模拟场景

-- 转账例子
CREATE DATABASE shop CHARACTER SET utf8 COLLATE utf8_general_ci
USE shop

CREATE TABLE `account`(
  `id` INT(3) NOT NULL AUTO_INCREMENT,
  `name` VARCHAR(30) NOT NULL,
  `money` DECIMAL(9,2) NOT NULL,
  PRIMARY KEY(`id`)
)ENGINE=INNODB DEFAULT CHARSET=utf8

INSERT INTO `account`(`name`,`money`)
VALUES('A',2000.00),('B',10000.00)

-- 模拟转账:事务
SET autocommit = 0; -- 关闭自动提交
START TRANSACTION -- 开启一个事物

 UPDATE `account` SET `money` = `money`-500 WHERE `name` = 'A';  -- A-500
 UPDATE `account` SET `money` = `money`+500 WHERE `name` = 'B';  -- B+500
 
 COMMIT;  -- 提交
 ROLLBACK; -- 回滚
 
 SET autocommit = 1;  -- 恢复默认值,开启自动提交

索引

mysql官方对索引的定义为:**索引(index)是帮助mysql高效获取数据的数据结构。

提取句子主干,就可以得到索引的本质:索引是数据结构

索引的分类

在一个表中,主键索引只能有一个,唯一索引可以有多个

  • 主键索引(primary key)
    • 唯一的标识,主键不可重复,只能有一个列作为主键
  • 唯一索引(unique key)
    • 避免重复的列出现,唯一索引可以重复,多个列都可以标识为唯一索引
  • 常规索引(key)
    • 默认的,index、key关键字来设置
  • 全文索引(FullText)
    • 在特定的数据库引擎下才有,MyISAM
    • 快速定位数据 没加索引就是遍历,加了索引就是定位
-- 索引的使用
 -- 1. 在创建表的时候给字段增加索引
 -- 2. 创建完毕后,增加索引
-- 显示所有的索引信息 SHOW INDEX FROM 表名
SHOW INDEX FROM student
 
-- 增加一个全文索引(索引名) 列名
ALTER TABLE `school`.`student` ADD FULLTEXT INDEX `StudentName`(`StudentName`);

-- explain 分析sql执行的状况
EXPLAIN SELECT * FROM student;  -- 非全文索引

EXPLAIN SELECT * FROM student WHERE MATCH(studentName)AGAINST('李')

测试索引

 
CREATE TABLE `app_user` (
  `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
  `name` VARCHAR(50) DEFAULT '' COMMENT '用户名称',
  `email` VARCHAR(50) NOT NULL COMMENT '用户邮箱',
  `phone` VARCHAR(20) DEFAULT '' COMMENT '手机号',
  `gender` TINYINT(4) UNSIGNED DEFAULT '0' COMMENT '性别(0:男 1:女)',
  `password` VARCHAR(100) NOT NULL COMMENT '密码',
  `age` TINYINT(4) DEFAULT '0' COMMENT '年龄',
  `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP,
  `update_time` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY(`id`)
)ENGINE=INNODB DEFAULT CHARSET=utf8mb4 COMMENT='app用户表'

SHOW CREATE DATABASE `school`
SHOW  TABLES;

-- 插入100万数据
DELIMITER $$ -- 写函数之前必须要写,标志

CREATE FUNCTION mock_data()
RETURNS INT
BEGIN
  DECLARE num INT DEFAULT 1000000;
  DECLARE i INT DEFAULT 0;
  
  WHILE i<num DO
	-- 插入语句
	INSERT INTO app_user(`name`,`email`,`phone`,`gender`,`password`,`age`)
	VALUES(CONCAT('用户',i),'254635478@qq.com',CONCAT('18',FLOOR(RAND()*((999999999-100000000)+100000000))),	
	FLOOR(RAND()*2),UUID(),FLOOR(RAND()*100));
	SET i = i+1;
  END WHILE;
  RETURN i;
END;

SELECT mock_data();

SELECT * FROM app_user WHERE `name` = '用户500000'; -- 没创建索引之前执行6秒

-- id_表名_字段名
-- create index 索引名 no 表(字段)
CREATE INDEX id_app_user_name ON app_user(`name`);
SELECT * FROM app_user WHERE `id` = 500000; -- 创建索引之后执行0.007秒

索引在小数据量的时候,用处不大,但是在大数据的时候,区别十分明显

索引原则

  • 索引不是越多越好
  • 不要对进程变动数据加索引
  • 小数据量的表不需要加索引
  • 索引一般加在常用来查询的字段上

索引的数据结构

Hash类型的索引

Btree:InnoDB的默认数据结构

阅读:http://blog.codinglabs.org/articles/theory-of-mysql-index.html

权限管理和数据库备份

用户管理

SQL命令操作

用户表:mysql.user

本质:读这张表进行增删改查

-- 创建用户 CREATE USER 用户名 IDENTIFIED BY '密码';
CREATE USER xun IDENTIFIED BY '123456';

-- 修改密码 (修改当前用户密码)
SET PASSWORD = PASSWORD('111111');

-- 修改密码(修改指定用户密码)
SET PASSWORD FOR xun = PASSWORD('111111');

-- 重命名 RENAME USER 原来名字 TO 新的名字;
RENAME USER xun TO xun2;

-- 用户授权 ALL PRIVILEGES 全部的权限 ,库.表
-- ALL PRIVILEGES 除了给别人权限,其他的都能干
GRANT ALL PRIVILEGES ON *.* TO xun2

-- 查询权限
SHOW GRANTS FOR xun2;  -- 查看指定用户的权限
SHOW GRANTS FOR root@localhost

-- 撤销权限
REVOKE ALL PRIVILEGES ON *.* FROM xun2

-- 删除用户
DROP USER xun2

Mysql备份

为什么要备份:

  • 保证重要的数据不丢失
  • 数据转移

MySQL数据库备份方式

  • 直接拷贝物理文件 data文件夹
  • 在Sqlyog可视化工具手动导出
    • 在想要导出的表过着库中,右键选择备份 然后选择SQL转储
  • 使用命令行导出 mysqldump 命令行使用
# mysqldump -h主机 -u用户名 -p密码 数据库 表名 > 物理磁盘位置/文件名
mysqldump -hlocalhost -uroot -p123456 shop account >E:/aa.sql

# 导出多个表
# mysqldump -h主机 -u用户名 -p密码 数据库 表1 表2 表3 > 物理磁盘位置/文件名

# 导出数据库
mysqldump -h主机 -u用户名 -p密码 数据库 > 物理磁盘位置/文件名

#导入
#登录的情况下,切换到指定的数据库
# source 备份文件
source e:aa.sql

# 或
mysql -u用户名 -p密码 库名<备份文件

规范数据库设计

为什么需要设计

当数据库比较复杂的时候,我们就需要设计了

糟糕的数据库设计:

  • 数据冗余,浪费空间
  • 数据库插入和删除都会麻烦,异常【屏蔽使用物理外键】
  • 程序的性能差

良好的数据库设计:

  • 节省内存空间
  • 保证数据库的完整性
  • 方便我们开发系统

软件开发中,关于数据库的设计

  • 分析需求:分析业务和需要处理的数据库的需求

  • 概要设计:设计关系图E-R图

设计数据库步骤:(个人博客)

  • 手机信息,分析需求
    • 用户表(用户登录注销,用户的个人信息,写博客,创建分类)
    • 分类表(文章分类,谁创建的)
    • 文章表(文章的信息)
    • 评论表
    • 友链表(友链信息)
    • 自定义表(系统信息,某个关键的字,或者一些主字段) key:value
  • 标识实体(把需求落地到每个字段)
  • 标识实体之间的关系
    • 写博客:user -> blog
    • 创建分类:user->category
    • 关注:user->user
    • 友链:links
    • 评论:user-user-blog

三大规范

为什么需要数据规范化

  • 信息重复
  • 更新异常
  • 插入异常
    • 无法正常显示信息
  • 删除异常
    • 丢失有效的信息

三大范式

第一范式(1NF)

原子性:保证每一列不可再分

第二范式(2NF)

前提:满足第一范式

每张表只描述一件事情

第三范式(3NF)

前提:满足第一范式和第二范式

第三范式需要确保数据表中的每一列数据都和主键直接相关,而不能间接相关

(规范数据库的设计)

规范性 和 性能的问题

关联查询的表不得超过三张表

  • 考虑商业化的的需求和目标,(成本,用户体验!)数据库的性能更加重要
  • 在规范性能的问题的时候,需要适当的考虑一下 规范性!
  • 故意给某些表增加一些冗余的字段。(从多表查询中变为单表查询)
  • 故意增加一些计算列(从大数据量降低为小数据量的查询 :索引)

JDBC(重点)

数据库驱动

驱动:声卡、显卡、数据库 都是需要驱动才能运行的

应用程序 会先连接数据库的驱动 后 再从驱动连接数据库 而不是能直接连接数据库

JDBC

SUN公司为了简化开发人员的(对数据库的统一) 操作,提供了一个(Java操作数据库的)规范,俗称JDBC

这些规范的实现由具体的厂商去做

对于开发人员来说,我们只需要掌握JDBC接口的操作即可!

需要导入 java.sql javax.sql 还需要导入一个数据库驱动包 mysql-connector-java-5.1.47.jar

第一个JDBC程序

先创建测试数据库

CREATE DATABASE jdbcStudy CHARACTER SET utf8 COLLATE utf8_general_ci;

USE jdbcStudy;

CREATE TABLE users(
  `id` INT PRIMARY KEY,
  `name` VARCHAR(40),
  `password` VARCHAR(60),
  `email` VARCHAR(60),
  `birthday` DATE
  );
  
 INSERT INTO users(id,`name`,`password`,email,birthday) 
 VALUES(1,'zhangsan','123456','hwaw@qq.com','1980-12-04'),
(2,'lishi','123456','awdf@qq.com','1985-12-04'),
(3,'wangwu','123456','gtre@qq.com','1981-12-04');
  1. 创建一个项目

  2. 导入数据库驱动,自己下载jar包导入也行 用maven导入也行(推荐) mysql-connector-java-5.1.47.jar

  3. 编写测试代码

    package com.kuang.jdbc01;
    
    import java.sql.*;
    
    // 第一个JDBC程序
    public class test01 {
        public static void main(String[] args) throws ClassNotFoundException, SQLException {
            //1.加载驱动
            Class.forName("com.mysql.jdbc.Driver");//固定写法,加载驱动
    
            //2.用户信息和url
            //"jdbc:mysql://localhost:3306/数据库名?支持中文编码&设置中文字符集=utf8&use使用安全的一个连接";
            String url = "jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=true";
            //用户名
            String username = "root";
            //密码
            String password = "123456";
    
            //3.连接成功  后,会返回 数据库对象
            //驱动管理DriverManager  获得连接getConnection   代表数据库connection
            Connection connection = DriverManager.getConnection(url, username, password);
    
            //4.执行SQL的对象     用statement执行sql对象
            Statement statement = connection.createStatement();
    
            //5.执行SQL对象 去 执行SQL, 可能存在结果,查看返回结果
            String sql = "SELECT * FROM users";
            // 查询 sql
            ResultSet resultSet = statement.executeQuery(sql); // 返回的结果集,结果集中封装了我们全部的查询出来的结果
            while(resultSet.next()){
                System.out.println("id"+ resultSet.getObject("id"));
                System.out.println("name"+ resultSet.getObject("name"));
                System.out.println("password"+ resultSet.getObject("password"));
                System.out.println("email"+ resultSet.getObject("email"));
                System.out.println("birthday"+ resultSet.getObject("birthday"));
                System.out.println("============================");
            }
            //6.释放连接
            resultSet.close();
            statement.close();
            connection.close();
        }
    }
    

    步骤总结: 只有查询才有结果集

    1. 加载驱动 Class.forName()
    2. 连接数据库 DriverManager
    3. 获得执行sql的对象 Statement
    4. 获得返回的结果集
    5. 释放连接

    DriverManager

     // DriverManager.registerDriver(new com.mysql.jdbc.Driver());
     Class.forName("com.mysql.jdbc.Driver");//固定写法,加载驱动
    
     // connection 代表数据库
    //数据库设计自动提交
    //事务提交
    //事物回滚
            connection.rollback();
            connection.commit();
            connection.setAutoCommit();
    

    URL

    String url = "jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=true";
    //mysql  -- 3306
    // sTRING URL = 协议://主机地址+端口号/数据库名?参数1&参数2&参数3
    
    // oralce --1521
    // jdbc:oracle:thin:@localhost:1521:sid
    

    Statement执行SQL的对象

    String sql = "SELECT * FROM users"; //编写SQL
    
    statement.executeQuery();  //查询操作 返回ResultSet(结果集)
    statement.execute();        //执行任何sql
    statement.executeUpdate();  //更新、插入、删除、都使用这个,返回一个受影响的行数
    

    ResultSet 查询的结果集:封装了所有的查询结果

    获得指定的数据类型

            resultSet.getObject();// 在不知道列类型的情况下使用
            //如果知道列的类型就使用指定的类型
            resultSet.getString();
            resultSet.getInt();
            resultSet.getFloat();
            resultSet.getTime();
    		...
    

    遍历,指针

            resultSet.beforeFirst();//移动到最前面
            resultSet.afterLast();//移动到最后面
            resultSet.next();//移动到下一个数据
            resultSet.previous();//移动到下一行
            resultSet.absolute(row);//指定到移动行
    

    释放资源

    resultSet.close();
    statement.close();
    connection.close();
    //耗资源 ,用完关掉
    

statement对象

jdbc中的 statement 对象用于向数据库发送SQL语句,想完成对数据库的增删改查,只需要通过这个对象向数据库发送增删改查语句即可

Statement 对象的 executeUpdate 方法,用于向数据库发送增、删、改的sql语句,executeUpdate 执行完后,将会返回一个整数(即增删改语句导致了数据库几行数据发生了变化)。

Statement.executeQuery 方法用于向数据库发送查询语句,executeQuery 方法返回代表查询结果的ResultSet对象

CRUD操作-create

使用 executeUpdate (String sql)方法完成数据添加操作,示例操作:

Statement statement = connection.createStatement();
String sql = "insert into users(...)values(...)";
int num = statement.executeUpdate(sql);
if(num>0){
    System.out.println("插入成功!!!");
}

CRUD操作-delete

使用 executeUpdate(String sql)方法完成数据删除操作,示例操作:

Statement statement = connection.createStatement();
String sql = "delete from users where id=1";
int sum = statement.executeUpdate(sql);
if(sum>0){
    System.out.println("删除成功!!!");
}

CRUD操作-update

使用executeUpdate(String sql)方法完成数据修改操作,示例操作:

Statement statement = connection.createStatement();
String sql = "update users set name = '' where name = ''";
int sum = statement.executeUpdate(sql);
if(sum>0){
    System.out.println("修改成功");
}

CRUD操作-read

使用 executeQuery(String sql)方法完成数据查询操作,实例操作:

Statement statement = connection.createStatement();
String sql = "select * from users where id=1";
ResultSet rs = statement.executeQuery(sql);
while(rs.next()){
    //根据获取列的数据类型,分别调用rs的相应方法映射到java对象中
}
代码实现

1、创建配置文件

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=true
username=root
password=123456

2、提取工具类

package com.kuang.jdbc02.utils;

import java.io.InputStream;
import java.sql.*;
import java.util.Properties;

public class JdbcUtils {
    private static String driver=null;
    private static String url=null;
    private static String username=null;
    private static String password=null;
    static{
        try{     //反射获取类的装载器,然后在获取资源(db.properties)
            InputStream in = JdbcUtils.class.getClassLoader().getResourceAsStream("db.properties");
            //属性
            Properties properties = new Properties();
            //加载获取到的属性
            properties.load(in);
//            从文件把这4个信息读取出来
            driver = properties.getProperty("driver");
            url = properties.getProperty("url");
            username = properties.getProperty("username");
            password = properties.getProperty("password");

            //1.驱动只用加载一次
            Class.forName(driver);
        }catch (Exception e) {
            e.printStackTrace();
        }
    }
    //获取连接
    public static Connection getConnection() throws SQLException {
        return  DriverManager.getConnection(url, username, password);
    }
    //释放资源
    public static void release(Connection conn, Statement sta, ResultSet res) throws SQLException {
        if (res!=null){
            res.close();
        }
        if (sta!=null){
            sta.close();
        }
        if (conn!=null){
            conn.close();
        }
    }
}

3、编写增删改

增 insert

package com.kuang.jdbc02;

import com.kuang.jdbc02.utils.JdbcUtils;

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;

public class TestInsert {

    public static void main(String[] args) throws SQLException {

            Connection connection = JdbcUtils.getConnection(); //通过工具类获取链接

            Statement statement = connection.createStatement();//获得SQL的执行对象
            String sql = "INSERT INTO users(id,`name`,`password`,`email`,`birthday`) VALUES(5,'fenqi','123456','hfghs@qq.com','1985-12-04')";
            int i = statement.executeUpdate(sql);
            if (i>0){
                System.out.println("插入成功");
            }
            JdbcUtils.release(null,null,null); //释放资源
    }
}


删 Delete

package com.kuang.jdbc02;

import com.kuang.jdbc02.utils.JdbcUtils;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class TestSelete {
    public static void main(String[] args) throws SQLException {
        //通过工具类获取数据库连接
        Connection connection = JdbcUtils.getConnection();
        //获得SQL的执行对象
        Statement statement = connection.createStatement();
        //编写SQL语句
        String sql = "select  * from users";
        //执行
        ResultSet resultSet = statement.executeQuery(sql);

        while(resultSet.next()){
            System.out.println("id"+resultSet.getInt("id"));
            System.out.println("name"+resultSet.getString("name"));
            System.out.println("password"+resultSet.getString("password"));
            System.out.println("email"+resultSet.getString("email"));
            System.out.println("birthday"+resultSet.getDate("birthday"));
            System.out.println("=================");
        }
        JdbcUtils.release(null,null,null);
    }
}

改 Update

package com.kuang.jdbc02;

import com.kuang.jdbc02.utils.JdbcUtils;

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;

public class TestUpdate {
    public static void main(String[] args) throws SQLException {
        //通过工具类获取连接
        Connection connection = JdbcUtils.getConnection();
        //获得SQL的执行对象
        Statement statement = connection.createStatement();
        //编写SQL语句
        String sql = "update users set name='xiugai' where id = 1";
        //执行
        int i = statement.executeUpdate(sql);

        if (i>0){
            System.out.println("更新成功");
        }
        JdbcUtils.release(null,null,null);
    }
}

查 Select

package com.kuang.jdbc02;

import com.kuang.jdbc02.utils.JdbcUtils;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class TestSelete {
    public static void main(String[] args) throws SQLException {
        //通过工具类获取数据库连接
        Connection connection = JdbcUtils.getConnection();
        //获得SQL的执行对象
        Statement statement = connection.createStatement();
        //编写SQL语句
        String sql = "select  * from users";
        //执行
        ResultSet resultSet = statement.executeQuery(sql);

        while(resultSet.next()){
            System.out.println("id"+resultSet.getInt("id"));
            System.out.println("name"+resultSet.getString("name"));
            System.out.println("password"+resultSet.getString("password"));
            System.out.println("email"+resultSet.getString("email"));
            System.out.println("birthday"+resultSet.getDate("birthday"));
            System.out.println("=================");
        }
        JdbcUtils.release(null,null,null);
    }
}

sql注入问题

sql存在漏洞,会被攻击导致数据泄露,SQL会被拼接or

package com.kuang.jdbc02;

import com.kuang.jdbc02.utils.JdbcUtils;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class SQL注入 {
    public static void main(String[] args) throws SQLException {
        //正常登录
        // login("lishi","123456");
        login(" 'or' 1=1"," 'or' 1=1");

    }
    public static void login(String username,String password) throws SQLException {
        //通过工具类获取数据库连接
        Connection connection = JdbcUtils.getConnection();
        //获得SQL的执行对象
        Statement statement = connection.createStatement();
        //编写SQL语句
        String sql = "select * from users where `name` = '"+username+"' and password='"+password+"'";
        //执行
        ResultSet resultSet = statement.executeQuery(sql);
        while(resultSet.next()){
            System.out.println(resultSet.getString("name"));
            System.out.println(resultSet.getString("password"));
            System.out.println("======================");
        }
    }
}

PreparedStatement对象

PreparedStatement 可以防止SQL注入。效果更好

1、新增

package com.kuang.jdbc03;

import com.kuang.jdbc02.utils.JdbcUtils;

import java.sql.Connection;
import java.util.Date;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class TestInsert {
    public static void main(String[] args) throws SQLException {
        //调用工具类获取数据库连接
        Connection connection = JdbcUtils.getConnection();
        //使用? 占位符代表参数
        String sql = "insert into users(id,name,password,email,birthday)values(?,?,?,?,?)";
        //预编译SQL,先写sql,然后不执行
        PreparedStatement pr = connection.prepareStatement(sql);
        //手动给参数赋值
        pr.setInt(1,5); //第一个值为参数下标,第二个为参数 id
        pr.setString(2,"王胜");
        pr.setString(3,"123456");
        pr.setString(4,"jukk@qq.com");
        // 注意点:sql.Date     数据库 java.sql.Date()
        //       util.Date     Java   new Date().getTime() 获得时间戳
        pr.setDate(5,new java.sql.Date(new Date().getTime()));

        //执行
        int i = pr.executeUpdate();

        if (i>0){
            System.out.println("插入成功");
        }
        JdbcUtils.release(null,null,null);
    }
}

2、删除

package com.kuang.jdbc03;

import com.kuang.jdbc02.utils.JdbcUtils;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class TestDelete {
    public static void main(String[] args) throws SQLException {
        //通过工具类获取数据库的连接
        Connection connection = JdbcUtils.getConnection();
        //使用占位符?   代表参数
        String sql = "delete from users where id = ?";
        //预编译SQL  先写sql 然后不执行
        PreparedStatement pre = connection.prepareStatement(sql);
        //手动给参数赋值
        pre.setInt(1,5);
        //执行
        int i = pre.executeUpdate();

        if (i>0){
            System.out.println("删除成功");
        }
        //释放资源
        JdbcUtils.release(null,null,null);
    }
}

3、更新

package com.kuang.jdbc03;

import com.kuang.jdbc02.utils.JdbcUtils;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class TestUpdate {
    public static void main(String[] args) throws SQLException {
        //通过工具类获取数据库的连接
        Connection connection = JdbcUtils.getConnection();
        //使用占位符?  代表参数
        String sql = "update users set name = ? where  name = ?";
        //预编译SQL  先写SQL 然后不执行
        PreparedStatement pre = connection.prepareStatement(sql);
        //手动给参数赋值
        pre.setString(1,"zhangsan");
        pre.setString(2,"xiugai");
        //执行
        int i = pre.executeUpdate();

        if (i>0){
            System.out.println("修改成功");
        }
        //关闭资源
        JdbcUtils.release(null,null,null);
    }
}

4、查询

package com.kuang.jdbc03;

import com.kuang.jdbc02.utils.JdbcUtils;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class TestSelete {
    public static void main(String[] args) throws SQLException {
        //通过工具类获取数据库的连接
        Connection connection = JdbcUtils.getConnection();
        //使用占位符?  代表参数
        String sql = "select * from users where id=?";
        //预编译SQL  先写sql  然后不执行
        PreparedStatement pre = connection.prepareStatement(sql);
        //手动传递参数
        pre.setObject(1,2);
        //执行
        ResultSet resultSet = pre.executeQuery();

        while (resultSet.next()){
            System.out.println(resultSet.getInt("id"));
            System.out.println(resultSet.getString("name"));
            System.out.println(resultSet.getString("password"));
        }
        //释放资源
        JdbcUtils.release(null,null,null);
    }
}

5、防止Sql注入

package com.kuang.jdbc03;

import com.kuang.jdbc02.utils.JdbcUtils;

import java.sql.*;

public class SQL注入 {
    public static void main(String[] args) throws SQLException {
        //正常登录
        // login("lishi","123456");
        login("'' or 1=1","123456");

    }
    public static void login(String username,String password) throws SQLException {
        //通过工具类获取数据库连接
        Connection connection = JdbcUtils.getConnection();
        //使用占位符? 传递参数
        //PreparedStatement放置SQL注入的本质,把值传递进来的参数当作字符
        //假设其中存在转义字符,比如说 ‘ 会被直接转义
        String sql = "select * from users where name = ? and password = ?";
        //预编译Sql  先写sql 然后不执行
        PreparedStatement pre = connection.prepareStatement(sql);
        //手动传递参数
        pre.setString(1,username);
        pre.setString(2,password);
        //执行
        ResultSet resultSet = pre.executeQuery();

        while(resultSet.next()){
            System.out.println(resultSet.getString("name"));
            System.out.println(resultSet.getString("password"));
            System.out.println("======================");
        }
        //释放资源
        JdbcUtils.release(null,null,null);
    }
}

idea连接数据库

  1. 找到右侧的Database 点击进去

  2. 点击 + 号 → 选择Data Source →选择MySql

  3. 然后输入用户名 和 密码 → 点击 Test Connection

    • 成功的话会显示✔

      • 然后 点击Apply →点击上面一点的Schemas → 然后选择要添加的数据库就可以了
    • 不成功的话,要么是时区异常要么就是版本没选好

      • 左侧NySQL点击进去

      • Class选择 com.musql.jdbc.Driver

      • Driver files 选择你对应的SQL版本 → 然后就可以点击Apply → 然后就去重新输入账号密码连接

      • 时区的话就需要打开命令行的sql然后设计时区了

        • -- 查看时区,显示 SYSTEM 就是没有设置时区啦。
          show variables like'%time_zone';
          
        • --设置时区 设置完需要重新连接sql重新查看
          set global time_zone = '+8:00';
          

事物

要么都成功,要么都失败

ACID原则

原子性:

​ 要么全部完成,要么都不完成

一致性:

​ 总数不变

隔离性:

​ 多个进程互不干扰

持久性:

​ 一旦提交不可逆,持久化到数据库

隔离性问题:

脏读:一个事务读取了另一个没有提交的事物

不可重复读:在同一个事物内,重复读取表中的数据,表数据发生了改变

虚读(幻读):在一个事务内,读取到了别人插入的数据,导致前后读出来结果不一致

代码实现

​ 1、开启事务 conn.setAutoCommit(false);

​ 2、一组业务执行完毕,提交事务

​ 3、可以在catch语句中显示的定义 回滚语句,但默认失败就会回滚

package com.kuang.jdbc04;

import com.kuang.jdbc02.utils.JdbcUtils;
import com.sun.org.apache.xpath.internal.objects.XNull;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class TestTransation1 {
    public static void main(String[] args) throws SQLException {
         Connection conn = null;
        PreparedStatement st = null;
        try {
            //通过工具类连接数据库
             conn = JdbcUtils.getConnection();
            //关闭数据库的自动提交,会自动开启事物
            conn.setAutoCommit(false);

            //编写SQL语句
            String sql1 = "update account set miney = miney-100 where name = 'A'";
            //预编译SQL
            st = conn.prepareStatement(sql1);
            st.executeUpdate();

            //编写SQL语句
            String sql2 = "update account set miney = miney+100 where name = 'B'";
            //预编译SQL
            st= conn.prepareStatement(sql2);
            st.executeUpdate();

            //业务完毕,提交事务
            conn.commit();
            System.out.println("成功");
        }catch (Exception e){
            try{
                conn.rollback(); //如果失败则回滚事物
            } catch (SQLException throwables)
            {
                throwables.printStackTrace();
            }
            e.printStackTrace();
        }finally {
            //释放资源
            JdbcUtils.release(conn,st,null);
        }

    }
}

数据库连接池

数据库连接 - 执行完毕 - 释放

连接-释放 十分浪费系统资源

池化技术:准备一些预先的资源,过来就连接预先准备好的

例子(银行)

比如去银行办理业务,开门-办理-关门 下一个 开门-办理-关门, 这是平常数据库的操作,很繁琐麻烦

使用连接池 开门-安排业务员-等待-办理-不使用了就彻底关门

常用连接数10个

最小连接数:10(按照常用连接数设计最小连接数)

最大连接数:(业务最高承载上线,超过了就需要等待排队)

等待超时:(超过等待的时间就不等了)

编写连接池,只需要实现一个接口 DataSource

开源数据源实现(拿来即用)

​ DBCP

​ C3P0

​ Druid:阿里巴巴

使用了这些数据库连接池之后我们在项目开发中就不需要编写连接数据库的代码了!

DBCP

​ 需要用到的jar包

​ commons-dbcp-1.4、commons-pool-1.6

C3P0

需要用到的jar包

c3p0-0.9.5.5、mchange-commons-java-0.2.19

结论

无论使用什么数据源,本质还是一样的 ,DataSource接口不会变,方法就不会变

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值