1基础语句
net start mysql --开启mysqlnet stop mysql --关闭mysqlexit --退出链接mysql -uroot -p密码 --链接数据库ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '新密码'; --修改密码flush privileges; --刷新权限show databases; --查看所有数据库use mysql; --切换至mysql这一个数据库creat dadabase 数据库名 --创建一个数据库show tables; --查看所有表describe 表名; --查看表
数据库语言
DDL:数据库定义语言
DML:数据库操作语言
DQL:数据库查询语言
DCL:数据库控制语言
2.操作数据库
操作数据库
CREATE DATABASE [IF NOT EXISTS] test;--创建数据库,if not exists是判断是否存在,可以不加DROP DATABASE [IF EXISTS] test; --删除数据库
数据库的列数据类型
1.数值
tinyint 非常小的数据(1个字节)
smallint 比较小的数据(2个字节)
mediumint 中间小的数据(3个字节)
int 标准的整形(4个字节) 常用int
bigint 较大的数据(8个字节)
float 单精度浮点数 (4个字节) 常用float
double 双精度浮点数(8个字节)
decimal 字符串形式的浮点数(金融计算是精确计算)
2.字符串
- char 字符串固定大小的(0-255)
- varchar 可变字符串(0-65535) 常用 String
- tinytext 微型文本 2**8-1
- text 文本串 2**16-1
3.时间,日期
- date YYYY-MM-DD,日期格式
- time HH:mm:ss 时间格式
- datetime YYYY-MM-DD HH:mm:ss 最常用的时间格式
- timestamp(时间戳) 1970.1.1到现在的毫秒数,也常用
- year年份表示
4.null
- 没有值
数据库的字段属性(重点)
Unsigned
- 无符号的整数
- 表明该项数据不能声明为负数
zerofill
- 0填充
- int(3): 5->005
Auto incr (自增)
- 自动在上一条记录基础上+1
- 通常用来设计位移的主键~index ,必须是整数类型
- 可以自定义初始值与步长
非空Not NULL
- 如果设置为not null就必须赋值
默认(Defult)
- 设置默认值
创建一个表
USE `student`;CREATE TABLE IF NOT EXISTS `students`(`id` INT(20)NOT NULL AUTO_INCREMENT COMMENT'学号',`name`VARCHAR(10) NOT NULL DEFAULT'匿名' COMMENT'姓名',`age`INT(100)NOT NULL COMMENT'年龄',`password`VARCHAR(50)NOT NULL COMMENT'密码',`sex`VARCHAR(2)NOT NULL DEFAULT'男' COMMENT'性别',`birthday`DATETIME NOT NULL COMMENT'出生日期'PRIMARY KEY(`id`))ENGINE=INNODB DEFAULT CHARSET=utf8
格式
use `数据库名`create table [if not exists]`表名`(`字段`[列类型][属性][索引][注释]`字段`[列类型][属性][索引][注释]......`字段`[列类型][属性][索引][注释]primary key(`字段`)--设置为主键)[表类型][字符集][注释]SHOW CREATE DATABASE school --查看创建数据库的语句SHOW CREATE TABLE students --查看创建表的语句DESC students(等价于describe students) --查看表的结构
两种引擎INNODB与MYISAM
MyISAM是MySQL的默认数据库引擎(5.5版之前),由早期的ISAM(Indexed Sequential Access Method:有索引的顺序访问方法)所改良。虽然性能极佳,但却有一个缺点:不支持事务处理(transaction)。不过,在这几年的发展下,MySQL也导入了InnoDB(另一种数据库引擎),以强化参考完整性与并发违规处理机制,后来就逐渐取代MyISAM。
| MyISAM | InnoDB | |
|---|---|---|
| 构成上的区别: | 每个MyISAM在磁盘上存储成三个文件。第一个文件的名字以表的名字开始,扩展名指出文件类型。 .frm文件存储表定义。 数据文件的扩展名为.MYD (MYData)。 索引文件的扩展名是.MYI (MYIndex)。 | 基于磁盘的资源是InnoDB表空间数据文件和它的日志文件,InnoDB 表的大小只受限于操作系统文件的大小,一般为 2GB |
| 事务处理上方面: | MyISAM类型的表强调的是性能,其执行数度比InnoDB类型更快,但是不提供事务支持 | InnoDB提供事务支持事务,外部键(foreign key)等高级数据库功能 |
| SELECT UPDATE,INSERT,Delete操作 | 如果执行大量的SELECT,MyISAM是更好的选择 | 1.如果你的数据执行大量的INSERT或UPDATE,出于性能方面的考虑,应该使用InnoDB表 2.DELETE FROM table时,InnoDB不会重新建立表,而是一行一行的删除。 3.LOAD TABLE FROM MASTER操作对InnoDB是不起作用的,解决方法是首先把InnoDB表改成MyISAM表,导入数据后再改成InnoDB表,但是对于使用的额外的InnoDB特性(例如外键)的表不适用 |
| 对AUTO_INCREMENT的操作 | 每表一个AUTO_INCREMEN列的内部处理。 MyISAM为INSERT和UPDATE操作自动更新这一列。这使得AUTO_INCREMENT列更快(至少10%)。在序列顶的值被删除之后就不能再利用。(当AUTO_INCREMENT列被定义为多列索引的最后一列,可以出现重使用从序列顶部删除的值的情况)。 AUTO_INCREMENT值可用ALTER TABLE或myisamch来重置 对于AUTO_INCREMENT类型的字段,InnoDB中必须包含只有该字段的索引,但是在MyISAM表中,可以和其他字段一起建立联合索引 更好和更快的auto_increment处理 | 如果你为一个表指定AUTO_INCREMENT列,在数据词典里的InnoDB表句柄包含一个名为自动增长计数器的计数器,它被用在为该列赋新值。 自动增长计数器仅被存储在主内存中,而不是存在磁盘上 关于该计算器的算法实现,请参考 AUTO_INCREMENT列在InnoDB里如何工作 |
| 表的具体行数 | select count() from table,MyISAM只要简单的读出保存好的行数,注意的是,当count()语句包含 where条件时,两种表的操作是一样的 | InnoDB 中不保存表的具体行数,也就是说,执行select count(*) from table时,InnoDB要扫描一遍整个表来计算有多少行 |
| 锁 | 表锁 | 提供行锁(locking on row level),提供与 Oracle 类型一致的不加锁读取(non-locking read in SELECTs),另外,InnoDB表的行锁也不是绝对的,如果在执行一个SQL语句时MySQL不能确定要扫描的范围,InnoDB表同样会锁全表, 例如update table set num=1 where name like “%aaa%” |
INNODB与MYISAM选择:
- 一、InnoDB支持事务,MyISAM不支持,这一点是非常之重要。事务是一种高级的处理方式,如在一些列增删改中只要哪个出错还可以回滚还原,而MyISAM就不可以了。
- 二、MyISAM适合查询以及插入为主的应用,InnoDB适合频繁修改以及涉及到安全性较高的应用
- 三、InnoDB支持外键,MyISAM不支持
- 四、MyISAM是默认引擎,InnoDB需要指定
- 五、InnoDB不支持FULLTEXT类型的索引
- 六、InnoDB中不保存表的行数,如select count() from table时,InnoDB需要扫描一遍整个表来计算有多少行,但是MyISAM只要简单的读出保存好的行数即可。注意的是,当count()语句包含where条件时MyISAM也需要扫描整个表
- 七、对于自增长的字段,InnoDB中必须包含只有该字段的索引,但是在MyISAM表中可以和其他字段一起建立联合索引
- 八、清空整个表时,InnoDB是一行一行的删除,效率非常慢。MyISAM则会重建表
- 九、InnoDB支持行锁(某些情况下还是锁整表,如 update table set a=1 where user like ‘%lee%’
表以及字段的修改删除
/*修改表名*/ALTER TABLE students RENAME AS studentALTER TABLE students2 RENAME AS student2/*修改表的属性*/ALTER TABLE `school`.`student`ENGINE=INNODB AUTO_INCREMENT=1 COMMENT='第一张学生表' ROW_FORMAT=DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci/*添加字段*/ALTER TABLE student ADD height FLOAT(8)NOT NULL COMMENT'身高'/*修改表的字段(1.change可以修改字段名)*/ALTER TABLE `school`.`student` CHANGE `age` `agee` FLOAT NOT NULL COMMENT '年龄'/*修改表的字段(2.modify不可以修改字段名,只是修改约束)*/ALTER TABLE `school`.`student` MODIFY `age` INT(100)/*删除字段*/ALTER TABLE `school`.`student` DROP `height`/*删除表*/DROP TABLE IF EXISTS `student`
所有的创建与删除尽量加上判断,以免出错
3.mySQL数据管理
DML语言
- insert
- update
- delete
添加insert
/*插入语句insert*/-- insert into 表名([字段1,字段2,字段3...])value('值1','值2','值3'...)INSERT INTO `student`(`name`,`age`,`password`)VALUES('肖云飞','18','123456789')/*如果不写表的字段就会默认一对一匹配*/INSERT INTO `student` VALUES('10','王五','30','123456789','男','2000-1-1')/*可以一次插入多个值*/INSERT INTO `student`(`name`,`age`,`password`)VALUES('张三','20','123456789'),('李四','22','123456789')
修改update
/*修改语句update*/-- update 表名 set 字段=值 where[判断条件]UPDATE `student` SET `age`='18' WHERE `id`='1'UPDATE `student` SET `id`='3' WHERE `id`='6'/*可以同时修改多个值(用“,"隔开)*/UPDATE `student` SET `id`=4,`age`='25' WHERE `id`='8'/*where后面实际上就是一个判断,可以用基本的逻辑运算符=,!=(<>),<,>,<=,>=此外可以用 between...and...(如where `id` BETWEEN 1 AND 3)控制到一个闭区间内and使多个条件同时成立(where `name`='肖云飞' and `sex`='男')or使范围变大 (where `id`='1' or `id`='2')*/UPDATE `student` SET `name`='肖云飞' WHERE `id` BETWEEN 1 AND 2UPDATE `student` SET `name`='张三' where`id`='2' AND `age`='20'
删除delete/truncate
/*删除语句delete*//*删除表项*/-- delete from 表名[where条件]DELETE FROM `school`.`student` WHERE `id`='10'DELETE FROM `teacher` -- 实现删除所有表项,但不会影响自增(删除前主键自增到10,删除后再添加一项该项的主键值为11)/*删除语句truncate*/-- truncate table 表名(或直接truncate 表名)TRUNCATE `teacher` -- 实现删除所有表项,同时自增归0(删除后再添加一项主键值一定是1)
相同点:两者都可以删除所有表项,但都不可以改变表的结构(要删除字段或表要用drop)
不同点:
- truncate会重新设置自增列,计数器归0
- truncate不会影响事务
4.DQL(数据库查询语言)
select完整语法
select [all | DISTINCT]{* | table.* | table.filed1[as 别名1] [,table.filed2 [as 别名2] ] [......]}from table_name [as 表别名1][inner | left | right join table_name2 [as 表别名2] ][on ......] -- 与join一同使用表的连接条件[group by......] -- 指定按照那几个字段类分组[having ......] -- 过滤分组的记录必须满足的次要条件[order by ......] [asc | desc] -- 排序[limit 起始位置,查询数据数]
1.简单操作:
/*查询语句select*/-- select 字段 from 表名/*查询全部信息*/SELECT * FROM `student`/*查询指定字段*/SELECT `id`,`name` FROM `student`/*可以为查询的字段起一个别名*/SELECT `id` AS 学生编号,`name` AS 学生姓名 FROM `student`/*可以为查询的表其一个别名*/SELECT `id` AS 学生编号,`name` AS 学生姓名 FROM `student` AS 学生表/*简单函数的调用(concat)*/SELECT CONCAT('学生姓名:',`name`) AS 姓名介绍 FROM `student`-- select 字段1 [as 别名1],字段2 [as 别名2]... from 表名 [as 别名](添加别名时as甚至可以省略)
2.去重(dintinct):
INSERT INTO `teacher`(`name`) VALUES ('A'),('A'),('A'),('B'),('B'),('B'),('B')SELECT `name` FROM `teacher` -- 有很多重复的数据SELECT DISTINCT `name` FROM `teacher` -- 去掉重复的数据INSERT INTO `student`(`name`,`age`,`password`)VALUES('肖云飞','20','123456789'),('肖云飞','30','123456789'),('肖云飞','40','123456789')SELECT DISTINCT `name` FROM `student`/*但是,当同时查询多个字段时,只有两个表项的查询字段都相同时才会去重*/SELECT DISTINCT `name` AS 姓名 ,`age` AS 年龄 FROM `student`
3.数据库的列(表达式)
SELECT VERSION()-- 函数SELECT 100+100 AS 计算结果 -- 表达式SELECT @@auto_increment_increment -- 查询自增的步长(变量)SELECT `name` AS 学生姓名 , `age` AS 今年年龄 , `age`+1 AS 明年年龄 FROM `student`
4.where简单用法
/*查询90-100的分数(where)*/SELECT `studentno` AS 学生编号,`studentresult` AS 考试成绩 FROM `result`WHERE studentresult >=90 AND studentresult <= 100;/*等价写法*/SELECT `studentno` AS 学生编号,`studentresult` AS 考试成绩 FROM `result`WHERE studentresult BETWEEN 90 AND 100;/*查询编号不是1001的学生的成绩(where)*/SELECT `studentno` AS 学生编号,`studentresult` AS 考试成绩 FROM `result`WHERE studentno <> 1001;/*等价写法*/SELECT `studentno` AS 学生编号,`studentresult` AS 考试成绩 FROM `result`WHERE NOT studentno = 1001;
5.模糊查询
| 运算符 | 语法 | 描述 |
|---|---|---|
| is NULL | a is NULL | 如果操作符为NULL则为真 |
| is NOT NULL | a is NOT NULL | 如果操作符不为NULL则为真 |
| BETWEEN | a BETWEEN … ADN … | 若a在b和c之间则为真 |
| LIKE | a LIKE b | 若a与b匹配,则为真 |
| IN | a IN (a1,a2,a3….) | 若a为a1,a2,a3其中任意一个值则为真 |
USE `school2`/*匹配LIKE*/-- SELECT 表达式 from 表名 where .../*用_一个下划线表示之后只有一个字符*/SELECT * FROM `student` WHERE `studentname` LIKE '张_'SELECT * FROM `subject` WHERE `subjectname` LIKE '____1'/*用_下划线表示之后有任意多字符*/SELECT * FROM `student` WHERE `studentname` LIKE '张%'SELECT * FROM `subject` WHERE `subjectname` LIKE '%1'/*范围IN*/SELECT * FROM `subject` WHERE `classhour` IN (100,110,120)
6.连表查询
NUION
/*UNION查询*/-- 当需要查询多张表的相同字段时使用(只用union的话会自动去重)SELECT `name` FROM `school`.studentUNIONSELECT `name` FROM `school`.teacher;-- 加上ALL就会查询所有的内容SELECT `name` FROM `school`.studentUNION ALLSELECT `name` FROM `school`.teacher;/*注意如果要为查询字段起别名,只会保留第一个*/SELECT `name` AS 学生姓名 FROM `school`.studentUNION ALLSELECT `name` AS 教师姓名 FROM `school`.teacher;-- 此外union也可以用于查询一张表SELECT `subjectno` ,`subjectname` FROM `school2`.`subject` WHERE `gradeid` IN ('1','2')UNION ALLSELECT `subjectno` ,`subjectname` FROM `school2`.`subject` WHERE `gradeid` IN ('3');
join
/*JOIN*/-- 查询学号为1000的学生的所有信息与考试结果/*内连接inner join*/-- 内连接比较苛刻,必须要两张表的指定属性相同的行的数据才会被显示出来SELECT * FROM `student`INNER JOIN `result`ON `student`.studentno = `result`.studentnoWHERE `student`.studentno = '1000';/*外连接1.lefe join*/-- 左表(第一个表)连接会把两张表的数据全部显示出来,其中左表完全显示,右表只有与左表匹配的行才会显示数据,否则为NULLSELECT * FROM `student`LEFT JOIN `result`ON `student`.studentno = `result`.studentnoWHERE `student`.studentno = '1000';/*外连接2.right join*/-- 右表(第二个表)连接会把两张表的数据全部显示出来,其中右表完全显示,左表只有与右表匹配的行才会显示数据,否则为NULLSELECT * FROM `student`RIGHT JOIN `result`ON `student`.studentno = `result`.studentnoWHERE `student`.studentno = '1000';/*交叉连接3.cross join*/-- 实际上就是把左表与右表的数据交叉组合,如果左表有6行数据,右表有7行数据,那交叉组合后就有42行数据SELECT * FROM `student`CROSS JOIN `result`
7.自连接
/*创建一张表,pid是学科大的分类序号(父id),categoryid是学科的分类序号,现在要把父类学科与子类对应,但只有一张表,所以就要用 到自连接,实际上就是把一张表在一个SELECT语句里面通过起两个别名使用两次*/CREATE TABLE `school2`.`category`(`categoryid` INT(3) NOT NULL COMMENT 'id',`pid` INT(3) NOT NULL COMMENT '父id 没有父则为1',`categoryname` VARCHAR(10) NOT NULL COMMENT '种类名字',PRIMARY KEY (`categoryid`)) ENGINE=INNODB CHARSET=utf8 COLLATE=utf8_general_ci;INSERT INTO `school2`.`category` (`categoryid`, `pid`, `categoryname`) VALUES ('2', '1', '信息技术');INSERT INTO `school2`.`CATEGOrY` (`categoryid`, `pid`, `categoryname`) VALUES ('3', '1', '软件开发');INSERT INTO `school2`.`category` (`categoryid`, `PId`, `categoryname`) VALUES ('5', '1', '美术设计');INSERT INTO `School2`.`category` (`categoryid`, `pid`, `categorynamE`) VALUES ('4', '3', '数据库');INSERT INTO `school2`.`category` (`CATEgoryid`, `pid`, `categoryname`) VALUES ('8', '2', '办公信息');INSERT INTO `school2`.`category` (`categoryid`, `pid`, `CAtegoryname`) VALUES ('6', '3', 'web开发');INSERT INTO `SCHool2`.`category` (`categoryid`, `pid`, `categoryname`) VALUES ('7', '5', 'ps技术');/*自连接(实际上就是把一张表拆为两张表)*/SELECT father.`categoryname` AS '父种类',child.`categoryname` AS '子种类' FROM`school2`.category AS father ,`school2`.category AS childWHERE father.categoryid = child.pid;
9.排序order by
/*排序order by (asc升序,desc降序)*/-- 下面语句表示优先按照studentresult降序排序,如果studentresult相同,再按照subjectno降序排列SELECT * FROM `school2`.resultORDER BY `studentresult` DESC , `subjectno` DESC;
10.分页
/*分页limit(最后一个select子句)*/-- limit 页起始值,页面大小SELECT student.identitycard,student.studentname,grade.gradename,result.studentresultFROM studentINNER JOIN grade ON student.gradeid = grade.gradeidINNER JOIN result ON student.studentno = result.studentnoORDER BY student.studentno DESCLIMIT 0,5-- limit 0,5 第一页,5条数据-- limit 5,5 第二页,5条数据-- limit 10,5 第三页,5条数据-- limit (n-1)*5,5 第n页,5条数据
11.子查询
子查询实际上就是在where子句中使用一个select
/*查询数据库结构-1的考试结果(学号,科目编号,成绩)*/SELECT `student`.`studentNo`,`subject`.`gradeid`,`subjectname`,`subjectno`FROM `student`INNER JOIN `subject`ON `student`.gradeid = `subject`.gradeidWHERE subject.subjectname = '数据库结构-2'ORDER BY student.studentno DESC;/*还是上一道题,用子查询*/SELECT `result`.`studentno` , student.studentname, `subject`.subjectname , `result`.`subjectno` , result.`studentresult`FROM `result`INNER JOIN `student`INNER JOIN `subject`WHERE result.studentNo =(SELECT student.studentno FROM student WHERE student.gradeid = 2)
5.MySQL函数
1.聚合函数
聚合函数是平时比较常用的一类函数,这里列举如下:
COUNT(col) 统计查询结果的行数MIN(col) 查询指定列的最小值MAX(col) 查询指定列的最大值SUM(col) 求和,返回指定列的总和AVG(col) 求平均值,返回指定列数据的平均值
SELECT COUNT(studentresult),MAX(studentresult),MIN(studentresult),SUM(studentresult),AVG(studentresult) FROM `result`;
2.数值型函数
数值型函数主要是对数值型数据进行处理,得到我们想要的结果,常用的几个列举如下,具体使用方法大家可以试试看。
ABS(x) 返回x的绝对值BIN(x) 返回x的二进制CEILING(x) 返回大于x的最小整数值EXP(x) 返回值e(自然对数的底)的x次方FLOOR(x) 返回小于x的最大整数值GREATEST(x1,x2,...,xn) 返回集合中最大的值LEAST(x1,x2,...,xn) 返回集合中最小的值LN(x) 返回x的自然对数LOG(x,y) 返回x的以y为底的对数MOD(x,y) 返回x/y的模(余数)PI() 返回pi的值(圆周率)RAND() 返回0到1内的随机值,可以通过提供一个参数(种子)使RAND()随机数生成器生成一个指定的值ROUND(x,y) 返回参数x的四舍五入的有y位小数的值TRUNCATE(x,y) 返回数字x截短为y位小数的结果
3.字符串函数
字符串函数可以对字符串类型数据进行处理,在程序应用中用处还是比较大的,同样这里列举几个常用的如下:
LENGTH(s) 计算字符串长度函数,返回字符串的字节长度CONCAT(s1,s2...,sn) 合并字符串函数,返回结果为连接参数产生的字符串,参数可以是一个或多个INSERT(str,x,y,instr) 将字符串str从第x位置开始,y个字符长的子串替换为字符串instr,返回结果LOWER(str) 将字符串中的字母转换为小写UPPER(str) 将字符串中的字母转换为大写LEFT(str,x) 返回字符串str中最左边的x个字符RIGHT(str,x) 返回字符串str中最右边的x个字符TRIM(str) 删除字符串左右两侧的空格REPLACE 字符串替换函数,返回替换后的新字符串SUBSTRING 截取字符串,返回从指定位置开始的指定长度的字符换REVERSE(str) 返回颠倒字符串str的结果
4.日期和时间函数
CURDATE 和 CURRENT_DATE 两个函数作用相同,返回当前系统的日期值CURTIME 和 CURRENT_TIME 两个函数作用相同,返回当前系统的时间值NOW 和 SYSDATE 两个函数作用相同,返回当前系统的日期和时间值UNIX_TIMESTAMP 获取UNIX时间戳函数,返回一个以 UNIX 时间戳为基础的无符号整数FROMUNIXTIME 将 UNIX 时间戳转换为时间格式,与UNIXTIMESTAMP互为反函数MONTH 获取指定日期中的月份MONTHNAME 获取指定日期中的月份英文名称DAYNAME 获取指定曰期对应的星期几的英文名称DAYOFWEEK 获取指定日期对应的一周的索引位置值WEEK 获取指定日期是一年中的第几周,返回值的范围是否为 0〜52 或 1〜53DAYOFYEAR 获取指定曰期是一年中的第几天,返回值范围是1~366DAYOFMONTH 获取指定日期是一个月中是第几天,返回值范围是1~31YEAR 获取年份,返回值范围是 1970〜2069TIMETOSEC 将时间参数转换为秒数SECTOTIME 将秒数转换为时间,与TIMETOSEC 互为反函数DATE_ADD 和 ADDDATE 两个函数功能相同,都是向日期添加指定的时间间隔DATE_SUB 和 SUBDATE 两个函数功能相同,都是向日期减去指定的时间间隔ADDTIME 时间加法运算,在原始时间上添加指定的时间SUBTIME 时间减法运算,在原始时间上减去指定的时间DATEDIFF 获取两个日期之间间隔,返回参数 1 减去参数 2 的值DATE_FORMAT 格式化指定的日期,根据参数返回指定格式的值WEEKDAY 获取指定日期在一周内的对应的工作日索引
5.流程控制函数
流程控制类函数可以进行条件操作,用来实现SQL的条件逻辑,允许开发者将一些应用程序业务逻辑转换到数据库后台,列举如下:
IF(test,t,f) 如果test是真,返回t;否则返回fIFNULL(arg1,arg2) 如果arg1不是空,返回arg1,否则返回arg2NULLIF(arg1,arg2) 如果arg1=arg2返回NULL;否则返回arg1CASE WHEN[test1] THEN [result1]...ELSE [default] END 如果testN是真,则返回resultN,否则返回defaultCASE [test] WHEN[val1] THEN [result]...ELSE [default]END 如果test和valN相等,则返回resultN,否则返回default
6.加密函数
加密函数主要用于对字符串进行加密,常用的几个列举如下:
ENCRYPT(str,salt) 使用UNIXcrypt()函数,用关键词salt(一个可以惟一确定口令的字符串,就像钥匙一样)加密字符串strENCODE(str,key) 使用key作为密钥加密字符串str,调用ENCODE()的结果是一个二进制字符串,它以BLOB类型存储MD5() 计算字符串str的MD5校验和PASSWORD(str) 返回字符串str的加密版本,这个加密过程是不可逆转的,和UNIX密码加密过程使用不同的算法。SHA() 计算字符串str的安全散列算法(SHA)校验和
13.分组
/*分组group by*/SELECT subjectno,COUNT(result.studentresult) FROM result GROUP BY subjectno/*having与group连用添加约束条件*/SELECT subject.gradeid,COUNT(subject.subjectname) FROM `subject` GROUP BY `subject`.gradeidHAVING COUNT(subject.subjectname) > 4
select学习程序清单
CREATE DATABASE IF NOT EXISTS `school2`;-- 创建一个school数据库USE `school2`;-- 创建学生表DROP TABLE IF EXISTS `student`;CREATE TABLE `student`(`studentno` INT(4) NOT NULL COMMENT '学号',`loginpwd` VARCHAR(20) DEFAULT NULL,`studentname` VARCHAR(20) DEFAULT NULL COMMENT '学生姓名',`sex` TINYINT(1) DEFAULT NULL COMMENT '性别,0或1',`gradeid` INT(11) DEFAULT NULL COMMENT '年级编号',`phone` VARCHAR(50) NOT NULL COMMENT '联系电话,允许为空',`address` VARCHAR(255) NOT NULL COMMENT '地址,允许为空',`borndate` DATETIME DEFAULT NULL COMMENT '出生时间',`email` VARCHAR (50) NOT NULL COMMENT '邮箱账号允许为空',`identitycard` VARCHAR(18) DEFAULT NULL COMMENT '身份证号',PRIMARY KEY (`studentno`),UNIQUE KEY `identitycard`(`identitycard`),KEY `email` (`email`))ENGINE=MYISAM DEFAULT CHARSET=utf8;-- 创建年级表DROP TABLE IF EXISTS `grade`;CREATE TABLE `grade`(`gradeid` INT(11) NOT NULL AUTO_INCREMENT COMMENT '年级编号',`gradename` VARCHAR(50) NOT NULL COMMENT '年级名称',PRIMARY KEY (`gradeid`)) ENGINE=INNODB AUTO_INCREMENT = 6 DEFAULT CHARSET = utf8;-- 创建科目表DROP TABLE IF EXISTS `subject`;CREATE TABLE `subject`(`subjectno`INT(11) NOT NULL AUTO_INCREMENT COMMENT '课程编号',`subjectname` VARCHAR(50) DEFAULT NULL COMMENT '课程名称',`classhour` INT(4) DEFAULT NULL COMMENT '学时',`gradeid` INT(4) DEFAULT NULL COMMENT '年级编号',PRIMARY KEY (`subjectno`))ENGINE = INNODB AUTO_INCREMENT = 19 DEFAULT CHARSET = utf8;-- 创建成绩表DROP TABLE IF EXISTS `result`;CREATE TABLE `result`(`studentno` INT(4) NOT NULL COMMENT '学号',`subjectno` INT(4) NOT NULL COMMENT '课程编号',`examdate` DATETIME NOT NULL COMMENT '考试日期',`studentresult` INT (4) NOT NULL COMMENT '考试成绩',KEY `subjectno` (`subjectno`))ENGINE = INNODB DEFAULT CHARSET = utf8;-- 插入学生数据INSERT INTO `student` (`studentno`,`loginpwd`,`studentname`,`sex`,`gradeid`,`phone`,`address`,`borndate`,`email`,`identitycard`)VALUES(1000,'123456','张伟',0,2,'13800001234','北京朝阳','1980-1-1','text123@qq.com','123456198001011234'),(1001,'123456','赵强',1,3,'13800002222','广东深圳','1990-1-1','text111@qq.com','123456199001011233');-- 插入成绩数据INSERT INTO `result`(`studentno`,`subjectno`,`examdate`,`studentresult`)VALUES(1000,1,'2013-11-11 16:00:00',85),(1000,2,'2013-11-12 16:00:00',70),(1000,3,'2013-11-11 09:00:00',68),(1000,4,'2013-11-13 16:00:00',98),(1000,5,'2013-11-14 16:00:00',58),(1001,1,'2013-11-11 16:00:00',85),(1001,2,'2013-11-12 16:00:00',70),(1001,3,'2013-11-11 09:00:00',68),(1001,4,'2013-11-13 16:00:00',98),(1001,5,'2013-11-14 16:00:00',58);-- 插入年级数据INSERT INTO `grade` (`gradeid`,`gradename`) VALUES(1,'大一'),(2,'大二'),(3,'大三'),(4,'大四'),(5,'预科班');-- 插入科目数据INSERT INTO `subject`(`subjectno`,`subjectname`,`classhour`,`gradeid`)VALUES(1,'高等数学-1',110,1),(2,'高等数学-2',110,2),(3,'高等数学-3',100,3),(4,'高等数学-4',130,4),(5,'C语言-1',110,1),(6,'C语言-2',110,2),(7,'C语言-3',100,3),(8,'C语言-4',130,4),(9,'Java程序设计-1',110,1),(10,'Java程序设计-2',110,2),(11,'Java程序设计-3',100,3),(12,'Java程序设计-4',130,4),(13,'数据库结构-1',110,1),(14,'数据库结构-2',110,2),(15,'数据库结构-3',100,3),(16,'数据库结构-4',130,4),(17,'C#基础',130,1);/*查询90-100的分数(where)*/SELECT `studentno` AS 学生编号,`studentresult` AS 考试成绩 FROM `result`WHERE studentresult >=90 AND studentresult <= 100;/*等价写法*/SELECT `studentno` AS 学生编号,`studentresult` AS 考试成绩 FROM `result`WHERE studentresult BETWEEN 90 AND 100;/*查询编号不是1001的学生的成绩(where)*/SELECT `studentno` AS 学生编号,`studentresult` AS 考试成绩 FROM `result`WHERE studentno <> 1001;/*等价写法*/SELECT `studentno` AS 学生编号,`studentresult` AS 考试成绩 FROM `result`WHERE NOT studentno = 1001;USE `school2`/*匹配LIKE*/-- SELECT 表达式 from 表名 where .../*用_一个下划线表示之后只有一个字符*/SELECT * FROM `student` WHERE `studentname` LIKE '张_'SELECT * FROM `subject` WHERE `subjectname` LIKE '____1'/*用_下划线表示之后有任意多字符*/SELECT * FROM `student` WHERE `studentname` LIKE '张%'SELECT * FROM `subject` WHERE `subjectname` LIKE '%1'/*范围IN*/SELECT * FROM `subject` WHERE `classhour` IN (100,110,120)/*UNION查询*/-- 当需要查询多张表的相同字段时使用(只用union的话会自动去重)SELECT `name` FROM `school`.studentUNIONSELECT `name` FROM `school`.teacher;-- 加上ALL就会查询所有的内容SELECT `name` FROM `school`.studentUNION ALLSELECT `name` FROM `school`.teacher;/*注意如果要为查询字段起别名,只会保留第一个*/SELECT `name` AS 学生姓名 FROM `school`.studentUNION ALLSELECT `name` AS 教师姓名 FROM `school`.teacher;-- 此外union也可以用于查询一张表SELECT `subjectno` ,`subjectname` FROM `school2`.`subject` WHERE `gradeid` IN ('1','2')UNION ALLSELECT `subjectno` ,`subjectname` FROM `school2`.`subject` WHERE `gradeid` IN ('3');/*JOIN*/-- 查询学号为1000的学生的所有信息与考试结果/*内连接inner join*/-- 内连接比较苛刻,必须要两张表的指定属性相同的行的数据才会被显示出来SELECT * FROM `student`INNER JOIN `result`ON `student`.studentno = `result`.studentnoWHERE `student`.studentno = '1000';/*外连接1.lefe join*/-- 左表(第一个表)连接会把两张表的数据全部显示出来,其中左表完全显示,右表只有与左表匹配的行才会显示数据,否则为NULLSELECT * FROM `student`LEFT JOIN `result`ON `student`.studentno = `result`.studentnoWHERE `student`.studentno = '1000';/*外连接2.right join*/-- 右表(第二个表)连接会把两张表的数据全部显示出来,其中右表完全显示,左表只有与右表匹配的行才会显示数据,否则为NULLSELECT * FROM `student`RIGHT JOIN `result`ON `student`.studentno = `result`.studentnoWHERE `student`.studentno = '1000';/*交叉连接3.cross join*/-- 实际上就是把左表与右表的数据交叉组合,如果左表有6行数据,右表有7行数据,那交叉组合后就有42行数据SELECT * FROM `student`CROSS JOIN `result`CREATE TABLE `school2`.`category`(`categoryid` INT(3) NOT NULL COMMENT 'id',`pid` INT(3) NOT NULL COMMENT '父id 没有父则为1',`categoryname` VARCHAR(10) NOT NULL COMMENT '种类名字',PRIMARY KEY (`categoryid`)) ENGINE=INNODB CHARSET=utf8 COLLATE=utf8_general_ci;INSERT INTO `school2`.`category` (`categoryid`, `pid`, `categoryname`) VALUES ('2', '1', '信息技术');INSERT INTO `school2`.`CATEGOrY` (`categoryid`, `pid`, `categoryname`) VALUES ('3', '1', '软件开发');INSERT INTO `school2`.`category` (`categoryid`, `PId`, `categoryname`) VALUES ('5', '1', '美术设计');INSERT INTO `School2`.`category` (`categoryid`, `pid`, `categorynamE`) VALUES ('4', '3', '数据库');INSERT INTO `school2`.`category` (`CATEgoryid`, `pid`, `categoryname`) VALUES ('8', '2', '办公信息');INSERT INTO `school2`.`category` (`categoryid`, `pid`, `CAtegoryname`) VALUES ('6', '3', 'web开发');INSERT INTO `SCHool2`.`category` (`categoryid`, `pid`, `categoryname`) VALUES ('7', '5', 'ps技术');/*自连接(实际上就是把一张表拆为两张表)*/SELECT father.`categoryname` AS '父种类',child.`categoryname` AS '子种类' FROM`school2`.category AS father ,`school2`.category AS childWHERE father.categoryid = child.pid;-- 查询学员所属年级(学号,姓名,年级)SELECT `student`.`identitycard`,`student`.`studentname`,`grade`.`gradename`FROM `student` INNER JOIN `grade`ON `student`.gradeid = `grade`.gradeid;/*排序order by (asc升序,desc降序)*/-- 下面语句表示优先按照studentresult降序排序,如果studentresult相同,再按照subjectno降序排列SELECT * FROM `school2`.resultORDER BY `studentresult` DESC , `subjectno` DESC;/*分页limit(最后一个select子句)*/-- limit 页起始值,页面大小SELECT student.identitycard,student.studentname,grade.gradename,result.studentresultFROM studentINNER JOIN grade ON student.gradeid = grade.gradeidINNER JOIN result ON student.studentno = result.studentnoORDER BY student.studentno DESCLIMIT 0,5-- limit 0,5 第一页,5条数据-- limit 5,5 第二页,5条数据-- limit 10,5 第三页,5条数据-- limit (n-1)*5,5 第n页,5条数据/*查询数据库结构-1的考试结果(学号,科目编号,成绩)*/SELECT `student`.`studentNo`,`subject`.`gradeid`,`subjectname`,`subjectno`FROM `student`INNER JOIN `subject`ON `student`.gradeid = `subject`.gradeidWHERE subject.subjectname = '数据库结构-2'ORDER BY student.studentno DESC;/*还是上一道题,用子查询*/SELECT `result`.`studentno` , student.studentname, `subject`.subjectname , `result`.`subjectno` , result.`studentresult`FROM `result`INNER JOIN `student`INNER JOIN `subject`WHERE result.studentNo =(SELECT student.studentno FROM student WHERE student.gradeid = 2)SELECT COUNT(studentresult),MAX(studentresult),MIN(studentresult),SUM(studentresult),AVG(studentresult) FROM `result`;/*分组group by*/SELECT subjectno,COUNT(result.studentresult) FROM result GROUP BY subjectno/*having与group连用添加约束条件*/SELECT subject.gradeid,COUNT(subject.subjectname) FROM `subject` GROUP BY `subject`.gradeidHAVING COUNT(subject.subjectname) > 4
6.事务
本质就是把要执行的sql语句放在一个批次执行,要不都成功,要不都不成功
重点:事务四大原则(ACID:原子性atomicity,一致性consistence,隔离性isolation,持久性durability)
-- 一、原子性(atomicity)一个事务要么全部提交成功,要么全部失败回滚,不能只执行其中的一部分操作,这就是事务的原子性-- 二、一致性(consistency)事务的执行不能破坏数据库数据的完整性和一致性,一个事务在执行之前和执行之后,数据库都必须处于一致性状态。如果数据库系统在运行过程中发生故障,有些事务尚未完成就被迫中断,这些未完成的事务对数据库所作的修改有一部分已写入物理数据库,这是数据库就处于一种不正确的状态,也就是不一致的状态-- 三、隔离性(isolation)事务的隔离性是指在并发环境中,并发的事务时相互隔离的,一个事务的执行不能不被其他事务干扰。不同的事务并发操作相同的数据时,每个事务都有各自完成的数据空间,即一个事务内部的操作及使用的数据对其他并发事务时隔离的,并发执行的各个事务之间不能相互干扰。在标准SQL规范中,定义了4个事务隔离级别,不同的隔离级别对事务的处理不同,分别是:未授权读取,授权读取,可重复读取和串行化1、读未提交(Read Uncommited),该隔离级别允许脏读取,其隔离级别最低;比如事务A和事务B同时进行,事务A在整个执行阶段,会将某数据的值从1开始一直加到10,然后进行事务提交,此时,事务B能够看到这个数据项在事务A操作过程中的所有中间值(如1变成2,2变成3等),而对这一系列的中间值的读取就是未授权读取2、授权读取也称为已提交读(Read Commited),授权读取只允许获取已经提交的数据。比如事务A和事务B同时进行,事务A进行+1操作,此时,事务B无法看到这个数据项在事务A操作过程中的所有中间值,只能看到最终的10。另外,如果说有一个事务C,和事务A进行非常类似的操作,只是事务C是将数据项从10加到20,此时事务B也同样可以读取到20,即授权读取允许不可重复读取。3、可重复读(Repeatable Read)就是保证在事务处理过程中,多次读取同一个数据时,其值都和事务开始时刻是一致的,因此该事务级别禁止不可重复读取和脏读取,但是有可能出现幻影数据。所谓幻影数据,就是指同样的事务操作,在前后两个时间段内执行对同一个数据项的读取,可能出现不一致的结果。在上面的例子中,可重复读取隔离级别能够保证事务B在第一次事务操作过程中,始终对数据项读取到1,但是在下一次事务操作中,即使事务B(注意,事务名字虽然相同,但是指的是另一个事务操作)采用同样的查询方式,就可能读取到10或20;4、串行化是最严格的事务隔离级别,它要求所有事务被串行执行,即事务只能一个接一个的进行处理,不能并发执行。-- 四、持久性(durability)一旦事务提交,那么它对数据库中的对应数据的状态的变更就会永久保存到数据库中。--即使发生系统崩溃或机器宕机等故障,只要数据库能够重新启动,那么一定能够将其恢复到事务成功结束的状态
CREATE DATABASE `shop` CHARACTER SET utf8 COLLATE utf8_general_ciUSE `shop`/*创建一张表模拟转账*/CREATE TABLE `person`(`id` INT(5) NOT NULL AUTO_INCREMENT ,`name`VARCHAR(10) NOT NULL,`money`DECIMAL(10,3) NOT NULL ,PRIMARY KEY(`id`))ENGINE = INNODB DEFAULT CHARSET = utf8/*插入三条数据*/INSERT INTO `person`(`name`,`money`)VALUES ('张三',10000.000),('李四',20000.000),('王五',30000.00)/*mysql默认开启自动提交(只要运行一条就会吧结果提交到数据库),而由于事务要求的ACID原则,必须关闭自动提交*/-- 有两种方法关闭自动提交 1.set autocommit = 0;-- 2.start transaction ; 开启事务默认关闭自动提交START TRANSACTION ; -- 开启事务(同时关闭自动提交)UPDATE `person` SET `money` = `money`-5000.000 WHERE `name` = '李四' -- 李四减少5000元UPDATE `persin` SET `money` = `money`+5000.000 WHERE `name` = '王五' -- 王五收到5000元UPDATE `person` SET `money` = `money`-3000.000 WHERE `name` = '王五' -- 王五减少3000元UPDATE `person` SET `money` = `money`+3000.000 WHERE `name` = '张三' -- 张三收到3000元/*刚刚运行完的sql语句由于关闭了自动提交,只是展示修改了数据,可以回滚*/COMMIT; -- 如果事务全部准确完成,就提交ROLLBACK; -- 如果事务有问题,就回滚,数据库回到提交之前的状态SET autocommit = 1; -- 事务提交完成后必须将自动提交改回去
7.索引
索引实际上就是帮助MySQL高效获取数据的数据结构
1.索引分类
- 主键索引(primary key):唯一的标识,只有一列可以用
- 唯一索引(unicon key):避免重复的列出现,多个列都可以 表示
- 常规索引(key | index):默认的
- 全文索引(fulltext):特定的数据库引擎下才有(比如myisam),可以快速定位数据
2.索引原则
- 索引不是越多越好
- 经常变动的数据不加索引
- 小数据量的表不需要索引
- 索引一般加在经常查询的字段上
8.权限管理与备份
权限管理实际上就是不同用户对数据库的操作权限
用户的信息实际上是存储在mysql这个数据库里的user表中的信息,所以添加,删除用户,修改用户权限都是对这张表的操作
1.创建用户
create user ‘{用户名}’@’{主机地址}’ identified with {身份验证插件类型} by ‘{密码}’;
CREATE USER IF NOT EXISTS 'employees'@'%'IDENTIFIED WITH mysql_native_password BY 'Study@2019'WITH MAX_QUERIES_PER_HOUR 500 MAX_UPDATES_PER_HOUR 100;用户名:employees可以从任意主机访问,也可以限制对IP范围的访问,例如10.148.%.%密码: Study@2019使用mysql_native_password身份验证插件,还可以指定任何可选的身份验证,例如sha256_password、LDAP或Kerberos用户可以在一小时内执行的最大查询数为500用户可以在一小时内执行的最大更新次数为100
二、授予和撤销用户的访问权限
grant {权限类型} on {databases}.{table} to ‘{用户名}’@’{主机地址}’;(赋予权限)
revoke{权限类型} on {databases}.{table} to’{用户名}’@’{主机地址}’;(删除权限)
三.修改用户密码
CREATE USER `xiaoyunfei` IDENTIFIED BY 'xiaoyunfei' -- 创建用户ALTER USER `xiaoyunfei`@`%` IDENTIFIED BY '123456' -- 修改密码
四.删除用户
DROP USER `xiaoyunfei`@`%` -- 删除用户
注意,哪怕是授予了所有权限的用户也没有ruut的权限高,因为只有root用户才有grant(授予)权限
五。查看用户权限
SHOW GRANTS FOR root@localhost -- 查看权限
六。数据库备份(导出)与导入
/*命令行执行*/mysqldump -hlocalhost -uroot -p123456 school student > D:/sql备份.sql/*mysqldump -h主机 -u用户 -p密码 数据库 表 > 文件位置 /文件名.sql*//*导入(必须先登录)*/source sql备份.sql
9.数据库设计三大范式
第一范式(1NF)
数据库的表的每一列的必须是不可再分的
第二范式(2NF)
在第一范式的基础上,数据库的表中的每一列都必须与主键相关,即每张表值描述一件事
第三范式(3NF)
在第二范式的基础上,数据库的表的每一列都必须与主键直接相关,不能是间接相关
但是,如果完全遵照范式,性能会降低(表会很多),
因此,要在优先考虑性能的基础上参考范式
10.IDBC
第一个程序
ackage com.xiao.JDBC;import java.sql.*;/*** JDBC学习1*/public class JDBCLearn1 {public static void main(String[] args) throws ClassNotFoundException, SQLException {//1.加载驱动Class.forName("com.mysql.cj.jdbc.Driver");//2.加载用户信息与URLString url="jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai";/**在高版本的mysql中需要指明是否要SSL连接serverTimeZone服务时区一般有UTC(Coordinated Universal Time世界协调时间),比北京时间早8个小时,就导致时间出现偏差,所以可以申明为Asia/Shanghai保证时间没有问题.*/String user = "root";String pwd = "123456";//3.连接成功后获取数据库对象(connection就代表数据库)Connection connection = DriverManager.getConnection(url,user,pwd);//4.创建一个用于执行sql指令的对象Statement statement = connection.createStatement();//5.执行sql的对象statement要执行的sql语句String sql = "select * from jdbcstudy.users";//6.执行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("email = "+resultSet.getObject("email"));System.out.println("birthday = "+resultSet.getObject("birthday"));System.out.println("=============================================================");}//7.释放连接resultSet.close();statement.close();connection.close();}}/*结果*/id = 1name = zhansanemail = zs@sina.combirthday = 1980-12-04=============================================================id = 2name = lisiemail = lisi@sina.combirthday = 1981-12-04=============================================================id = 3name = wangwuemail = wangwu@sina.combirthday = 1979-12-04=============================================================
将配置包装
首先需要一个配置文件(命名为db.properties)
/**这是文件中的配置信息*/driverName=com.mysql.cj.jdbc.Driverurl=jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghaiuser=rootpwd=123456
之后,需要一个工具类获取该文件中的资源(命名为JDBCLoader)
/*** 这是一个工具类JDBCLoader* loadConnection方法获取数据库对象connection* getStatement方法获取一个用于调用sql语句的对象* release方法释放资源*/import java.io.IOException;import java.io.InputStream;import java.sql.*;import java.util.Properties;public class JDBCLoad {private static final InputStream in = JDBCLoad.class.getClassLoader().getResourceAsStream("db.properties");private static final Properties properties = new Properties();/*获取数据库对象*/static Connection loadConnection(){Connection connection = null;try {properties.load(in);String driverName = properties.getProperty("driverName");String url = properties.getProperty("url");String user = properties.getProperty("user");String pwd = properties.getProperty("pwd");Class.forName(driverName);connection = DriverManager.getConnection(url,user,pwd);} catch (IOException e) {e.printStackTrace();System.out.println("获取配置失败");} catch (SQLException e) {e.printStackTrace();System.out.println("获取连接失败");} catch (ClassNotFoundException e) {e.printStackTrace();System.out.println("加载驱动失败");}return connection;}/*获取执行sql的对象*/static Statement getStatement(Connection connection){Statement statement = null;try {statement = connection.createStatement();} catch (SQLException e) {e.printStackTrace();System.out.println("获取失败");}return statement;}/*释放资源*/static void release(Connection connection,Statement statement,ResultSet resultSet){if (resultSet != null){try {resultSet.close();} catch (SQLException e) {e.printStackTrace();}}if (statement != null){try {statement.close();} catch (SQLException e) {e.printStackTrace();}}if (connection != null){try {connection.close();} catch (SQLException e) {e.printStackTrace();}}}/*释放资源(没有结果集)*/static void releaseWithoutResultSet(Connection connection,Statement statement){if (statement != null){try {statement.close();} catch (SQLException e) {e.printStackTrace();}}if (connection != null){try {connection.close();} catch (SQLException e) {e.printStackTrace();}}}}
接下来用工具类去完成上面的代码
package com.xiao.JDBC;import java.sql.Connection;import java.sql.ResultSet;import java.sql.SQLException;import java.sql.Statement;/*** 用工具类获取数据库*/public class JDBCLearn2 {public static void main(String[] args) throws SQLException {Connection connection = JDBCLoad.loadConnection();Statement statement = JDBCLoad.getStatement(connection);String sql = "select * from jdbcstudy.users";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("email = " + resultSet.getObject("email"));System.out.println("birthday = " + resultSet.getObject("birthday"));System.out.println("==============================================================");}JDBCLoad.release(connection,statement,resultSet);}}
sql注入
SQL注入即是指web应用程序对用户输入数据的合法性没有判断或过滤不严,攻击者可以在web应用程序中事先定义好的查询语句的结尾上添加额外的SQL语句,在管理员不知情的情况下实现非法操作,以此来实现欺骗数据库服务器执行非授权的任意查询,从而进一步得到相应的数据信息。
package com.xiao.JDBC;import java.sql.Connection;import java.sql.ResultSet;import java.sql.SQLException;import java.sql.Statement;/*sql注入*/public class Learn3 {public static void main(String[] args) {//login("zhansan","123456"); //理想的情况login("'or'1=1","123456"); //这样的查询或返回所有的信息login("'or'1=1","'or'1=1"); //甚至这样的查询或返回所有的信息}public static void login(String username,String password){Connection connection = JDBCLoad.loadConnection();Statement statement = JDBCLoad.getStatement(connection);String sql = "SELECT * FROM `jdbcstudy`.`users` WHERE `NAME`='"+username+"'" +"AND"+" `password`="+"'"+password+"'";/**通过字符串的拼接完成的sql语句*/String sql1 = "SELECT * FROM `jdbcstudy`.`users` WHERE `NAME`='zhansan' AND `password`='123456'";String sql2 = "SELECT * FROM `jdbcstudy`.`users` ";/**不通过字符串的拼接完成的sql语句*/try {ResultSet resultSet = statement.executeQuery(sql);while (resultSet.next()) {String name =(String) resultSet.getObject("NAME");String pwd = (String) resultSet.getObject("password");System.out.println(name);System.out.println(pwd);System.out.println("=================================================");}} catch (SQLException e) {e.printStackTrace();}finally {JDBCLoad.release(connection,statement,resultSet);}}
preparedStatement对象
这个对象的作用与Statement相同,只是可以一定程度上解决SQL注入,并且比Statement效率更高
package com.xiao.JDBC;import java.sql.Connection;import java.sql.PreparedStatement;import java.sql.SQLException;import java.util.Date;/*** prepareStatement对象测试*/public class Learn4 {public static void main(String[] args) {Connection connection = JDBCLoad.loadConnection();PreparedStatement preparedStatement = null;//区别,PreparedStatement是预处理,用?占位后手动赋值/*** 如果用Statement就只能这么写*/String oldSQL = "INSERT INTO `jdbcstudy`.users (`id`,`NAME`,`PASSWORD`,`email`,`birthday`) " +"VALUES ('4','zhaoliu',123456,'zhaoliu@sina.com','1982-12-6')";/*** 用PreparedStatement就可以用?先占位*/String newSQL = "INSERT INTO `jdbcstudy`.users (`id`,`NAME`,`PASSWORD`,`email`,`birthday`) " +"VALUES (?,?,?,?,?)";try {preparedStatement = connection.prepareStatement(newSQL);//在赋值时索引是从1开始的//手动给参数赋值preparedStatement.setObject(1,4);preparedStatement.setObject(2,"zhaoliu");preparedStatement.setObject(3,123456);preparedStatement.setObject(4,"zhaoliu@sina.com");preparedStatement.setObject(5,new java.sql.Date(new Date().getTime()));int lines = preparedStatement.executeUpdate();if (lines > 0){System.out.println("添加成功");}} catch (SQLException e) {e.printStackTrace();}finally {JDBCLoad.releaseWithoutResultSet(connection,preparedStatement);}}}
用preparedStatement尝试之前的SQL注入
package com.xiao.JDBC;import java.sql.Connection;import java.sql.PreparedStatement;import java.sql.ResultSet;import java.sql.SQLException;/*** 再次尝试之前的SQL注入*/public class Learn5 {public static void main(String[] args) {login("zhansan","123456"); //只有这一条可以查出来login("'or'1=1","123456"); //无查询结果login("'or'1=1","'or'1=1"); //无查询结果}public static void login(String username,String password){Connection connection = null;PreparedStatement preparedStatement = null;ResultSet resultSet = null;try {connection = JDBCLoad.loadConnection();String sql = "SELECT * FROM `jdbcstudy`.`users` WHERE `NAME`= ? AND `password`= ? ";preparedStatement = connection.prepareStatement(sql);preparedStatement.setObject(1,username);preparedStatement.setObject(2,password);resultSet = preparedStatement.executeQuery();while (resultSet.next()){String name =(String) resultSet.getObject("NAME");String pwd = (String) resultSet.getObject("password");System.out.println(name);System.out.println(pwd);System.out.println("=================================================");}} catch (SQLException e) {e.printStackTrace();}finally {JDBCLoad.release(connection,preparedStatement,resultSet);}}}
java中完成事务
package com.xiao.JDBC;import java.sql.Connection;import java.sql.PreparedStatement;import java.sql.SQLException;/*** 事务*/public class Learn6 {public static void main(String[] args) {Connection connection = JDBCLoad.loadConnection();PreparedStatement preparedStatement = null;try {//关闭数据库自动提交,自动开启事务connection.setAutoCommit(false);//由于连接的是jdbcstudy,换数据库String sql0 = "USE `shop`";preparedStatement = connection.prepareStatement(sql0);preparedStatement.executeUpdate();String sql1 = "UPDATE `person` SET `money` = `money`-5000.000 WHERE `name` = '李四'"; //李四减少5000元preparedStatement = connection.prepareStatement(sql1);preparedStatement.executeUpdate();String sql2 = "UPDATE `person` SET `money` = `money`+5000.000 WHERE `name` = '王五'"; //王五收到5000元preparedStatement = connection.prepareStatement(sql2);preparedStatement.executeUpdate();String sql3 = "UPDATE `person` SET `money` = `money`-3000.000 WHERE `name` = '王五'"; //王五减少3000元preparedStatement = connection.prepareStatement(sql3);preparedStatement.executeUpdate();String sql4 = "UPDATE `person` SET `money` = `money`+3000.000 WHERE `name` = '张三'"; // 张三收到3000元preparedStatement = connection.prepareStatement(sql4);preparedStatement.executeUpdate();//事务执行完成,提交connection.commit();System.out.println("交易成功");} catch (SQLException e) {e.printStackTrace();try {connection.rollback();} catch (SQLException e1) {e1.printStackTrace();System.out.println("交易失败");}finally {//释放资源JDBCLoad.releaseWithoutResultSet(connection,preparedStatement);}}}}结果:交易成功
