1.表与表之间的对应关系:
一对一、一对多、多对一、多对多
2.约束
约束作用:约束是一种限制,用于限制表内的数据,为了保证数据的准确性以及可靠性。
not null、default、primary key、foreign key、unique
约束分类:
列级约束:定义列的同时指定的约束
表级约束:列定义之后指定的约束
外键:对应一对多的关系。一为主表,多为从表。
3.锁🔒
只允许拿到锁的事务访问数据
(1)根据锁的粒度分类:行锁与表锁
表级锁:只有当前用户可以操作整张表,其他排队等候,等待当前sql操作执行完毕
特点:开销小(内存),加锁快,不会出现死锁(一锁就锁整张表,不会争用数据行),锁定颗粒大(一锁锁整张表,锁定范围大),发生锁冲突的概率高(争用锁),并行性差(一锁锁整张表,不能同时处理多行数据),一致性极好
行级锁:只有当前用户可以操作该行记录,其他对该行的操作排队等候,等待当前sql操作执行完毕。
特点:开销大,枷锁满,会出现死锁,锁定颗粒小,发生锁冲突的概率低,并发性高。
(2)根据数据库系统分:读锁与写锁
读锁:其他请求可读不可写(共享),用于不更新数据的操作(只读操作),例如 select语句
select加共享锁示例:
select * from table lock in share mode;
写锁:其他请求不可读不可写(排他),用于数据修改操作。insert、update,delete默认自带写锁,确保不会同时对同一资源多重更新。
select 加排他锁示例:
select * from table for update;//该条语句在执行时,所有扫描过的行都会被加锁
由于使用悲观锁时,扫描到的数据都会被锁定。所以在使用悲观锁时,务必确定使用索引,而不是全表扫描,否则会把整个表都锁住。
(3)根据程序员的角度分类:悲观锁与乐观锁
定义:
悲观锁:指对数据被外界(包括当前系统、外部系统的事务)修改保持保守态度。即在数据操作整个期间,对所有扫描到的数据进行加锁。
悲观锁是大多数依靠数据库提供的锁机制(数据库提供的锁机制才能实现数据访问的排他性)
缺点:如果加锁的时间太长,其他用户会长时间无法访问数据,影响程序的并行性,对数据库性能影响很大。
乐观锁:乐观锁认为数据一般情况下不会造成冲突,所以只有在数据提交更新操作时,才会正式对数据的冲突与否进行检测。如果检测到冲突,会返回错误信息,让用户决定如何去做。
实现乐观锁方法:时间戳、版本号
先查询,记录当前数据的版本号(version)或时间戳(timestamp);进行更新操作,提交更新数据前先查询版本号或时间戳是否改变,没有改变则提交数据并更新版本号或时间戳。
示例:
通过版本号:
set @version = 1;
select * from stu_info;//查到当前版本号为1
update set grade = 80,version = version + 1 from stu_info where version = 1 and id = 3;//当版本号是前面查到的版本号时,说明前面查询到的语句没有被改变,没有发生数据冲突
通过时间戳:
类似于版本号,设置一个时间变量
set @time = '2022-01-10-15:08';
update set grade = 80,time = now() from stu_info where id = 3 and time = '2022-01-10-15:08';
示例:如下是一种数据冲突,v1通过事务1修改,通过事务2修改,事务1应该再从v2继续修改。这里事务1与2就产生了数据冲突。
4.引擎
(1)innodb:自动给更新操作涉及的数据集加排他锁。innodb的行锁是通过给索引上的索引项加锁来实现。而oracle是通过在数据块中相应数据行加锁实现的。所以,innodb只有在使用索引条件检索数据时才会使用行级锁,否则innodb将使用表锁。
(2)myisam:自动加锁,一般不需要用户干预。在查询时,myisam自动给表加读锁;在更新操作时,默认给表加写锁。
5.范式
设计关系数据库的规范要求。各种范式成递次规范。越高的范式数据库冗余越小
目的:使数据库结构更合理,消除存储异常,使数据冗余尽量小,便于插入删除更新。
(1)第一范式(1NF):每一列都是不可分割的原子数据项。
一个字段只能有一个确定的值,不能是集合。
1NF是关系数据库的基本要求
(2)第二范式是建立在第一范式的基础上的。一个表必须有一个主键;没有包含在主键中的列必须完全依赖于主键,而不能只依赖主键的一部分。
完全依赖:X–>Y,唯一确定
(3)第三范式:属性不依赖其他非主键属性
在学生表里,主键是学生id,但是班级姓名依赖于班级id,科目名称依赖科目id,成绩也依赖于科目id。
(4)BCNF(巴斯科德范式)
(5)第四范式
6.函数依赖:X–>Y,函数Y依赖于X,唯一确定
传递函数依赖:
码:当K确定的情况下,该表除K之外的所有属性的值也就随之确定,那么K就是码。一个表中可以有超过一个码。(实际应用中,通过选择其中一个码为主码)
7.补充:
1)当不知道查询条件中的字段个数时,可以用sql语句拼接:
//占位符拼接
String sql = "select * from staff_infor where 1=1";
//标签的name属性就是id、staffName、phone、depart
//对应的可以直接用req.getParameter("")获取value值
String id = req.getParameter("id");
String name = req.getParameter("staffName");
String phone = req.getParameter("phone");
String d = req.getParameter("depart");
System.out.println(id+","+name+","+phone+","+d);
if(id != "" && id != null) {
sql +=" and staff_id = ?";
str[ans++] = id;
}
if(name != "" && name != null) {
sql += " and staff_name = ?";
str[ans++] = name;
}
if(d != "" && d != null) {
sql += " and staff_depart = ?";
str[ans++] = d;
}
if(phone != "" && phone != null) {
sql += " and phone = ?";
str[ans++] = phone;
}
sql += ";";
String[] b = new String[ans];
System.arraycopy(str, 0, b,0, ans);
List<Staff> list = DBHelper.queryAll(sql, Staff.class,b);
2)直接拼接查询字段
StringBuffer sql = new StringBuffer("select * from staff_infor where 1 = 1");
if(!StrUtil.isEmpty(id)) {//sql语句中字符串用""引起来;StringBuffer的isEmpty方法,如果id为null,会判错。
//现在重写了一个isEmpty方法,如果id为空,直接略过,不会判错。
sql.append(" and staff_id = '" + id + "'");
System.out.println(sql);
}
if(!StrUtil.isEmpty(name)) {
sql.append(" and staff_name = '" + name + "'");
System.out.println(sql);
}
if(!StrUtil.isEmpty(d)) {
sql.append(" and staff_depart = '" + d +"'");
System.out.println(sql);
}
if(!StrUtil.isEmpty(phone)) {
sql.append(" and phone = '" + phone + "'");
System.out.println(sql);
}
sql.append(";");
//基本数据类型用(int)x强转
List<Staff> list = DBHelper.queryAll(sql.toString(), Staff.class,null);
//StrUtil.java
package util;
public class StrUtil {
public static boolean isEmpty(Object obj) {
if (obj == null) {
return true;
}
if (obj.toString() == "" || obj.toString().length() == 0) {
return true;
}
return false;
}
public static void main(String[] args) {
String a = null;
a.isEmpty();
System.out.println(a);
}
}