数据库初识与基本查询
1、数据库的本质就是文件:
读文件势必产生IO流,但好在数据库有很多已经写好的流程可以帮我们操作文件;
如果我们想要读取保存的数据,直接给数据库发送指令就行了,这些封装好的指令可以进行文件的规范化读写;
2、数据库产品:
- 关系型数据库:数据对应实体对象之间的关系,表与表之间产生某种关系和约束;
- 非关系型数据库:不存储数据之间的关系如何,仅仅存储数据本身。
数据库的语言规范
Mysql而言,==Linux表名区分大小写==,Windows表名==不==区分大小写,注意是==表名==,仅仅表名,其他查询关键字、列名等一律不区分大小写,但数据库服务大都放在Linux上,因此为了避免可能的问题,表名字段名一律小写!!
1、==Data Definition Lang==
数据定义语言:Data Definition Language,操作表格的结构
- 用来创建、删除、修改数据库中的对象(表格、用户、索引、视图、存储过程、触发器等等)
1、CREATE创建
如果不加utf8则无法插入中文汉字
-- 建库设置字符集就所有表都应用了,但其实默认排序就是utf8_general_ci,性能高 create database 库名 default character set = 'utf8' default collate = 'utf8_general_ci'; create database 库名 character set utf8 collate utf8_general_ci; -- 简写
-- 建表,可以单独设置字符集 create table student ( sid int(14), sname varchar(16), sbrithday date ) character set utf8 collate utf8_general_ci; -- 创建新表,数据完全复制于旧表 create table new_table as select * from old_table;
2、DROP删除
drop table 表名; drop database 库名;
3、ALTER修改
- 表名
alter table 原表名 rename to 新表名;
- 列名、列类型、长度
alter table 表名 change 原列名 新列名 类型(长度); -- 仅仅改列名
-- 添加约束
alter table stu change id id int(16) primary key auto_increment;
-- 从3开始自增
alter table stu auto_increment = 3;
- 删除列
alter table 表名 drop [column] 列名; -- column关键字不写也行
- 新增列
alter table 表 add 列名 类型(长度);
-- 新增时添加约束
alter table 表 add 列名 类型(长度) primary key auto_increment;
-- 从34开始自增
alter table 表 auto_increment = 34;
2、==Data Manipulation Lang==
数据库操作语言:data manipulation language,操作表格的具体数据
条件筛选是用来控制显示符合条件的行数据;
where后面按照某一条件或者某一列进行筛选
1、INSERT
insert into student (sname, ssex, sday) values ('值1', '值1', '值1') ('值2', '值2', '值2');
2、DEELTE
delete from student where sid = ?;
3、UPDATE
update student set sname = ? where sid = ?;
3、==Data Query Lang==
数据查询语言(SELECT WHERE GROUP BY HAVING ORDER BY),也可包括在DML中
1、关于几个关键字
嵌套的使用;列的约束、表格关系、联合查询;行列互换;分页查询
select field1, field2 from table_name where -- 后面可以接什么? 比较运算 > >= < <= != = 算数运算 + - * / 逻辑运算 and or not -- and 关键字会先筛选出符合第一个条件的结果集A,再在集合A中按第二个筛选,再按第三个筛选……因此and性能不太好,能不用尽量别用,非要用的话尽量将条件苛刻的放在第一个,第一次筛选就能减少结果集,增加执行效率。 -- or 关键字也是,先查询第一个条件再查第二个条件
-- 代替and的优化查询方法有 between and 关键字 select * from student where age between 19 and 25; -- 代替or的可以用in select * from student where age in (19, 20, 21);
-
like 模糊查询 模糊查询默认忽略大小写 %代替n个字符 _代替一个字符,无论你是什么,你只有一个字符 where sname like 'Z%';查询以z开头的 where sname like '%z';查询以z结尾的 where sname like '_z%';查询第二个字符为z的 where sname like '%z%';查询带z的,不管位置 where sname not like condition;反模糊查询
select chinese + english as scoresum from student;-- 列可以做计算,查询语文和英语成绩和,起别名 as 写不写都行 select 100 + null; -- 结果是null,因为100无法与null相加很显然 IFNULL(null, 0) -- 使用函数转换
2、==排序==
order by column asc[desc]-- 按照某个列升序[降序]排列,不写顺序默认按升序排列 select * from sutdent order by age desc, english desc;-- 整体按照age降序排,age相同的怎么排呢,按english降序排(因为默认升序,所以english不写会自动按升序排)
3、函数
函数可以直接放在查询的后面,按功能分类学习;
-
比较函数
ISNULL(); -- is null 返回1,not null 返回0
-
数学函数
ABS(x); -- 绝对值 FLOOR(x); -- 向下取整 FLOOR(2.9) = 2 MOD(5, 2); -- 取余数 MOD(5, 2) = 1
-
日期和时间函数
now() year(参数) month(参数) day(参数) week(参数)
-
控制流程函数
if(是否, '是', '否'); -- 就是一个变形的三目运算 ifnull(null, '值'); -- 如果第一个条件为null则返回第二个,返回值是数字或者字符串,具体情况取决于其所使用的语境,简言之会返回更通用的一个。
-
==*字符串函数==
instr("abc", "a"); -- 返回1,就是后者第一次出现在前者的位置,0就是不存在 substr("abc", 1); -- 截取字符串,但是索引从第一开始
-
==*分组函数(聚合函数)==,就是这些函数使用起来一般后面都要跟一个group by 进行分组,否则查询的结果意义不大。
==分组函数在没有where条件时,优先级最高,有where先查where。==
distinct -- 关键字 select distinct 列 from student; -- 只为这一列单独去重; select 列1, distinct 列2 from student; -- 也是只为一列单独去重,但是前面还有一个列1,如果列1的结果集条数和distinct 列2条数不一致则会报错;
select distinct 列1, 列2 from student; -- 有点联合主键的味道,会把列1列2联合起来当做一个字段进行去重操作;
- ```mysql group by -- 关键字 select count(classid) from XX grouy by classid;-- 查询每个班级学生数量 select max(socre) from XX grouy by classid; -- 查询每个班的最高分 select avg(score) from XX group by classid; -- 查询每个班的平均分 -- 分组 + 条件过滤: -- 分组函数在没有where条件时,优先级最高,有where先查where。 -- 查询哪几个班级有女生,所以先where筛选出所有女生再分组 select class_id from student where sex = female group by class_id; -- 查询18岁的同学都在哪个班级,先where筛选出所有18岁的同学再分组 select class_id from sutdent where age = 18 group by class_id; -- 有些条件过滤只能在分组的基础上才能完成: -- 查询平均分高于85分的班级,只有先按班级分组了才能知道每个班级的平均分 select class_id from student group by class_id having avg(score) > 85;
-
-- 嵌套查询 select * from tableA; -- tableA可能来自另一条查询的结果集,并且还能取别名 select * from (select * from student where location = '厦门') as location_table; select * from student where city_id in (select city_id from city where city_name = '厦门');
==分组查询==能展示的信息==只有两种==:group by 的列和所有分组函数,具体分组函数的参数并不一定要是group by 的那一列,就是方便统计的
count() max() min() avg() -- 包括但不限于以上这些
4、分页查询
select * from table where xx group by yy limit offset, size; -- limit写在最后面,从offset偏移量起,往后面获取size条,offset偏移量从0开始 -- service层调用dao,传参(pageNo - 1) * pageSize), pageSize;因为service是处理业务逻辑的,dao是操作数据库的,尽量各司其职,不要让Dao去做逻辑处理的操作。service处理之后传给dao的就是被处理过的offset了。 dao原生sql:limit offset, pageSize
5、几个关键字的使用
any 、 some 、 all
-- 满足子集中的任何一个就行,是==比较
select column1, column2 from table_name where id in (1, 2, 3);
-- any 后面只能跟查询语句,不能接具体的值
-- 按照英文语义理解即可,选择id大于子集中任何其中一个就行,简单来说就是大于子集的最大值
select column from table where id > any (select id from table where name = '');
= any 等于其中一个 < any 小于其中一个
some某些
all全部 != all() 就是not in ()
UNION
select t_id, t_name, t_sex from teacher union select s_id, s_name, s_sex from student;
-- 竖着拼接,而且选择两个表的列数要一样,但是列的数据类型没有限制,并且自动去重
--- union all ,也是拼接,是全部拼接不会去重。
6、IN 和 EXITS
(1)IN关键字
SELECT * FROM TABLE_A WHERE ID IN (
SELECT A_ID FROM TABLE_B
)
先进行子查询,查出子表的匹配的数据,再将内表(子表)和外表做笛卡尔积(99相乘),再筛选匹配的数据。
若果子表数据量少,可以使用。
(2)EXISTS关键字
SELECT * FROM TABLE_A WHERE EXISTS (
SELECT TABLE_B.A_ID FROM TABLE_B WHERE TABLE_B.A_ID = TABLE_A.ID
)
查询结果和上面完全一样,但是执行流程完全不一样;
exists 优先查询主表,也就是外表,根据主表的查询结果去判断子查询是否存在,存在则返回true则该记录保留否则返回false该记录不保留。
exists 以外表为驱动表。
4、DCL
数据库控制语言:DATA CONTROL LANGUAGE,控制用户权限
1、权限操作
关键字grant赋予权限,revoke回收权限;
MySQL的root就是sysdba,拥有最高权限,可以创建新的用户并赋予他权限;
Mysql Privilege System:
-- 进入mysql库中查看已存在用户情况
select user, host, authentication_string from user;
create user 'username'@ip identified by 'password';
create user 'Shlyn'@'localhost' identified by '111111';
-- after user being created, has only one privilege, logining; you must be granted from sysdba for select, update, isnert, delete on tables of databases;
show grants for 'Shlyn'@'localhost'; -- view the privilege of user Shlyn;
-- grants command
grant privileges on database.table to username@'ip' identified by 'password';
grant all privileges on *.* to Shlyn@'localhost'; -- 后面密码可以不写。
flush privileges; -- 一般可以不用刷新,但多操作一步也保险。
-- Obviuously, it's not safety to grant all privileges to another user, so revoke his privileges an grant an lower level privileges;
revoke privileges on database.table from username@'ip' identified by 'password';
grant all privileges on jdbc.* to Shlyn@localhost;
update user set authentication_string = password('密码') where user = 'Shlyn';
drop user Shlyn@localhost;
2、常用的权限如下
数据库/数据表/数据列权限:
Create 建立新的数据库或数据表
Alter 修改已存在的数据表(例如增加/删除列)
Drop 删除数据表或数据库
Insert 增加表的记录
Delete 删除表的记录
Update 修改表中已存在的记录
Select 显示/搜索表的记录
References 允许创建外键
Index 建立或删除索引
Create View 允许创建视图
Create Routine 允许创建存储过程和包
Execute 允许执行存储过程和包
Trigger 允许操作触发器
Create User 允许更改、创建、删除、重命名用户和收回所有权限
全局管理MySQL用户权限:
Grant Option 允许向其他用户授予或移除权限
Show View 允许执行SHOW CREATE VIEW语句
Show Databases 允许账户执行SHOW DATABASE语句来查看数据库
Lock Table 允许执行LOCK TABLES语句来锁定表
File 在MySQL服务器上读写文件
Process 显示或杀死属于其它用户的服务线程
Reload 重载访问控制表,刷新日志等
ShutDown 关闭MySQL服务
特别的权限:
All 允许做任何事(和root一样)
Usage 只允许登录,其它什么也不允许做
5、TPL
TRANSACTION PROCESS LANGUAGE,事务处理语言
6、总结
- SQL语句
- DDL DML DQL DCL TPL
- DQL细化
- where
- order by
- group by having :::where xx = xx group by
- 嵌套(in any some all union [union all])
- 函数的使用
表格列的约束、联合查询
1、表格列的约束
通常来讲只有三种:==主键、外键、唯一==
a、主键约束:只能有一个列(或者一个联合主键列)被设置为主键约束,同时默认添加NOT NULL的约束
添加约束使用DDL语句
alter table 表名 add constraint pk_id primary key (id);
alter table 表名 add constraint fk_class_id foreign key (class_id);
-- 添加好约束之后通过 desc table; 或者 show keys from student; 命令查看表的相关信息
-- 标准的约束添加语句如上,但是主键的名字通常没有意义,可以简写
alter table 表名 add primary key (列);
-- 设置主键自增
alter table 表名 modify id int(16) auto_increment;
-- 从3开始自增
alter table stu auto_increment = 3;
alter table 表名 modify id int(16); -- 不写约束就能解除自增
alter table stu change id id int(16); -- 也能解除自增
alter table stu drop primary key; ---- 删除主键约束,前提是必须先解除主键自增
b、唯一约束:不能重复可以为空,也可以设置联合Unique Key
alter table stu add constraint uk_stu_no unique (stu_no, tea_no); -- 联合UK
alter table stu add unique (stu_no); -- 也是可以简写的,也可以不写约束名字,默认名字为列名,但是最好为UK加上名字,UK不想PK的唯一一个,UK有很多个必定需要名字来区分
alter table stu add unique uk_stu_no (stu_no); -- 最好写UK索引的名字
alter table stu drop index uk_stu_no; ---- 删除唯一索引用drop index indexname
alter table sut drop index idx_stu_id; ---- 删除普通索引也是用drop index indexname
c、非空约束
alter table stu modify sname varchar(32) not null default ''; -- 非空之后可以加个默认值
d、普通索引:就叫INDEX,允许重复
表格与表格之间的关系
1、类与类的关系
is-a继承或实现;has-a拥有其属性(聚合关系,一对多时,多中拥有一的信息);包含:use-a把另一个类当参数放入方法中临时变量;
2、表格之间的关系
一对一、一对多、多对多
3、表格之间的联合查询
-
==等值连接==:先获取两个表的广义笛卡尔积再筛选符合条件的行;总之不会出现左连接或者右连接存在空值的现象,笛卡尔积每一行都有完整的数据,一般是一对多时多的那一方为主导。
- 任何两张表都能进行笛卡尔积的联合,但是要看有没有意义。
select * from a, b where a.id = b.id; -- where条件写 > < != 都行,但是没有意义
-
==外连接==:分左外连接和右外连接
select * from a left outer join b on a.id = b.id; left outer join 左边的是左表也就是基准表 right outer join 右边的是右表也就是基准表
左连接时以左表为基准,左表的数据必须全部显示,右表有匹配的则显示,没有的则显示为NULL,==但如果此时左表的一条记录匹配右表的多条记录则右表的记录全部显示,并且左表会重复匹配==
-
==内连接(自连接)==:不分左右,因此没有left/right关键字,从结果上来看和等值连接的结果完全一致,但是性能却好很多。可以连自己进行查询,就是把一张表当两张表互不影响查出想要的信息。
select * from a inner join b on a.id = b.id;
4、表格的行列互换(有可能会应用到)
有个原始记录表,是按当前月份时间插入的数据。
ware_house(仓库名) | amount(库存量) | month(月份) |
---|---|---|
A | 10 | 1 |
B | 100 | 1 |
C | 1000 | 1 |
A | 20 | 2 |
B | 200 | 2 |
C | 2000 | 2 |
A | 30 | 3 |
B | 300 | 3 |
C | 3000 | 3 |
但是,显然这样的数据不适合统计,想要的效果如下。
ware_house(仓库名) | month(一月) | month(二月) | month(一月) |
---|---|---|---|
A | 10 | 20 | 30 |
B | 100 | 200 | 300 |
C | 1000 | 2000 | 3000 |
分析:
-
显然按第一列来看需要一个group by ==ware_house==;其次,使用分组函数时,select后面只能写分组条件==ware_house==,和分组函数max()、avg()等……
select ware_house, max(需要一个语句A,能显示一月的存量), max(需要一个语句B,能显示二月的存量), max(需要一个语句C,能显示三月的存量) from ware_table group by ware_house; -- 由于每个仓库每个月的存量只有一个数字,因此max(?)就是自己,不用多虑,关键是语句A、B、C怎么写? -- 使用三目运算 if(month = 1, amount, 0); -- 分组函数在没有where条件时,优先级最高,有where先查where,这里没有where,所以先按库名分组,分组之后,只有1、2、3月份了,使用if()就能轻松筛选了。 select ware_house, max(if(month = 1, amount, 0)) as '一月', max(if(month = 2, amount, 0)) as '二月', max(if(month = 3, amount, 0)) as '三月', from ware_table group by ware_house;
数据库设计范式
1、1NF(1 Normal Form)
强调数据列的原子性;
每一个表格的每一列都是不可分割的;
第一范式很容易理解,且一般设计数据库自然而然地就会满足第一范式,除非建表人脑子有问题?
2、2NF
在满足1NF的前提下;
表必须有一个主键;
不允许出现数据的部分依赖性,就是==非主键列==必须完全依赖==主键列==而不能只有部分依赖?部分依赖那就是联合主键的时候了
部分依赖的例子:
项目编号 | 项目名称 | 工程师编号 | 工程师名称 | 职级 | 小时工资 |
---|---|---|---|---|---|
A | 教学楼 | 1 | 赵一 | 高工 | 1000 |
A | 教学楼 | 2 | 钱二 | 助工 | 100 |
B | 餐厅 | 3 | 孙三 | 高工 | 1200 |
项目编号和工程师编号构成==联合主键==,显然非主键列都只依赖主键的一部分,而并非去全部依赖,此时就不遵循第二范式了。
怎么解决?
将数据拆开分在两个表中,把engineer_name和engineer_no都拿出去放在engineer表中。
3、3NF
在满足前两个范式的前提下,不允许出现传递依赖性;
即一个数据库不能包含另一个数据库的非主键信息,有个隐式前提,就是已经包含对方的主键信息,如果再包含非主键信息就会产生冗余,更新表数据将产生困难。
就像上面的表拆分之后,发现项目跟小时工资绑定了,显然这两者本应该毫无关系,但现在却关联在一起了,这就是非主键列受到非主键的影响。
范式终归只是范式,视实际情况而定,有时候并不一定要完全遵循,例如员工表存储公民身份信息,那么除了存入主键之外,身份证、姓名、籍贯等都可以一并存入,因为这些信息一般不会频繁更新,但是却需要频繁查询。
数据库的事务和隔离级别
1、事务Transaction
事务可以理解为数据库做的某件完整的事情,但有些事情中可能包含很多小的流程。
而处理这件事情(事务),要么其中的小流程都完成要么都不完成,不可能前面都完成了就最后一件没有完成,而且出现这样的情况数据库需要有一个回滚机制,让前面已完成的事情恢复初始状态,整个事务就都没完成。
事务的本质:可以理解为多线程并发操作同一张表格带来的安全问题。
2、四大特性
Atomocity
原子性:一个事务的所有操作是一个整体不可分割,事务中的所有操作要么都成功要么都失败。
Consistency
一致性:一个用户操作了数据提交之后看到的数据与另一个用户看到的数据是一致的,因为数据库的本质就是文件,而且不同用户看到的同一张表就是一份文件,数据肯定是一致的。
Isolation
隔离性:多个事务并发访问操作时,事务之间是隔离的,一个事务不应该影响其它事务运行效果。
Durability
持久性:事务完成以后,该事务所对数据库所作的更改便永久的保存在数据库之中,不会被回滚。
3、开启事务
之前CRUD自动完成是因为Mysql默认自动开启事务和自动提交事务,如果是手动开启则需要手动提交。
show variables like 'autocommit';
set autocommit = off; -- 设置关闭自动提交
begin; 或者 start transaction;
-- 操作1
-- 操作2
-- 操作3……
commit; 或者 rollback;
4、隔离级别
隔离级别由高到低(从严到宽):
Serializable
可以避免所有事务问题,只是并发性极低,因为一个请求操作会有锁定资源的过程,其他请求均无法操作,有效避免了事务冲突。就是单线程了,只有一条线程串行执行,执行完一执行二再执行三。
Repeatable Read
可重复读,可以避免==不可重复读==,但是无法避免==幻读==,Mysql默认的隔离级别。
Read Committed
读已提交,会产生不可重复读,但是可以避免脏读。这样就够了,脏读和不可重复读本质上来说数据并没有错乱只是人的视觉效果错乱,并发性能很高。Oracle默认的隔离级别。
Read Uncommitted
读未提交,会产生==脏读==
5、由不同级别产生的不同问题:
Read Uncommitted产生脏读
现象:一个人读到了另一个人已修改但未提交的数据,但已修改的不一定是有用的,有可能回滚,因此是谓脏数据,读到脏数据称之为脏读。
Read Committed产生不可重复读
现象:A读取了一些数据,读完之后B对同一数据片段进行==修改或者删除==,A再按之前的条件再读一次发现结果与之前不同,是谓不可重复读。
其实,读取也是一条事务,但由于没有更改的操作一般也不用开启,但是也可以开启,开启之后A的读取是一条事务,B的更改也是一条事务,在A没有提交之前读取的所有数据结果都应该是一样的,不会因为过程中B的提交而导致A的读取事务读到不一样的数据,因此我们都希望事务之间的==完全隔离==,这种隔离级别就是==Repeatable Read==,也就是Mysql的默认级别。在A的第一次读取事务提交之后再读取发现数据的变化属于正常情况了。
Repeatable Read产生幻读
在两边都设置set session transaction isolation level repeatable read;
的情况下。
现象:A 开始事务,B 开启事务,A 新增一条,然后查询发现有记录此时还未提交;B 也新增同一条带着相同的主键,发现事务执行阻塞;A commit 发现 B 的阻塞被恢复且报错了说不能插入相同的主键,这就奇怪了,B 就查询看一下发现并没有相同的主键的记录,这就是幻读,像 TM 发生了幻觉一样。 此时 B 如果提交事务,然后重新查询就会发现 A 新增的记录。
这不是锁表。
6、修改数据库的隔离级别
set session transaction isolation level read uncommitted;
set session transaction isolation level read committed;
set session transaction isolation level repeatable read;
select @@tx_isolation; -- 查看隔离级别
JDBC规范
Java DataBase Connectivity
1、JDBC
但是Database有那么多,Oracle、Mysql、SqlServer等等,JDBC却没有指定是和哪种数据库进行联通,因此那就认为只要是数据库都能和Java进行联通吧。
确切的说JDBC是Java平台提供的一种统一执行的规范(或者说标准)。
提到规范必定想到接口,因此JDBC是一种接口,但具体实现由谁进行?
因此每一个数据库厂商的数据库软件产品都实现了此规范。
2、JDBC六部曲
JDBC的统一规范
1、导包
将jar包导入到工程中去
2、加载驱动类Driver
3、获取连接
可以理解为之前的Socket
4、创建状态参数
可以理解为Socket的getInputStream和getOutPutStream
5、执行数据库操作
DML:executeUpdate();
DQL:executeQuery();
6、关闭流管道
最好放在finally{}块中执行
resultSet.close()
ps.close()
connection.close()
3、模糊查询
1、模糊查询:select * from table where name like '&?&'
用预处理的话就就在传参数的时候给参数拼接 "%" + name + "%"
2、模糊查询:select * from table where name like "%"?"%" 这样写在拼接字符串的时候会将左右两边的%与?作为整体拼接,但是要使用转义字符。
预处理的时候传参正常传递name就行了
4、分页查询
1、service
service进行逻辑处理:将当前页面转为SQL语句中的偏移量offset;
offset = (pageNum - 1) * pageSize;
2、dao
dao被service调用传进来的参数就是正常的offset和size,直接调用jdbc操作就行了。
5、多表联合
1、一对多连接
返回值的接收问题:如果是一对多的两张表,对应两个domain实体对象时,通常把一的一方作为多的一方的属性聚合在多的一方的类的内部,是谓类之间的聚合。
2、多对多连接
多对多由于两表之间没有直接的关系,通过第三张表关联起来的,因此两domain对象不用关联,获取到ResultSet直接解析构建成两个对象就行了。
3、特殊查询
比如查询每个班级的学生人数,返回值自由班级编号和班级人数,那就用List
Mysql面试相关
要展示一下技能
Sql语句方面
比如group by, having,左连接,子查询(带in),行转列等高级用法。