1、初识MySQL
1.1、什么是数据库
数据库 ( DataBase , 简称DB )
- 概念 : 长期存放在计算机内,有组织,可共享的大量数据的集合,是一个数据 “仓库”
- 作用 : 保存,并能安全管理数据(如:增删改查等),减少冗余…
数据库总览 :
- 关系型数据库 ( SQL )
- MySQL , Oracle , SQL Server , SQLite , DB2 , …
- 关系型数据库通过外键关联来建立表与表之间的关系
- 非关系型数据库 ( NOSQL )
- Redis , MongoDB , …
- 非关系型数据库通常指数据以对象的形式存储在数据库中,而对象之间的关系通过每个对象自身的属性来决定
1.2、什么是DBMS
数据库管理系统 ( DataBase Management System ) 数据库管理软件 , 科学组织和存储数据 , 高效地获取和维护数据 为什么要说这个呢? 因为我们要学习的MySQL应该算是一个数据库管理系统.
1.3、MySQL简介
概念 : 是现在流行的开源的,免费的 关系型数据库 历史 : 由瑞典MySQL AB 公司开发,目前属于 Oracle 旗下产品。
特点 :
- 免费 , 开源数据库
- 小巧 , 功能齐全
- 使用便捷
- 可运行于Windows或Linux操作系统
- 可适用于中小型甚至大型网站应用
官网 :
几个基本的数据库操作命令 :
update user set password=password('123456')where user='root'; 修改密码
flush privileges; 刷新数据库
show databases; 显示所有数据库
use dbname; 打开某个数据库
show tables; 显示数据库mysql中所有的表
describe user; 显示表mysql数据库中user表的列信息
create database name; 创建数据库
use databasename; 选择数据库
exit; 退出Mysql
? 命令关键词 : 寻求帮助
2、操作数据库
2.1、结构化查询语句分类
名称 | 解释 | 命令 |
---|
DDL (数据定义语言) | 定义和管理数据对象,如数据库,数据表等 | CREATE、DROP、ALTER | DML (数据操作语言) | 用于操作数据库对象中所包含的数据 | INSERT、UPDATE、DELETE | DQL (数据查询语言) | 用于查询数据库数据 | SELECT | DCL (数据控制语言) | 用于管理数据库的语言,包括管理权限及数据更改 | GRANT、commit、 rollback |
2.2、数据库操作
命令行操作数据库
创建数据库 : create database [if not exists] 数据库名; 删除数据库 :drop database [if exists] 数据库名; 查看数据库 : show databases; 使用数据库 : use 数据库名;
对比工具操作数据库
学习方法:
- 对照SQLyog工具自动生成的语句学习
- 固定语法中的单词需要记忆
2.3、创建数据表
属于DDL的一种,语法 :
create table [if not exists] `表名`(
'字段名1' 列类型 [属性][索引][注释],
'字段名2' 列类型 [属性][索引][注释],
'字段名n' 列类型 [属性][索引][注释]
)[表类型][表字符集][注释];
说明 : 反引号用于区别MySQL保留字与普通字符而引入的 (键盘esc下面的键).
2.4、数据值和列类型
列类型 : 规定数据库中该列存放的数据类型
数值类型
字符串类型 日期和时间型数值类型
NULL值
- 理解为 “没有值” 或 “未知值”
- 不要用NULL进行算术运算 , 结果仍为NULL
2.5、数据字段属性
UnSigned
ZEROFILL
- 0填充的
- 不足位数的用0来填充 , 如int(3),5则为005
Auto_InCrement
- 自动增长的 , 每添加一条数据 , 自动在上一个记录数上加 1(默认)
- 通常用于设置主键 , 且为整数类型
- 可定义起始值和步长
- 当前表设置步长(AUTO_INCREMENT=100) : 只影响当前表
- SET @@auto_increment_increment=5 ; 影响所有使用自增的表(全局)
NULL 和 NOT NULL
- 默认为NULL , 即没有插入该列的数值
- 如果设置为NOT NULL , 则该列必须有值
DEFAULT
- 默认的
- 用于设置默认值
- 例如,性别字段,默认为"男" , 否则为 “女” ; 若无指定该列的值 , 则默认值为"男"的值
址,email
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 '密码',
`sex` 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
SHOW CREATE DATABASE school;
SHOW CREATE TABLE student;
DESC student;
sql_mode='STRICT_TRANS_TABLES';
2.6、数据表的类型
设置数据表的类型
CREATE TABLE 表名(
)ENGINE = MyISAM (or InnoDB)
SHOW ENGINES;
MySQL的数据表的类型 : MyISAM , InnoDB , HEAP , BOB , CSV等…
常见的 MyISAM 与 InnoDB 类型:
名称 | MyISAM | InnoDB |
---|
事务处理 | 不支持 | 支持 | 数据行锁定 | 不支持 | 支持 | 外键约束 | 不支持 | 支持 | 全文索引 | 支持 | 不支持 | 表空间大小 | 教小 | 较大,约 2 倍! |
经验 ( 适用场合 ) :
- 适用 MyISAM : 节约空间及相应速度
- 适用 InnoDB : 安全性 , 事务处理及多用户操作数据表
数据表的存储位置
-
MySQL数据表以文件方式存放在磁盘中
- 包括表文件 , 数据文件 , 以及数据库的选项文件
- 位置 : Mysql安装目录\data\下存放数据表 . 目录名对应数据库名 , 该目录下文件名对应数据表
-
注意 :
- InnoDB类型数据表只有一个 *.frm文件 , 以及上一级目录的ibdata1文件
-
MyISAM类型数据表对应三个文件 :
- *. frm – 表结构定义文件
- *. MYD – 数据文件 ( data )
- *. MYI – 索引文件 ( index )
设置数据表字符集
我们可为数据库,数据表,数据列设定不同的字符集,设定方法 :
- 创建时通过命令来设置 , 如 : CREATE TABLE 表名()CHARSET = utf8;
- 如无设定 , 则根据MySQL数据库配置文件 my.ini中的参数设定
2.7、修改数据库
修改表 ( ALTER TABLE )
修改表名 : ALTER TABLE 旧表名 RENAME AS 新表名
添加字段 : ALTER TABLE 表名 ADD字段名 列属性[属性]
修改字段 :
- ALTER TABLE 表名 MODIFY 字段名 列类型[属性]
- ALTER TABLE 表名 CHANGE 旧字段名 新字段名 列属性[属性]
删除字段 : ALTER TABLE 表名 DROP 字段名
删除数据表
语法: DROP TABLE [IF EXISTS] 表名
- IF EXISTS 为可选 , 判断是否存在该数据表
- 如删除不存在的数据表会抛出错误
其他
1. 可用反引号(`)为标识符(库名、表名、字段名、索引、别名)包裹,以避免与关键字重名!中文也可以作为标识符!
2. 每个库目录存在一个保存当前数据库的选项文件db.opt。
3. 注释:
单行注释
多行注释
单行注释
4. 模式通配符:
_ 任意单个字符
% 任意多个字符,甚至包括零字符
单引号需要进行转义 \'
5. CMD命令行内的语句结束符可以为 ";", "\G", "\g",仅影响显示结果。其他地方还是用分号结 束。delimiter 可修改当前对话的语句结束符。
6. SQL对大小写不敏感 (关键字)
7. 清除已有语句:\c
3、MySQL数据管理
3.1、外键
外键概念
如果公共关键字在一个关系中是主关键字,那么这个公共关键字被称为另一个关系的外键。由此可见,外键表示了两个关系之间的相关联系。以另一个关系的外键作主关键字的表被称为主表,具有此外键的表被称为主表的从表。
在实际操作中,将一个表的值放入第二个表来表示关联,所使用的值是第一个表的主键值(在必要时可包括复合主键值)。此时,第二个表中保存这些值的属性称为外键**(foreign key)**。
外键作用
保持数据一致性,完整性,主要目的是控制存储在外键表中的数据,约束。 使两张表形成关联,外键只能引用外表中的列的值或使用空值。
创建外键
建表时指定外键约束
CREATE TABLE `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 `student` (
`studentno` INT(4) NOT NULL COMMENT '学号',
`studentname` VARCHAR(20) NOT NULL DEFAULT '匿名' COMMENT '姓名',
`sex` TINYINT(1) DEFAULT '1' COMMENT '性别',
`gradeid` INT(10) DEFAULT NULL COMMENT '年级',
`phoneNum` VARCHAR(50) NOT NULL COMMENT '手机',
`address` VARCHAR(255) DEFAULT NULL COMMENT '地址',
`borndate` DATETIME DEFAULT NULL COMMENT '生日',
`email` VARCHAR(50) DEFAULT NULL COMMENT '邮箱',
`idCard` VARCHAR(18) DEFAULT NULL COMMENT '身份证号',
PRIMARY KEY (`studentno`),
KEY `FK_gradeid` (`gradeid`),
CONSTRAINT `FK_gradeid` FOREIGN KEY (`gradeid`) REFERENCES `grade`
(`gradeid`)
) ENGINE=INNODB DEFAULT CHARSET=utf8
建表后修改
ALTER TABLE `student`
ADD CONSTRAINT `FK_gradeid` FOREIGN KEY (`gradeid`) REFERENCES `grade` (`gradeid`);
删除外键
操作:删除 grade 表,发现报错
注意 : 删除具有主外键关系的表时 , 要先删子表 , 后删主表
ALTER TABLE student DROP FOREIGN KEY FK_gradeid;
ALTER TABLE student DROP INDEX FK_gradeid;
3.2、DML语言
数据库意义 : 数据存储、数据管理
管理数据库数据方法:
- 通过SQLyog等管理工具管理数据库数据
- 通过DML语句管理数据库数据
DML语言 : 数据操作语言
- 用于操作数据库对象中所包含的数据
- 包括 :
- INSERT (添加数据语句)
- UPDATE (更新数据语句)
- DELETE (删除数据语句)
3.3、添加数据
INSERT命令
语法:
- 字段或值之间用英文逗号隔开 .
- ’ 字段1,字段2…’ 该部分可省略 , 但添加的值务必与表结构,数据列,顺序相对应,且数量一致 .
- 可同时插入多条数据 , values 后用英文逗号隔开 .
INSERT INTO grade(gradename) VALUES ('大一');
INSERT INTO grade VALUES ('大二');
Column count doesn`t match value count at row 1
INSERT INTO grade(gradename) VALUES ('大三'),('大四');
练习题目 自己使用INSERT语句为课程表subject添加数据 . 使用到外键.
3.4、修改数据
update命令
语法:
UPDATE 表名 SET column_name=value [column_name2=value2,...] [WHERE condition];
注意 :
- column_name 为要更改的数据列
- value 为修改后的数据 , 可以为变量 , 具体指 , 表达式或者嵌套的SELECT结果
- condition 为筛选条件 , 如不指定则修改该表的所有列数据
where条件子句
可以简单的理解为 : 有条件地从表中筛选数据
运算符 | 含义 | 范围 | 结果 |
---|
= | 等于 | 5=6 | false | <> 或 != | 不等于 | 5!=6 | true | > | 大于 | 5>6 | false | < | 小于 | 5<6 | true | >= | 大于等于 | 5>=6 | false | <= | 小于等于 | 5<=6 | true | BETWEEN | 在某个范围之间 | BETWEEN 5 AND 10 | | AND | 并且 | 5 > 1 AND 1 > 2 | false | OR | 或 | 5 > 1 OR 1 > 2 | true |
测试:
UPDATE grade SET gradename = '高中' WHERE gradeid = 1;
3.5、删除数据
DELETE命令
语法:
DELETE FROM 表名 [WHERE condition];
注意:condition为筛选条件 , 如不指定则删除该表的所有列数据
DELETE FROM grade WHERE gradeid = 5
TRUNCATE命令
作用:用于完全清空表数据 , 但表结构 , 索引 , 约束等不变 ; 语法:
TRUNCATE [TABLE] table_name;
TRUNCATE grade
注意:区别于DELETE命令
- 相同 : 都能删除数据 , 不删除表结构 , 但TRUNCATE速度更快
- 不同 :
- 使用TRUNCATE TABLE 重新设置AUTO_INCREMENT计数器
- 使用TRUNCATE TABLE不会对事务有影响 (事务后面会说)
测试:
CREATE TABLE `test` (
`id` INT(4) NOT NULL AUTO_INCREMENT,
`coll` VARCHAR(20) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8
INSERT INTO test(coll) VALUES('row1'),('row2'),('row3');
DELETE FROM test;
TRUNCATE TABLE test;
4、使用DQL查询数据
4.1、DQL语言
DQL( Data Query Language 数据查询语言 )
- 查询数据库数据 , 如SELECT语句
- 简单的单表查询或多表的复杂查询和嵌套查询
- 是数据库语言中最核心,最重要的语句
- 使用频率最高的语句
SELECT语法
SELECT [ALL | DISTINCT]
{* | table.* | [table.field1[as alias1][,table.field2[as alias2]][,...]]}
FROM table_name [as table_alias]
[left | right | inner join table_name2]
[WHERE ...]
[GROUP BY ...]
[HAVING]
[ORDER BY ...]
[LIMIT {[offset,]row_count | row_countOFFSET offset}];
注意 : [ ] 括号代表可选的 , { }括号代表必选得 导入素材提供的SQL
4.2、指定查询字段
SELECT * FROM student;
SELECT studentno,studentname FROM student;
AS 子句作为别名
作用:
- 可给数据列取一个新别名
- 可给表取一个新别名
- 可把经计算或总结的结果用另一个新名称来代替
SELECT studentno AS 学号,studentname AS 姓名 FROM student;
SELECT studentno AS 学号,studentname AS 姓名 FROM student AS s;
SELECT CONCAT('姓名:',studentname) AS 新姓名 FROM student;
DISTINCT关键字的使用
作用 : 去掉SELECT查询返回的记录结果中重复的记录 ( 返回所有列的值都相同 ) , 只返回一条
SELECT * FROM result;
SELECT studentno FROM result;
SELECT DISTINCT studentno FROM result;
使用表达式的列
数据库中的表达式 : 一般由文本值 , 列值 , NULL , 函数和操作符等组成
应用场景 :
- SELECT语句返回结果列中使用
- SELECT语句中的ORDER BY , HAVING等子句中使用
- DML语句中的 where 条件语句中使用表达式
SELECT @@auto_increment_increment;
SELECT VERSION();
SELECT 100*3-1 AS 计算结果;
SELECT studentno,StudentResult+1 AS '提分后' FROM result;
- 避免SQL返回结果中包含 ’ . ’ , ’ * ’ 和括号等干扰开发语言程序.
4.3、where条件语句
作用:用于检索数据表中 符合条件 的记录 搜索条件可由一个或多个逻辑表达式组成 , 结果一般为真或假.
逻辑操作符
操作符名称 | 语法 | 描述 |
---|
AND 或 && | a AND b 或 a && b | 逻辑与,同时为真结果才为真 | OR 或|| | a OR b 或 a||b | 逻辑或,只要一个为真,则结果为真 | NOT 或 ! | NOT a 或 !a | 逻辑非,若操作数为假,则结果为真! |
测试
SELECT Studentno,StudentResult FROM result;
SELECT Studentno,StudentResult
FROM result
WHERE StudentResult>=95 AND StudentResult<=100;
SELECT Studentno,StudentResult
FROM result
WHERE StudentResult>=95 && StudentResult<=100;
SELECT Studentno,StudentResult
FROM result
WHERE StudentResult BETWEEN 95 AND 100;
SELECT studentno,studentresult
FROM result
WHERE studentno!=1000;
SELECT studentno,studentresult
FROM result
WHERE NOT studentno=1000;
模糊查询 : 比较操作符
操作符名称 | 语法 | 描述 |
---|
IS NULL | a IS NULL | 若操作符为NULL,则结果为真 | IS NOT NULL | a IS NOT NULL | 若操作符不为NULL,则结果为真 | BETWEEN | a BETWEEN b AND c | 若 a 范围在 b 与 c 之间,则结果为真 | LIKE | a LIKE b | SQL 模式匹配,若a匹配b,则结果为真 | IN | a IN (a1,a2,a3,…) | 若 a 等于 a1,a2… 中的某一个,则结果为真 |
注意:
- 数值数据类型的记录之间才能进行算术运算 ;
- 相同数据类型的数据之间才能进行比较 ;
测试:
SELECT studentno,studentname FROM student
WHERE studentname LIKE '刘%';
SELECT studentno,studentname FROM student
WHERE studentname LIKE '刘_';
SELECT studentno,studentname FROM student
WHERE studentname LIKE '刘__';
SELECT studentno,studentname FROM student
WHERE studentname LIKE '%嘉%';
SELECT studentno,studentname FROM student
WHERE studentno IN (1000,1001,1002);
SELECT studentno,studentname,address FROM student
WHERE address IN ('北京','南京','河南洛阳');
SELECT studentname FROM student
WHERE BornDate IS NULL;
SELECT studentname FROM student
WHERE BornDate IS NOT NULL;
SELECT studentname FROM student
WHERE Address='' OR Address IS NULL;
4.4、连接查询
JOIN 对比
操作符名称 | 描述 |
---|
INNER JOIN | 如果表中有至少一个匹配,则返回行 | LEFT JOIN | 即使右表中没有匹配,也从左表中返回所有的行 | RIGHT JOIN | 即使左表中没有匹配,也从右表中返回所有的行 |
测试
SELECT * FROM student;
SELECT * FROM result;
SELECT s.studentno,studentname,subjectno,StudentResult
FROM student s
INNER JOIN result r
ON r.studentno = s.studentno
SELECT s.studentno,studentname,subjectno,StudentResult
FROM student s
RIGHT JOIN result r
ON r.studentno = s.studentno
SELECT s.studentno,studentname,subjectno,StudentResult
FROM student s , result r
WHERE r.studentno = s.studentno
SELECT s.studentno,studentname,subjectno,StudentResult
FROM student s
LEFT JOIN result r
ON r.studentno = s.studentno
SELECT s.studentno,studentname,subjectno,StudentResult
FROM student s
LEFT JOIN result r
ON r.studentno = s.studentno
WHERE StudentResult IS NULL
SELECT s.studentno,studentname,subjectname,StudentResult
FROM student s
INNER JOIN result r
ON r.studentno = s.studentno
INNER JOIN `subject` sub
ON sub.subjectno = r.subjectno
自连接
CREATE TABLE `category` (
`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 `category` (`categoryid`, `pid`, `categoryName`)
VALUES('2','1','信息技术'),
('3','1','软件开发'),
('4','3','数据库'),
('5','1','美术设计'),
('6','3','web开发'),
('7','5','ps技术'),
('8','2','办公信息');
SELECT a.categoryName AS '父栏目',b.categoryName AS '子栏目'
FROM category AS a,category AS b
WHERE a.`categoryid`=b.`pid`
SELECT s.studentno,studentname,subjectname,StudentResult
FROM student s
INNER JOIN result r
ON r.studentno = s.studentno
INNER JOIN `subject` sub
ON sub.subjectno = r.subjectno
SELECT studentno AS 学号,studentname AS 学生姓名,gradename AS 年级名称
FROM student s
INNER JOIN grade g
ON s.`GradeId` = g.`GradeID`
SELECT subjectname AS 科目名称,gradename AS 年级名称
FROM SUBJECT sub
INNER JOIN grade g
ON sub.gradeid = g.gradeid
SELECT s.studentno,studentname,subjectname,StudentResult
FROM student s
INNER JOIN result r
ON r.studentno = s.studentno
INNER JOIN `subject` sub
ON r.subjectno = sub.subjectno
WHERE subjectname='数据库结构-1'
4.5、排序和分页
测试
SELECT s.studentno,studentname,subjectname,StudentResult
FROM student s
INNER JOIN result r
ON r.studentno = s.studentno
INNER JOIN `subject` sub
ON r.subjectno = sub.subjectno
WHERE subjectname='数据库结构-1'
ORDER BY StudentResult DESC
SELECT s.studentno,studentname,subjectname,StudentResult
FROM student s
INNER JOIN result r
ON r.studentno = s.studentno
INNER JOIN `subject` sub
ON r.subjectno = sub.subjectno
WHERE subjectname='数据库结构-1'
ORDER BY StudentResult DESC , studentno
LIMIT 0,5
SELECT s.studentno,studentname,subjectname,StudentResult
FROM student s
INNER JOIN result r
ON r.studentno = s.studentno
INNER JOIN `subject` sub
ON r.subjectno = sub.subjectno
WHERE subjectname='JAVA第一学年'
ORDER BY StudentResult DESC
LIMIT 0,10
4.6、子查询
SELECT studentno,r.subjectno,StudentResult
FROM result r
INNER JOIN `subject` sub
ON r.`SubjectNo`=sub.`SubjectNo`
WHERE subjectname = '数据库结构-1'
ORDER BY studentresult DESC;
SELECT studentno,subjectno,StudentResult
FROM result
WHERE subjectno=(
SELECT subjectno FROM `subject`
WHERE subjectname = '数据库结构-1'
)
ORDER BY studentresult DESC;
SELECT s.studentno,studentname
FROM student s
INNER JOIN result r
ON s.`StudentNo` = r.`StudentNo`
INNER JOIN `subject` sub
ON sub.`SubjectNo` = r.`SubjectNo`
WHERE subjectname = '高等数学-2' AND StudentResult>=80
SELECT r.studentno,studentname FROM student s
INNER JOIN result r ON s.`StudentNo`=r.`StudentNo`
WHERE StudentResult>=80
SELECT r.studentno,studentname FROM student s
INNER JOIN result r ON s.`StudentNo`=r.`StudentNo`
WHERE StudentResult>=80 AND subjectno=(
SELECT subjectno FROM `subject`
WHERE subjectname = '高等数学-2'
)
SELECT studentno,studentname FROM student WHERE studentno IN(
SELECT studentno FROM result WHERE StudentResult>=80 AND subjectno=(
SELECT subjectno FROM `subject` WHERE subjectname = '高等数学-2'
)
)
5、MySQL函数
官方文档
5.1、常用函数
数据函数
SELECT ABS(-8);
SELECT CEILING(9.4);
SELECT FLOOR(9.4);
SELECT RAND();
SELECT SIGN(0);
字符串函数
SELECT CHAR_LENGTH('狂神说坚持就能成功');
SELECT CONCAT('我','爱','程序');
SELECT INSERT('我爱编程helloworld',1,2,'超级热爱');
SELECT LOWER('KuangShen');
SELECT UPPER('KuangShen');
SELECT LEFT('hello,world',5);
SELECT RIGHT('hello,world',5);
SELECT REPLACE('狂神说坚持就能成功','坚持','努力');
SELECT SUBSTR('狂神说坚持就能成功',4,6);
SELECT REVERSE('狂神说坚持就能成功');
SELECT REPLACE(studentname,'周','邹') AS 新名字
FROM student WHERE studentname LIKE '周%';
日期和时间函数
SELECT CURRENT_DATE();
SELECT CURDATE();
SELECT NOW();
SELECT LOCALTIME();
SELECT SYSDATE();
SELECT YEAR(NOW());
SELECT MONTH(NOW());
SELECT DAY(NOW());
SELECT HOUR(NOW());
SELECT MINUTE(NOW());
SELECT SECOND(NOW());
系统信息函数
SELECT VERSION();
SELECT USER();
5.2、聚合函数
函数名称 | 描述 |
---|
COUNT() | 返回满足Select条件的记录总和数,如 select count(*) 【不建议使用 *,效率低】 | SUM() | 返回数字字段或表达式列作统计,返回一列的总和。 | AVG() | 通常为数值字段或表达列作统计,返回一列的平均值 | MAX() | 可以为数值字段,字符字段或表达式列作统计,返回最大的值。 | MIN() | 可以为数值字段,字符字段或表达式列作统计,返回最小的值。 |
SELECT COUNT(studentname) FROM student;
SELECT COUNT(*) FROM student;
SELECT COUNT(1) FROM student;
SELECT SUM(StudentResult) AS 总和 FROM result;
SELECT AVG(StudentResult) AS 平均分 FROM result;
SELECT MAX(StudentResult) AS 最高分 FROM result;
SELECT MIN(StudentResult) AS 最低分 FROM result;
题目:
SELECT subjectname,AVG(studentresult) AS 平均分,MAX(StudentResult) AS 最高 分,MIN(StudentResult) AS 最低分
FROM result AS r
INNER JOIN `subject` AS s
ON r.subjectno = s.subjectno
GROUP BY r.subjectno
HAVING 平均分>80;
MD5 加密
一、MD5简介 MD5即Message-Digest Algorithm 5(信息-摘要算法5),用于确保信息传输完整一致。是计算机广泛使用的杂凑算法之一(又译摘要算法、哈希算法),主流编程语言普遍已有MD5实现。将数据(如汉字)运算为另一固定长度值,是杂凑算法的基础原理,MD5的前身有MD2、MD3和MD4。 百度搜索md5介绍
二、实现数据加密
新建一个表 testmd5
CREATE TABLE `testmd5` (
`id` INT(4) NOT NULL,
`name` VARCHAR(20) NOT NULL,
`pwd` VARCHAR(50) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8
插入一些数据
INSERT INTO testmd5 VALUES(1,'kuangshen','123456'),(2,'qinjiang','456789')
如果我们要对pwd这一列数据进行加密,语法是:
update testmd5 set pwd = md5(pwd);
如果单独对某个用户(如kuangshen)的密码加密:
INSERT INTO testmd5 VALUES(3,'kuangshen2','123456')
update testmd5 set pwd = md5(pwd) where name = 'kuangshen2';
插入新的数据自动加密
INSERT INTO testmd5 VALUES(4,'kuangshen3',md5('123456'));
查询登录用户信息(md5对比使用,查看用户输入加密后的密码进行比对)
SELECT * FROM testmd5 WHERE `name`='kuangshen' AND pwd=MD5('123456');
5.3、小结(重点)
abs(x)
format(x, d)
ceil(x)
floor(x)
round(x)
mod(m, n)
pi()
pow(m, n)
sqrt(x)
rand()
truncate(x, d)
now(), current_timestamp();
current_date();
current_time();
date('yyyy-mm-dd hh:ii:ss');
time('yyyy-mm-dd hh:ii:ss');
date_format('yyyy-mm-dd hh:ii:ss', '%d %y %a %d %m %b %j');
unix_timestamp();
from_unixtime();
length(string)
char_length(string)
substring(str, position [,length])
replace(str ,search_str ,replace_str)
instr(string ,substring)
concat(string [,...])
charset(str)
lcase(string)
left(string, length)
load_file(file_name)
locate(substring, string [,start_position])
lpad(string, length, pad)
ltrim(string)
repeat(string, count)
rpad(string, length, pad)
rtrim(string)
strcmp(string1 ,string2)
count()
sum();
max();
min();
avg();
group_concat()
md5();
default();
6、事务
6.1、概述
什么是事务
- 事务就是将一组SQL语句放在同一批次内去执行
- 如果一个SQL语句出错,则该批次内的所有SQL都将被取消执行
- MySQL事务处理只支持InnoDB和BDB数据表类型
事务的ACID原则 百度 ACID
原子性(Atomic)
- 整个事务中的所有操作,要么全部完成,要么全部不完成,不可能停滞在中间某个环节。事务在执行过程中发生错误,会被回滚(ROLLBACK)到事务开始前的状态,就像这个事务从来没有执行过一样。
一致性(Consist)
- 一个事务可以封装状态改变(除非它是一个只读的)。事务必须始终保持系统处于一致的状态,不管在任何给定的时间并发事务有多少。也就是说:如果事务是并发多个,系统也必须如同串行事务一样操作。其主要特征是保护性和不变性(Preserving an Invariant),以转账案例为例,假设有五个账户,每个账户余额是100元,那么五个账户总额是500元,如果在这个5个账户之间同时发生多个转账,无论并发多少个,比如在A与B账户之间转账5元,在C与D账户之间转账10元,在B与E之间转账15元,五个账户总额也应该还是500元,这就是保护性和不变性。
隔离性(Isolated)
- 隔离状态执行事务,使它们好像是系统在给定时间内执行的唯一操作。如果有两个事务,运行在相同的时间内,执行相同的功能,事务的隔离性将确保每一事务在系统中认为只有该事务在使用系统。这种属性有时称为串行化,为了防止事务操作间的混淆,必须串行化或序列化请求,使得在同一时间仅有一个请求用于同一数据。
持久性(Durable)
- 在事务完成以后,该事务对数据库所作的更改便持久的保存在数据库之中,并不会被回滚。
6.2、事务实现
基本语法:
SET autocommit = 0;
SET autocommit = 1;
START TRANSACTION
COMMIT
ROLLBACK
SET autocommit =1;
SAVEPOINT 保存点名称
ROLLBACK TO SAVEPOINT 保存点名称
RELEASE SAVEPOINT 保存点名称
事务处理步骤:
6.3、测试题目
CREATE DATABASE `shop`CHARACTER SET utf8 COLLATE utf8_general_ci; USE `shop`;
CREATE TABLE `account` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`name` VARCHAR(32) NOT NULL,
`cash` DECIMAL(9,2) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8
INSERT INTO account (`name`,`cash`) VALUES('A',2000.00),('B',10000.00)
SET autocommit = 0;
START TRANSACTION;
UPDATE account SET cash=cash-500 WHERE `name`='A';
UPDATE account SET cash=cash+500 WHERE `name`='B';
COMMIT;
SET autocommit = 1;
7、索引
7.1、索引分类
索引的作用
- 提高查询速度
- 确保数据的唯一性
- 可以加速表和表之间的连接 , 实现表与表之间的参照完整性
- 使用分组和排序子句进行数据检索时 , 可以显著减少分组和排序的时间
- 全文检索字段进行搜索优化.
分类
- 主键索引 (Primary Key)
- 唯一索引 (Unique)
- 常规索引 (Index)
- 全文索引 (FullText)
7.2、主键索引
主键 : 某一个属性组能唯一标识一条记录
特点 :
- 最常见的索引类型
- 确保数据记录的唯一性
- 确定特定数据记录在数据库中的位置
7.3、唯一索引
作用 : 避免同一个表中某数据列中的值重复
与主键索引的区别
CREATE TABLE `Grade`(
`GradeID` INT(11) AUTO_INCREMENT PRIMARYKEY,
`GradeName` VARCHAR(32) NOT NULL UNIQUE
)
7.4、常规索引
作用 : 快速定位特定数据
注意 :
- index 和 key 关键字都可以设置常规索引
- 应加在查询找条件的字段
- 不宜添加太多常规索引,影响数据的插入,删除和修改操作
CREATE TABLE `result`(
INDEX/KEY `ind` (`studentNo`,`subjectNo`)
)
ALTER TABLE `result` ADD INDEX `ind`(`studentNo`,`subjectNo`);
7.5、全文索引
百度搜索:全文索引 作用 : 快速定位特定数据 注意 :
- 只能用于MyISAM类型的数据表
- 只能用于CHAR , VARCHAR , TEXT数据列类型
- 适合大型数据集
ALTER TABLE `school`.`student` ADD FULLTEXT INDEX `studentname`
(`StudentName`);
EXPLAIN SELECT * FROM student WHERE studentno='1000';
EXPLAIN SELECT *FROM student WHERE MATCH(studentname) AGAINST('love');
关于 EXPLAIN :
百度搜索:explain 中文意思:解释,说明
拓展:测试索引
建表app_user:
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用户表'
批量插入数据:100w
DROP FUNCTION IF EXISTS mock_data;
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), '24736743@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 = '用户9999';
SELECT * FROM app_user WHERE name = '用户9999';
SELECT * FROM app_user WHERE name = '用户9999';
mysql> EXPLAIN SELECT * FROM app_user WHERE name = '用户9999'\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: app_user
partitions: NULL
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 992759
filtered: 10.00
Extra: Using where
1 row in set, 1 warning (0.00 sec)
CREATE INDEX idx_app_user_name ON app_user(name);
mysql> EXPLAIN SELECT * FROM app_user WHERE name = '用户9999'\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: app_user
partitions: NULL
type: ref
possible_keys: idx_app_user_name
key: idx_app_user_name
key_len: 203
ref: const
rows: 1
filtered: 100.00
Extra: NULL
1 row in set, 1 warning (0.00 sec)
mysql> SELECT * FROM app_user WHERE name = '用户9999';
1 row in set (0.00 sec)
mysql> SELECT * FROM app_user WHERE name = '用户9999';
1 row in set (0.00 sec)
mysql> SELECT * FROM app_user WHERE name = '用户9999';
1 row in set (0.00 sec)
7.6、索引准则
- 索引不是越多越好
- 不要对经常变动的数据加索引
- 小数据量的表建议不要加索引
- 索引一般应加在查找条件的字段
7.7、索引的数据结构
hash类型的索引:查询单条快,范围查询慢
btree类型的索引:b+树,层数越多,数据量指数级增长(我们就用它,因为innodb默认支持它)
InnoDB 支持事务,支持行级别锁定,支持 B-tree、Full-text 等索引,不支持 Hash 索引;
MyISAM 不支持事务,支持表级别锁定,支持 B-tree、Full-text 等索引,不支持 Hash 索引;
Memory 不支持事务,支持表级别锁定,支持 B-tree、Hash 等索引,不支持 Full-text 索引;
NDB 支持事务,支持行级别锁定,支持 Hash 索引,不支持 B-tree、Full-text 等索引;
Archive 不支持事务,支持表级别锁定,不支持 B-tree、Hash、Full-text 等索引;
关于索引的本质:
8、权限管理
8.1、用户管理
1、使用SQLyog 创建用户,并授予权限演示
2、基本命令
用户信息表:mysql.user
FLUSH PRIVILEGES
CREATE USER 用户名 IDENTIFIED BY [PASSWORD] 密码(字符串)
- 必须拥有mysql数据库的全局CREATE USER权限,或拥有INSERT权限。
- 只能创建用户,不能赋予权限。
- 用户名,注意引号:如 'user_name'@'192.168.1.1'
- 密码也需引号,纯数字密码也要加引号
- 要在纯文本中指定密码,需忽略PASSWORD关键词。要把密码指定为由PASSWORD()函数返回的
混编值,需包含关键字PASSWORD
RENAME USER old_user TO new_user
SET PASSWORD = PASSWORD('密码')
SET PASSWORD FOR 用户名 = PASSWORD('密码')
DROP USER 用户名
GRANT 权限列表 ON 表名 TO 用户名 [IDENTIFIED BY [PASSWORD] 'password']
- all privileges 表示所有权限
- *.* 表示所有库的所有表
- 库名.表名 表示某库下面的某表
SHOW GRANTS FOR 用户名
SHOW GRANTS; 或 SHOW GRANTS FOR CURRENT_USER; 或 SHOW GRANTS FOR
CURRENT_USER();
REVOKE 权限列表 ON 表名 FROM 用户名
REVOKE ALL PRIVILEGES, GRANT OPTION FROM 用户名
权限解释
ALL [PRIVILEGES]
ALTER
ALTER ROUTINE
CREATE
CREATE ROUTINE
CREATE TEMPORARY TABLES
CREATE USER
PRIVILEGES。
CREATE VIEW
DELETE
DROP
EXECUTE
FILE
INDEX
INSERT
LOCK TABLES
PROCESS
REFERENCES
RELOAD
REPLICATION CLIENT
REPLICATION SLAVE
SELECT
SHOW DATABASES
SHOW VIEW
SHUTDOWN
SUPER
mysqladmin debug命令;允许您连接(一次),即使已达到max_connections。
UPDATE
USAGE
GRANT OPTION
ANALYZE [LOCAL | NO_WRITE_TO_BINLOG] TABLE 表名 ...
CHECK TABLE tbl_name [, tbl_name] ... [option] ...
option = {QUICK | FAST | MEDIUM | EXTENDED | CHANGED}
OPTIMIZE [LOCAL | NO_WRITE_TO_BINLOG] TABLE tbl_name [, tbl_name] ...
8.2、MySQL备份
数据库备份必要性
MySQL数据库备份方法
- mysqldump备份工具
- 数据库管理工具,如SQLyog
- 直接拷贝数据库文件和相关配置文件
mysqldump客户端 作用 :
- 转储数据库
- 搜集数据库进行备份
- 将数据转移到另一个SQL服务器,不一定是MySQL服务器
1. 导出一张表
mysqldump -u用户名 -p密码 库名 表名 > 文件名(D:/a.sql)
2. 导出多张表
mysqldump -u用户名 -p密码 库名 表1 表2 表3 > 文件名(D:/a.sql)
3. 导出所有表
mysqldump -u用户名 -p密码 库名 > 文件名(D:/a.sql)
4. 导出一个库
mysqldump -u用户名 -p密码 -B 库名 > 文件名(D:/a.sql)
可以-w携带备份条件
1. 在登录mysql的情况下:
source 备份文件
2. 在不登录的情况下
mysql -u用户名 -p密码 库名 < 备份文件
9、规范化数据库设计
9.1、为什么需要数据库设计
当数据库比较复杂时我们需要设计数据库
糟糕的数据库设计 :
- 数据冗余,存储空间浪费
- 数据更新和插入的异常
- 程序性能差
良好的数据库设计 :
- 节省数据的存储空间
- 能够保证数据的完整性
- 方便进行数据库应用系统的开发
软件项目开发周期中数据库设计 :
- 需求分析阶段: 分析客户的业务和数据处理需求
- 概要设计阶段:设计数据库的E-R模型图 , 确认需求信息的正确和完整.
设计数据库步骤
- 收集信息
- 与该系统有关人员进行交流 , 座谈 , 充分了解用户需求 , 理解数据库需要完成的任务.
- 标识实体[Entity]
- 标识每个实体需要存储的详细信息[Attribute]
- 标识实体之间的关系[Relationship]
9.2、三大范式
问题 : 为什么需要数据规范化?
不合规范的表设计会导致的问题:
百度搜索:三大范式
第一范式 (1st NF) 第一范式的目标是确保每列的原子性,如果每列都是不可再分的最小数据单元,则满足第一范式
第二范式(2nd NF) 第二范式(2NF)是在第一范式(1NF)的基础上建立起来的,即满足第二范式(2NF)必须先满足第一范式(1NF)。
第二范式要求每个表只描述一件事情
第三范式(3rd NF) 如果一个关系满足第二范式,并且除了主键以外的其他列都不传递依赖于主键列,则满足第三范式. 第三范式需要确保数据表中的每一列数据都和主键直接相关,而不能间接相关。
规范化和性能的关系
为满足某种商业目标 , 数据库性能比规范化数据库更重要 在数据规范化的同时 , 要综合考虑数据库的性能 通过在给定的表中添加额外的字段,以大量减少需要从中搜索信息所需的时间 通过在给定的表中插入计算列,以方便查询
10、JDBC
10.1、数据库驱动
这里的驱动的概念和平时听到的那种驱动的概念是一样的,比如平时购买的声卡,网卡直接插到计算机上面是不能用的,必须要安装相应的驱动程序之后才能够使用声卡和网卡,同样道理,我们安装好数据库之后,我们的应用程序也是不能直接使用数据库的,必须要通过相应的数据库驱动程序,通过驱动程序去和数据库打交道,如下所示:
10.2、JDBC介绍
SUN公司为了简化、统一对数据库的操作,定义了一套Java操作数据库的规范(接口),称之为JDBC。这套接口由数据库厂商去实现,这样,开发人员只需要学习jdbc接口,并通过jdbc加载具体的驱动,就可以操作数据库。
如下图所示: JDBC全称为:Java Data Base Connectivity(java数据库连接),它主要由接口组成。 组成JDBC的2个包:java.sql、javax.sql 开发JDBC应用需要以上2个包的支持外,还需要导入相应JDBC的数据库实现(即数据库驱动)。
10.3、编写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(40),
email VARCHAR(60),
birthday DATE
);
INSERT INTO users(id,NAME,PASSWORD,email,birthday)
VALUES(1,'zhansan','123456','zs@sina.com','1980-12-04'),
(2,'lisi','123456','lisi@sina.com','1981-12-04'),
(3,'wangwu','123456','wangwu@sina.com','1979-12-04');
新建一个Java工程,并导入数据驱动
编写程序从user表中读取数据,并打印在命令行窗口中。
package com.kuang.lesson01;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
public class JdbcFirstDemo {
public static void main(String[] args) throws Exception {
String url = "jdbc:mysql://localhost:3306/jdbcStudy?
useUnicode=true&characterEncoding=utf8&useSSL=true";
String username = "root";
String password = "123456";
Class.forName("com.mysql.jdbc.Driver");
Connection conn = DriverManager.getConnection(url, username, password);
Statement st = conn.createStatement();
String sql = "select id,name,password,email,birthday from users";
ResultSet rs = st.executeQuery(sql);
while(rs.next()){
System.out.println("id=" + rs.getObject("id"));
System.out.println("name=" + rs.getObject("name"));
System.out.println("password=" + rs.getObject("password"));
System.out.println("email=" + rs.getObject("email"));
System.out.println("birthday=" + rs.getObject("birthday"));
}
rs.close();
st.close();
conn.close();
}
}
10.4、对象说明
DriverManager类讲解
Jdbc程序中的DriverManager用于加载驱动,并创建与数据库的链接,这个API的常用方法:
DriverManager.registerDriver(new Driver())
DriverManager.getConnection(url, user, password)
注意:在实际开发中并不推荐采用registerDriver方法注册驱动。原因有二:
- 查看Driver的源代码可以看到,如果采用此种方式,会导致驱动程序注册两次,也就是在内存中会有两个Driver对象。
- 程序依赖mysql的api,脱离mysql的jar包,程序将无法编译,将来程序切换底层数据库将会非常麻烦。
推荐方式:Class.forName(“com.mysql.jdbc.Driver”);
采用此种方式不会导致驱动对象在内存中重复出现,并且采用此种方式,程序仅仅只需要一个字符串,不需要依赖具体的驱动,使程序的灵活性更高。
数据库URL讲解
URL用于标识数据库的位置,通过URL地址告诉JDBC程序连接哪个数据库,URL的写法为: 常用数据库URL地址的写法:
- Oracle写法:jdbc:oracle:thin:@localhost:1521:sid
- SqlServer写法:jdbc:microsoft:sqlserver://localhost:1433; DatabaseName=sid
- MySql写法:jdbc:mysql://localhost:3306/sid
如果连接的是本地的Mysql数据库,并且连接使用的端口是3306,那么的url地址可以简写为 jdbc:mysql:///数据库
Connection类讲解
Jdbc程序中的Connection,它用于代表数据库的链接,Collection是数据库编程中最重要的一个对象,客户端与数据库所有交互都是通过connection对象完成的,这个对象的常用方法:
- createStatement():创建向数据库发送sql的statement对象。
- prepareStatement(sql) :创建向数据库发送预编译sql的PrepareSatement对象。
- setAutoCommit(boolean autoCommit):设置事务是否自动提交。
- commit() :在链接上提交事务。
- rollback() :在此链接上回滚事务。
Statement类讲解
Jdbc程序中的Statement对象用于向数据库发送SQL语句, Statement对象常用方法:
- executeQuery(String sql) :用于向数据发送查询语句。
- executeUpdate(String sql):用于向数据库发送insert、update或delete语句
- execute(String sql):用于向数据库发送任意sql语句
- addBatch(String sql) :把多条sql语句放到一个批处理中。
- executeBatch():向数据库发送一批sql语句执行。
ResultSet类讲解
Jdbc程序中的ResultSet用于代表Sql语句的执行结果。Resultset封装执行结果时,采用的类似于表格的方式。ResultSet 对象维护了一个指向表格数据行的游标,初始的时候,游标在第一行之前,调用ResultSet.next() 方法,可以使游标指向具体的数据行,进行调用方法获取该行的数据。
ResultSet既然用于封装执行结果的,所以该对象提供的都是用于获取数据的get方法:
- 获取任意类型的数据
- getObject(int index)
- getObject(string columnName)
- 获取指定类型的数据,例如:
- getString(int index)
- getString(String columnName)
ResultSet还提供了对结果集进行滚动的方法:
- next():移动到下一行
- Previous():移动到前一行
- absolute(int row):移动到指定行
- beforeFirst():移动resultSet的最前面。
- afterLast() :移动到resultSet的最后面。
释放资源
Jdbc程序运行完后,切记要释放程序在运行过程中,创建的那些与数据库进行交互的对象,这些对象通常是ResultSet, Statement和Connection对象,特别是Connection对象,它是非常稀有的资源,用完后必须马上释放,如果Connection不能及时、正确的关闭,极易导致系统宕机。Connection的使用原则是尽量晚创建,尽量早的释放。
为确保资源释放代码能运行,资源释放代码也一定要放在finally语句中。
10.5、statement对象
Jdbc中的statement对象用于向数据库发送SQL语句,想完成对数据库的增删改查,只需要通过这个对象向数据库发送增删改查语句即可。
Statement对象的executeUpdate方法,用于向数据库发送增、删、改的sql语句,executeUpdate执行完后,将会返回一个整数(即增删改语句导致了数据库几行数据发生了变化)。
Statement.executeQuery方法用于向数据库发送查询语句,executeQuery方法返回代表查询结果的ResultSet对象。
CRUD操作-create
使用executeUpdate(String sql)方法完成数据添加操作,示例操作:
Statement st = conn.createStatement();
String sql = "insert into user(….) values(…..) ";
int num = st.executeUpdate(sql);
if(num>0){
System.out.println("插入成功!!!");
}
CRUD操作-delete
使用executeUpdate(String sql)方法完成数据删除操作,示例操作:
Statement st = conn.createStatement();
String sql = "delete from user where id=1";
int num = st.executeUpdate(sql);
if(num>0){
System.out.println(“删除成功!!!");
}
CRUD操作-update
使用executeUpdate(String sql)方法完成数据修改操作,示例操作:
Statement st = conn.createStatement();
String sql = "update user set name='' where name=''";
int num = st.executeUpdate(sql);
if(num>0){
System.out.println(“修改成功!!!");
}
CRUD操作-read
使用executeQuery(String sql)方法完成数据查询操作,示例操作:
Statement st = conn.createStatement();
String sql = "select * from user where id=1";
ResultSet rs = st.executeUpdate(sql);
while(rs.next()){
}
使用jdbc对数据库增删改查 1、新建一个 lesson02 的包 2、在src目录下创建一个db.properties文件,如下图所示:
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbcStudy?
useUnicode=true&characterEncoding=utf8&useSSL=true
username=root
password=123456
3、在lesson02 下新建一个 utils 包,新建一个类 JdbcUtils
package com.kuang.lesson02.utils;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
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{
InputStream in =
JdbcUtils.class.getClassLoader().getResourceAsStream("db.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) {
throw new ExceptionInInitializerError(e);
}
}
public static Connection getConnection() throws SQLException{
return DriverManager.getConnection(url, username,password);
}
对象,存储查询结果的ResultSet对象
public static void release(Connection conn,Statement st,ResultSet rs){
if(rs!=null){
try{
rs.close();
}catch (Exception e) {
e.printStackTrace();
}
rs = null;
}
if(st!=null){
try{
st.close();
}catch (Exception e) {
e.printStackTrace();
}
}
if(conn!=null){
try{
conn.close();
}catch (Exception e) {
e.printStackTrace();
}
}
}
}
使用statement对象完成对数据库的CRUD操作
1、插入一条数据
public class TestInsert {
public static void main(String[] args) {
Connection conn = null;
Statement st = null;
ResultSet rs = null;
try{
conn = JdbcUtils.getConnection();
st = conn.createStatement();
String sql = "insert into users(id,name,password,email,birthday)
" +
"values(4,'kuangshen','123','24736743@qq.com','2020-01-
01')";
int num = st.executeUpdate(sql);
if(num>0){
System.out.println("插入成功!!");
}
}catch (Exception e) {
e.printStackTrace();
}finally{
JdbcUtils.release(conn, st, rs);
}
}
}
2、删除一条数据
import com.kuang.lesson02.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
public class TestDelete {
public static void main(String[] args) {
Connection conn = null;
Statement st = null;
ResultSet rs = null;
try{
conn = JdbcUtils.getConnection();
String sql = "delete from users where id=4";
st = conn.createStatement();
int num = st.executeUpdate(sql);
if(num>0){
System.out.println("删除成功!!");
}
}catch (Exception e) {
e.printStackTrace();
}finally{
JdbcUtils.release(conn, st, rs);
}
}
}
3、更新一条数据
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
public class TestUpdate {
public static void main(String[] args) {
Connection conn = null;
Statement st = null;
ResultSet rs = null;
try{
conn = JdbcUtils.getConnection();
String sql = "update users set
name='kuangshen',email='24736743@qq.com' where id=3";
st = conn.createStatement();
int num = st.executeUpdate(sql);
if(num>0){
System.out.println("更新成功!!");
}
}catch (Exception e) {
e.printStackTrace();
}finally{
JdbcUtils.release(conn, st, rs);
}
}
}
4、查询数据
public class TestSelect {
public static void main(String[] args) {
Connection conn = null;
Statement st = null;
ResultSet rs = null;
try{
conn = JdbcUtils.getConnection();
String sql = "select * from users where id=3";
st = conn.createStatement();
rs = st.executeQuery(sql);
if(rs.next()){
System.out.println(rs.getString("name"));
}
}catch (Exception e) {
e.printStackTrace();
}finally{
JdbcUtils.release(conn, st, rs);
}
}
}
SQL 注入问题
通过巧妙的技巧来拼接字符串,造成SQL短路,从而获取数据库数据
public class SQL注入 {
public static void main(String[] args) {
login("'or '1=1","123456");
}
public static void login(String username,String password){
Connection conn = null;
Statement st = null;
ResultSet rs = null;
try{
conn = JdbcUtils.getConnection();
'123456'
String sql = "select * from users where name='"+username+"' and
password='"+password+"'";
st = conn.createStatement();
rs = st.executeQuery(sql);
while(rs.next()){
System.out.println(rs.getString("name"));
System.out.println(rs.getString("password"));
System.out.println("==============");
}
}catch (Exception e) {
e.printStackTrace();
}finally{
JdbcUtils.release(conn, st, rs);
}
}
}
10.6、PreparedStatement对象
PreperedStatement是Statement的子类,它的实例对象可以通过调用Connection.preparedStatement()方法获得,相对于Statement对象而言:PreperedStatement可以避免SQL注入的问题。
Statement会使数据库频繁编译SQL,可能造成数据库缓冲区溢出。
PreparedStatement可对SQL进行预编译,从而提高数据库的执行效率。并且PreperedStatement对于sql中的参数,允许使用占位符的形式进行替换,简化sql语句的编写。
使用PreparedStatement对象完成对数据库的CRUD操作
1、插入数据
public class TestInsert {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement st = null;
ResultSet rs = null;
try{
conn = JdbcUtils.getConnection();
String sql = "insert into users(id,name,password,email,birthday)
values(?,?,?,?,?)";
st = conn.prepareStatement(sql);
st.setInt(1, 4);
st.setString(2, "kuangshen");
st.setString(3, "123");
st.setString(4, "24736743@qq.com");
st.setDate(5, new java.sql.Date(new
Date().getTime()));
int num = st.executeUpdate();
if(num>0){
System.out.println("插入成功!!");
}
}catch (Exception e) {
e.printStackTrace();
}finally{
JdbcUtils.release(conn, st, rs);
}
}
}
2、删除一条数据
public class TestDelete {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement st = null;
ResultSet rs = null;
try{
conn = JdbcUtils.getConnection();
String sql = "delete from users where id=?";
st = conn.prepareStatement(sql);
st.setInt(1, 4);
int num = st.executeUpdate();
if(num>0){
System.out.println("删除成功!!");
}
}catch (Exception e) {
e.printStackTrace();
}finally{
JdbcUtils.release(conn, st, rs);
}
}
}
3、更新一条数据
public class TestUpdate {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement st = null;
ResultSet rs = null;
try{
conn = JdbcUtils.getConnection();
String sql = "update users set name=?,email=? where id=?";
st = conn.prepareStatement(sql);
st.setString(1, "kuangshen");
st.setString(2, "24736743@qq.com");
st.setInt(3, 2);
int num = st.executeUpdate();
if(num>0){
System.out.println("更新成功!!");
}
}catch (Exception e) {
e.printStackTrace();
}finally{
JdbcUtils.release(conn, st, rs);
}
}
}
4、查询一条数据
public class TestSelect {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement st = null;
ResultSet rs = null;
try{
conn = JdbcUtils.getConnection();
String sql = "select * from users where id=?";
st = conn.prepareStatement(sql);
st.setInt(1, 1);
rs = st.executeQuery();
if(rs.next()){
System.out.println(rs.getString("name"));
}
}catch (Exception e) {
}finally{
JdbcUtils.release(conn, st, rs);
}
}
}
避免SQL 注入
public class SQL注入 {
public static void main(String[] args) {
login("'or '1=1","123456");
}
public static void login(String username,String password){
Connection conn = null;
PreparedStatement st = null;
ResultSet rs = null;
try{
conn = JdbcUtils.getConnection();
'123456'
String sql = "select * from users where name=? and password=?";
st = conn.prepareStatement(sql);
st.setString(1,username);
st.setString(2,password);
rs = st.executeQuery();
while(rs.next()){
System.out.println(rs.getString("name"));
System.out.println(rs.getString("password"));
System.out.println("==============");
}
}catch (Exception e) {
e.printStackTrace();
}finally{
JdbcUtils.release(conn, st, rs);
}
}
}
原理:执行的时候参数会用引号包起来,并把参数中的引号作为转义字符,从而避免了参数也作为条件的一部分
10.7、事务
概念
事务指逻辑上的一组操作,组成这组操作的各个单元,要不全部成功,要不全部不成功。 >ACID 原则
原子性(Atomic)
- 整个事务中的所有操作,要么全部完成,要么全部不完成,不可能停滞在中间某个环节。事务在执行过程中发生错误,会被回滚(ROLLBACK)到事务开始前的状态,就像这个事务从来没有执行过一样。
一致性(Consist)
- 一个事务可以封装状态改变(除非它是一个只读的)。事务必须始终保持系统处于一致的状态,不管在任何给定的时间并发事务有多少。也就是说:如果事务是并发多个,系统也必须如同串行事务一样操作。其主要特征是保护性和不变性(Preserving an Invariant),以转账案例为例,假设有五个账户,每个账户余额是100元,那么五个账户总额是500元,如果在这个5个账户之间同时发生多个转账,无论并发多少个,比如在A与B账户之间转账5元,在C与D账户之间转账10元,在B与E之间转账15元,五个账户总额也应该还是500元,这就是保护性和不变性。
隔离性(Isolated)
- 隔离状态执行事务,使它们好像是系统在给定时间内执行的唯一操作。如果有两个事务,运行在相同的时间内,执行相同的功能,事务的隔离性将确保每一事务在系统中认为只有该事务在使用系统。这种属性有时称为串行化,为了防止事务操作间的混淆,必须串行化或序列化请求,使得在同一时间仅有一个请求用于同一数据。
持久性(Durable)
- 在事务完成以后,该事务对数据库所作的更改便持久的保存在数据库之中,并不会被回滚。
隔离性问题
1、脏读:脏读指一个事务读取了另外一个事务未提交的数据。 2、不可重复读:不可重复读指在一个事务内读取表中的某一行数据,多次读取结果不同。 3、虚读(幻读) : 虚读(幻读)是指在一个事务内读取到了别的事务插入的数据,导致前后读取不一致。
代码测试
CREATE TABLE account(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(40),
money FLOAT
);
insert into account(name,money) values('A',1000);
insert into account(name,money) values('B',1000);
insert into account(name,money) values('C',1000);
当Jdbc程序向数据库获得一个Connection对象时,默认情况下这个Connection对象会自动向数据库提交在它上面发送的SQL语句。若想关闭这种默认提交方式,让多条SQL在一个事务中执行,可使用下列的JDBC控制事务语句
- Connection.setAutoCommit(false);//开启事务(start transaction)
- Connection.rollback();//回滚事务(rollback)
- Connection.commit();//提交事务(commit)
程序编写\
1、模拟转账成功时的业务场景
public class TestTransaction1 {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement st = null;
ResultSet rs = null;
try{
conn = JdbcUtils.getConnection();
conn.setAutoCommit(false);
String sql1 = "update account set money=money-100 where
name='A'";
st = conn.prepareStatement(sql1);
st.executeUpdate();
String sql2 = "update account set money=money+100 where
name='B'";
st = conn.prepareStatement(sql2);
st.executeUpdate();
conn.commit();
(commit)
System.out.println("成功!!!");
}catch (Exception e) {
e.printStackTrace();
}finally{
JdbcUtils.release(conn, st, rs);
}
}
}
2、模拟转账过程中出现异常导致有一部分SQL执行失败后让数据库自动回滚事务
public class TestTransaction2 {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement st = null;
ResultSet rs = null;
try{
conn = JdbcUtils.getConnection();
conn.setAutoCommit(false);
String sql1 = "update account set money=money-100 where
name='A'";
st = conn.prepareStatement(sql1);
st.executeUpdate();
务也无法正常提交,此时数据库会自动执行回滚操作
int x = 1/0;
String sql2 = "update account set money=money+100 where
name='B'";
st = conn.prepareStatement(sql2);
st.executeUpdate();
conn.commit();
(commit)
System.out.println("成功!!!");
}catch (Exception e) {
e.printStackTrace();
}finally{
JdbcUtils.release(conn, st, rs);
}
}
}
3、模拟转账过程中出现异常导致有一部分SQL执行失败时手动通知数据库回滚事务
public class TestTransaction3 {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement st = null;
ResultSet rs = null;
try{
conn = JdbcUtils.getConnection();
conn.setAutoCommit(false);
String sql1 = "update account set money=money-100 where
name='A'";
st = conn.prepareStatement(sql1);
st.executeUpdate();
务也无法正常提交
int x = 1/0;
String sql2 = "update account set money=money+100 where
name='B'";
st = conn.prepareStatement(sql2);
st.executeUpdate();
conn.commit();
(commit)
System.out.println("成功!!!");
}catch (Exception e) {
try {
conn.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
e.printStackTrace();
}finally{
JdbcUtils.release(conn, st, rs);
}
}
}
10.8、数据库连接池
用户每次请求都需要向数据库获得链接,而数据库创建连接通常需要消耗相对较大的资源,创建时间也较长。假设网站一天10万访问量,数据库服务器就需要创建10万次连接,极大的浪费数据库的资源,并且极易造成数据库服务器内存溢出、拓机。
数据库连接池的基本概念
数据库连接是一种关键的有限的昂贵的资源,这一点在多用户的网页应用程序中体现的尤为突出.对数据库连接的管理能显著影响到整个应用程序的伸缩性和健壮性,影响到程序的性能指标.数据库连接池正式针对这个问题提出来的.数据库连接池负责分配,管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是重新建立一个。
数据库连接池在初始化时将创建一定数量的数据库连接放到连接池中, 这些数据库连接的数量是由最小数据库连接数来设定的.无论这些数据库连接是否被使用,连接池都将一直保证至少拥有这么多的连接数量.连接池的最大数据库连接数量限定了这个连接池能占有的最大连接数,当应用程序向连接池请求的连接数超过最大连接数量时,这些请求将被加入到等待队列中.
数据库连接池的最小连接数和最大连接数的设置要考虑到以下几个因素:
- 最小连接数:是连接池一直保持的数据库连接,所以如果应用程序对数据库连接的使用量不大,将会有大量的数据库连接资源被浪费.
- 最大连接数:是连接池能申请的最大连接数,如果数据库连接请求超过次数,后面的数据库连接请求将被加入到等待队列中,这会影响以后的数据库操作
- 如果最小连接数与最大连接数相差很大:那么最先连接请求将会获利,之后超过最小连接数量的连接请求等价于建立一个新的数据库连接.不过,这些大于最小连接数的数据库连接在使用完不会马上被释放,他将被放到连接池中等待重复使用或是空间超时后被释放.
编写连接池需实现java.sql.DataSource接口。
开源数据库连接池
现在很多WEB服务器(Weblogic, WebSphere, Tomcat)都提供了DataSoruce的实现,即连接池的实现。 通常我们把DataSource的实现,按其英文含义称之为数据源,数据源中都包含了数据库连接池的实现。
也有一些开源组织提供了数据源的独立实现:
- DBCP 数据库连接池
- C3P0 数据库连接池
在使用了数据库连接池之后,在项目的实际开发中就不需要编写连接数据库的代码了,直接从数据源获得数据库的连接。
DBCP数据源
DBCP 是 Apache 软件基金组织下的开源连接池实现,要使用DBCP数据源,需要应用程序应在系统中增加如下两个 jar 文件:
- Commons-dbcp.jar:连接池的实现
- Commons-pool.jar:连接池实现的依赖库
Tomcat 的连接池正是采用该连接池来实现的。该数据库连接池既可以与应用服务器整合使用,也可由应用程序独立使用。
测试: 1、导入相关jar包
2、在类目录下加入dbcp的配置文件:dbcpconfig.properties
#连接设置
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbcStudy?
useUnicode=true&characterEncoding=utf8&useSSL=true
username=root
password=123456
#<!-- 初始化连接 -->
initialSize=10
#最大连接数量
maxActive=50
#<!-- 最大空闲连接 -->
maxIdle=20
#<!-- 最小空闲连接 -->
minIdle=5
#<!-- 超时等待时间以毫秒为单位 6000毫秒/1000等于60秒 -->
maxWait=60000
#JDBC驱动建立连接时附带的连接属性属性的格式必须为这样:[属性名=property;]
#注意:"user" 与 "password" 两个属性会被明确地传递,因此这里不需要包含他们。
connectionProperties=useUnicode=true;characterEncoding=UTF8
#指定由连接池所创建的连接的自动提交(auto-commit)状态。
defaultAutoCommit=true
#driver default 指定由连接池所创建的连接的只读(read-only)状态。
#如果没有设置该值,则“setReadOnly”方法将不被调用。(某些驱动并不支持只读模式,如:
Informix)
defaultReadOnly=
#driver default 指定由连接池所创建的连接的事务级别(TransactionIsolation)。
#可用值为下列之一:(详情可见javadoc。)NONE,READ_UNCOMMITTED, READ_COMMITTED,
REPEATABLE_READ, SERIALIZABLE
defaultTransactionIsolation=READ_UNCOMMITTED
3、编写工具类 JdbcUtils_DBCP
public class JdbcUtils_DBCP {
private static DataSource ds = null;
static{
try{
InputStream in =
JdbcUtils_DBCP.class.getClassLoader().getResourceAsStream("dbcpconfig.proper
ties");
Properties prop = new Properties();
prop.load(in);
ds = BasicDataSourceFactory.createDataSource(prop);
}catch (Exception e) {
throw new ExceptionInInitializerError(e);
}
}
public static Connection getConnection() throws SQLException{
return ds.getConnection();
}
public static void release(Connection conn,Statement st,ResultSet rs){
if(rs!=null){
try{
rs.close();
}catch (Exception e) {
e.printStackTrace();
}
rs = null;
}
if(st!=null){
try{
st.close();
}catch (Exception e) {
e.printStackTrace();
}
}
if(conn!=null){
try{
conn.close();
}catch (Exception e) {
e.printStackTrace();
}
}
}
}
测试类
public class DBCPTest {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement st = null;
ResultSet rs = null;
try{
conn = JdbcUtils_DBCP.getConnection();
String sql = "insert into users(id,name,password,email,birthday)
values(?,?,?,?,?)";
st = conn.prepareStatement(sql);
st.setInt(1, 5);
st.setString(2, "kuangshen");
st.setString(3, "123");
st.setString(4, "24736743@qq.com");
st.setDate(5, new java.sql.Date(new
Date().getTime()));
int i = st.executeUpdate();
if (i>0){
System.out.println("插入成功");
}
}catch (Exception e) {
e.printStackTrace();
}finally{
JdbcUtils_DBCP.release(conn, st, rs);
}
}
}
C3P0
C3P0是一个开源的JDBC连接池,它实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展。目前使用它的开源项目有Hibernate,Spring等。C3P0数据源在项目开发中使用得比较多。 c3p0与dbcp区别
- dbcp没有自动回收空闲连接的功能
- c3p0有自动回收空闲连接功能
测试 1、导入相关jar包 2、在类目录下加入C3P0的配置文件:c3p0-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
<default-config>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/jdbcStudy?
useUnicode=true&characterEncoding=utf8&useSSL=true</property>
<property name="user">root</property>
<property name="password">123456</property>
<property name="acquireIncrement">5</property>
<property name="initialPoolSize">10</property>
<property name="minPoolSize">5</property>
<property name="maxPoolSize">20</property>
</default-config>
<named-config name="MySQL">
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/jdbcStudy?
useUnicode=true&characterEncoding=utf8&useSSL=true</property>
<property name="user">root</property>
<property name="password">123456</property>
<property name="acquireIncrement">5</property>
<property name="initialPoolSize">10</property>
<property name="minPoolSize">5</property>
<property name="maxPoolSize">20</property>
</named-config>
</c3p0-config>
3、创建工具类
public class JdbcUtils_C3P0 {
private static ComboPooledDataSource ds = null;
static{
try{
必须放在src目录下
ds = new ComboPooledDataSource("MySQL");
据源
}catch (Exception e) {
throw new ExceptionInInitializerError(e);
}
}
public static Connection getConnection() throws SQLException{
return ds.getConnection();
}
public static void release(Connection conn,Statement st,ResultSet rs){
if(rs!=null){
try{
rs.close();
}catch (Exception e) {
e.printStackTrace();
}
rs = null;
}
if(st!=null){
try{
st.close();
}catch (Exception e) {
e.printStackTrace();
}
}
if(conn!=null){
try{
conn.close();
}catch (Exception e) {
e.printStackTrace();
}
}
}
}
测试
public class C3P0Test {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement st = null;
ResultSet rs = null;
try{
conn = JdbcUtils_C3P0.getConnection();
String sql = "insert into users(id,name,password,email,birthday)
values(?,?,?,?,?)";
st = conn.prepareStatement(sql);
st.setInt(1, 6);
st.setString(2, "kuangshen");
st.setString(3, "123");
st.setString(4, "24736743@qq.com");
st.setDate(5, new java.sql.Date(new
Date().getTime()));
int i = st.executeUpdate();
if (i>0){
System.out.println("插入成功");
}
}catch (Exception e) {
e.printStackTrace();
}finally{
JdbcUtils_C3P0.release(conn, st, rs);
}
}
}
|