外键约束 (完整性约束)(fk) *****
牵扯到两个表格:
概念:
1. 外键约束, 是存在两个表格的操作 !
2. 一张表格我们称为主表(父表) , 另一张叫做从表(子表) , 定义了外键约束的表格属于从表 !
3. 从表的外键字段, 是在参考主表中的主键 或 unique约束的字段 ! (被参考的字段 必须具备唯一性)
4. 从表外键字段的取值范围, 受限与主表的参考字段, 外键的取值, 必须是参考字段的值或者null!
作用:
外键约束 保证了参数的完整性, 限定了一个列的取值范围 !
外键的实现
编写外键时 , 出现外键的字段称为从表, 主表必须创建在从表之前!
编写外键时, 主表是被参考的表, 不用做任何的额外操作 !
格式:
create table 从表名(
字段名 字段类型(长度) references 主表名(参考字段)
);
案例:
建立主外键关系, 使用用户和订单表
1. 创建用户主表
drop table user15;
create table user15(
id number constraint user15_id_pk primary key,
uname varchar2(30)
);
2. 创建订单从表
drop table order15;
create table order15(
id number constraint order15_id_pk primary key,
info varchar2(100),
userid number references user15(id)
);
测试向订单表格(从表)插入数据
insert into order15 values(10000001.‘hahahaha’,10001);
发生了错误:
SQL 错误: ORA-02291: 违反完整约束条件
发生上述错误的原因在于 从表中的外键的取值, 在主表的被参考字段中不存在!
尝试向主表插入数据
insert into user15 values(10001,‘admin’);
测试向订单表格(从表)插入数据
insert into order15 values(10000001.‘hahahaha’,10001);
删除数据
在删除主表数据时, 需要先将从表中参考的数据 删除/置空 !
否则会出现错误: ORA-02292: 违反完整约束条件 已找到子记录
- 尝试删除id为10001的用户
delete from user15 where id=10001;
commit;
上述的操作, 发生了错误
ORA-02292: 违反完整约束条件 已找到子记录
-
先删除从表中的数据, 再尝试删除id为10001的用户
delete from order15 where userid=10001;
delete from user15 where id=10001;
commit; -
或者尝试使用置空操作, 来删除主表数据
update order15 set userid=null where userid=10001;
delete from user15 where id=10001;
commit;
删除被外键所关联的表格
在删除主表时, 必须先删除从表, 或取消表格间的关联关系 !
否则会报错: SQL 错误: ORA-02449: 表中的唯一/主键被外键引用
删除主表操作:
-
尝试删除user15表格
drop table user15;
-
尝试删除从表后 . 再删除主表 !
drop table order15;
drop table user15;上述的操作, 确实没有报错, 删除了从表以后. 就可以直接删除主表了 !
但是其实上述的操作 , 是不建议的 . 因为表格之间关系太复杂 !
删除一个表格时, 直接取消外键关联关系 ! ***
格式: drop table 主表名 cascade constraints;
主表是被参考的表格, 主表在删除时, 会抛出’表中的唯一/主键被外键引用’的错误
在删除时, 可以直接在删除表格语句后, 添加取消关系的关键字
cascade constraints;
这样删除的主表 , 会取消从表的外键关系, 并不会删除从表 !
案例:
1. 创建用户主表
drop table user15;
create table user15(
id number constraint user15_id_pk primary key,
uname varchar2(30)
);
2. 创建订单从表
drop table order15;
create table order15(
id number constraint order15_id_pk primary key,
info varchar2(100),
userid number references user15(id)
);
尝试使用cascade constraints 删除并取消关联关系
drop table user15 cascade constraints;
级联操作(主表数据的删除) ***
两种不同的操作:
-
级联置空
删除主表中被参考数据时, 从表外键字段数据自动设置为null
关键字: on delete set null -
级联删除
删除主表中被参考数据时, 从表外键字段数据自动删除一行 !
关键字: on delete cascade;
语法格式:
在声明外键的语句后, 加入级联关键字即可 ;
例如: userid number constraint order15_userid_fk references user15(id) on delete set null
级联置空案例:
1. 创建用户主表
drop table user15 cascade constraints;
create table user15(
id number constraint user15_id_pk primary key,
uname varchar2(30)
);
2. 创建订单从表
drop table order15 cascade constraints;
create table order15(
id number constraint order15_id_pk primary key,
info varchar2(100),
userid number constraint order15_userid_fk references user15(id) on delete set null
);
尝试向上述的表格中插入数据
insert into user15 values(10001,'admin');
insert into user15 values(10002,'root');
insert into user15 values(10003,'dongfei');
insert into order15 values(10000001,'hahaha1',10001);
insert into order15 values(10000002,'hahaha2',10002);
insert into order15 values(10000003,'hahaha3',10002);
insert into order15 values(10000004,'hahaha4',10001);
commit;
在数据已经存在的情况下, 尝试删除主表数据 !
delete from user15 where id=10002;
commit;
级联删除案例:
1. 创建用户主表
drop table user15 cascade constraints;
create table user15(
id number constraint user15_id_pk primary key,
uname varchar2(30)
);
2. 创建订单从表
drop table order15 cascade constraints;
create table order15(
id number constraint order15_id_pk primary key,
info varchar2(100),
userid number constraint order15_userid_fk references user15(id) on delete cascade
);
尝试向上述的表格中插入数据
insert into user15 values(10001,'admin');
insert into user15 values(10002,'root');
insert into user15 values(10003,'dongfei');
insert into order15 values(10000001,'hahaha1',10001);
insert into order15 values(10000002,'hahaha2',10002);
insert into order15 values(10000003,'hahaha3',10002);
insert into order15 values(10000004,'hahaha4',10001);
commit;
在数据已经存在的情况下, 尝试删除主表数据 !
delete from user15 where id=10002;
commit;
外键的表级约束 *
格式:
create table 表名(
字段列表…,
constraint 别名 foreign key(本表中的外键字段) references 主表名(主表字段) 级联关键字(on delete set null|on delete cascade)
);
案例:
1. 创建用户主表
drop table user15 cascade constraints;
create table user15(
id number constraint user15_id_pk primary key,
uname varchar2(30)
);
2. 创建订单从表
drop table order15 cascade constraints;
create table order15(
id number constraint order15_id_pk primary key,
info varchar2(100),
userid number,
constraint order15_userid_fk foreign key(userid) references user15(id) on delete cascade
);
尝试向上述的表格中插入数据
insert into user15 values(10001,'admin');
insert into user15 values(10002,'root');
insert into user15 values(10003,'dongfei');
insert into order15 values(10000001,'hahaha1',10001);
insert into order15 values(10000002,'hahaha2',10002);
insert into order15 values(10000003,'hahaha3',10002);
insert into order15 values(10000004,'hahaha4',10001);
commit;
在数据已经存在的情况下, 尝试删除主表数据 !
delete from user15 where id=10002;
commit;
先建立表格 后添加约束 (所有约束) ***
修改表结构, 来实现后添加约束的操作 !
一个表格 存在约束 和 不存在约束, 在大量的数据插入时, 不存在约束的表格, 效率更高 !
如果在进行数据初始化时 (给表格添加初始的行) , 我们一般在创建表格后. 不添加约束, 等数据初始化完毕后. 再通过修改表结构, 给它添加约束 !
后添加约束 需要注意:
表格中原本存在的数据, 一定要是符合约束条件的 , 否则会出错!
格式:
alter table 表名 add constraint 约束别名 约束关键字(字段名);
案例:
-
创建表格 person15
drop table person15 cascade constraints;
create table person15(
id number,
name varchar2(30),
age number
); -
插入数据
insert into person15 values(10001,‘dongfei’,80);
insert into person15 values(10002,‘zhulei’,18);
insert into person15 values(10003,‘yezhibin’,12);
commit; -
通过修改表结构, 添加约束
alter table person15 add constraint person15_id_pk primary key(id);
删除一个约束 ***
与添加约束一样, 都是通过修改表结构操作 来完成的 !
格式:
alter table 表名 drop constraint 约束别名;
案例:
alter table person15 drop constraint person15_id_pk;
数据库的其他对象
下面要学习的其它对象, 对于它们在数据库中的创建与删除操作 , 与表格操作 基本一致 !
序列 *****
什么是序列?
有时我们在数据的插入时, 需要一些可以自增长的变量 ~
例如: 我们在创建一行数据时, 需要添加唯一标识的id,
我们一般会将id设置为主键, 主键时不可重复的 .
我们怎么做, 才可以保证id 不会重复? 且有效?
序列就是解决这个问题的 , 类似Java中的一个整型的变量(int xx = 0;);
可以在语句中 直接使用, 并让其自增长(自身加一 类似Java中的i++)!
我们使用序列, 一般是在insert 语句 和 select语句中!
在Java程序中 , 进行数据的插入. 一般是先通过select语句, 查询到当前的序列值, 然后再使用值, 插入到行中 !
语法格式:
-
创建序列的语法格式:
create sequence 序列名称;
序列的命名规范:表名_字段名_seq;
(相当于在Java中创建了一个变量i: int i=0;) -
使用序列的语法格式:
序列名称.nextval
(相当于在Java中使用变量i:i++)
可以得到一个整型的值, 且自身会加一 !
这个自增长的值 是从1 开始的! -
删除序列的语法格式:
drop sequence 序列名称;
案例:
-
创建一个表格
drop table user15 cascade constraints;
create table user15(
id number constraint user15_id_pk primary key,
uname varchar2(32),
upass varchar2(32)
); -
创建一个序列
drop sequence user15_id_seq;
create sequence user15_id_seq; -
插入一些数据
insert into user15 values(user15_id_seq.nextval,‘dongfei’,‘aijiaoji’);
insert into user15 values(user15_id_seq.nextval,‘gaofan’,‘aixuexijiaoji’);
insert into user15 values(user15_id_seq.nextval,‘zhulei’,‘aijiaohahaha’);
commit;
索引 (了解)
数据库是一款软件, 用来存储数据的仓库 !
索引是通过消耗大量的时间和空间 来达到加速查询的目的 !
索引技术 是数据库自动使用的 , 一个表格中只存在一个索引就够了!
如果一个表格存在主键, 那么在主键创建时, 索引就已经存在了 !(由系统创建的);
命名规范: 表名_字段名_index
语法:
创建:
create index 索引名称 on 表名(字段名);
删除:
drop index 索引名称;
案例:
自己编写索引, 在数据量小的时候, 查询的速度 ,肉眼是无法观察到的 !
create index user15_uname_index on user15(uname);
drop index user15_uname_index;
视图 *****
查询一个不存在的表格
select * from hahahahha;
上面的操作, 出现了错误, 表或视图不存在
视图的概念:
视图就是一条SELECT语句 !
我们可以将一条SELECT语句的结果, 生成为视图, 然后把这个视图当作表格来操作!
相对于select语句产生的数据结果而言, 视图本身的空间很小!
作用:
可以对同一份数据 做出不同的表现 ! 可以用来简化子查询 !
还可以用来控制查询权限 !
注意:
在操作视图时, 对视图的修改, 会影响表格!
创建/替换 视图的语法格式:
create or replace view 视图名称 as 查询语句;
删除视图的语法格式:
drop view 视图名称;
对于视图的操作, 与 表格一致!
案例:
根据多表查询, 生成一个视图:
根据员工+部门表, 生成视图: (包含了员工的id,员工的姓,员工的月薪,部门的名称)
-
先编写出查询语句:
select e.id,e.last_name,e.salary,d.name from s_emp e,s_dept d where e.dept_id=d.id;
-
根据上面的语句, 创建视图
create or replace view s_e_d as select e.id,e.last_name,e.salary,d.name from s_emp e,s_dept d where e.dept_id=d.id;
-
把视图当前表来查询
select id,name from s_e_d;
分页查询技术
在数据库中执行查询操作时, 通过添加where条件, 限制查询的内容, 进而实现分页操作!
分页查询的常见公式:
分页查询时 id(行号) 的范围:
- 开始范围: 大于(当前页数-1)*一页准备显示的数量
- 结束范围: 小于等于 当前页数*一页准备显示的数量
查询员工表格(id) , 分页显示,一页显示10个:
查询第一页:
select id from s_emp where id>0 and id<=10;
查询第二页:
select id from s_emp where id>10 and id<=20;
查询第三页:
select id from s_emp where id>20 and id<=30;
通过行号来进行分页查询 ***
上述的案例 , 在一些特殊情况(id不连续)下 出现了错误
伪列: 在表格中不存在, 但是可以使用的列 !
行号(rownum) 结果集中当前行的 行号
-
尝试查询行号:
select rownum from s_emp;
-
查询第一页
select id from s_emp where rownum>0 and rownum<=10;
-
查询第二页
select id from s_emp where rownum>10 and rownum<=20;
行号在查询 比较时, 存在一个特点: 逐行判断, 当某一行数据不匹配时, 流程结束 !
那么这时就无法通过rownum来进行分页查询 ,因为查询第一页以后的数据, 都查询不到 !
可以借助子查询 / 视图 将rownum这个伪列, 变成一个存在的列 ! 来完成分页 !
查询第二页: *****
1. 子语句:
select rownum r,id from s_emp;
2. 编写select语句 查询上面的子语句
select r,id from (select rownum r,id from s_emp) where r>10 and r<=20;
排序分页查询 *****
三层嵌套子查询:
- 最内层查询 : 查询所有行的数据, 并进行排序
- 中间层查询 : 查询排序后的数据, 给行号起别名, 将行号变为 '存在的' 列! 并进行部分数据的过滤
- 最外层查询 : 使用中间层的 行号别名 进行数据的过滤 ,实现分页!
查询s_emp表格(id,salary) , 一页显示10个 ,根据salary升序排序 (第二页):
最内层: select id,salary from s_emp order by salary;
中间层: select id,salary,rownum r from (最内层) where rownum<=20;
最外层: select id,salary from(中间层) where r>10;
语句:
select id,salary from(select id,salary,rownum r from (select id,salary from s_emp order by salary) where rownum<=20) where r>10;
数据库表格设计范式
第一范式: 数据库表格中的列 不可再分 , 一行中单列的值只有一个!
第二范式: 要求,数据库中的表格在设计时, 表中的每一行数据 都可以被唯一区分 !(表格必须存在主键)
第三范式: 消除依赖传递: 满足第三范式, 必须先满足第二范式!
主外键关联时, 应只关联主表中的主键字段!