Oracle笔记2-数据库设计

数据库的设计

软件开发的流程:
立项->需求分析->概要设计->详细设计->实现->测试->交付->维护
[含数据库设计]

通过需求分析,就可以抽取出关键业务中的"实体类",这些实体类都是
用来封装数据的,这些数据最终都是要存储到数据库中.


数据库设计的基本流程:
根据业务的需求找出相应的实体类,然后把这些实体类映射成数据库
对象(表),再考虑实体的属性,也就是表格的列,最后考虑实体之间的
关系,也就是表格之间的关系.


数据库设计的常用工具:
1.sybase公司,PowerDesigner,功能强大,收费
2.ERWIN-Studio,功能比较强大,收费
3.JUDE,日本开发的开源,免费

E-R图(Entity-Relationship),实体关系映射图

它的核心就是映射(mapping):
1.实体名,映射成表名
2.属性,映射成列名
3.对象标示符,映射成主键约束
4.实体关系,映射成表之间的关系(外键约束)

映射举例:
实体类 表
名字 User 表名:tbl_user
属性 id 主键:id
属性 name 列名:name
属性 password 列名:password


实体类之间的关系:
一对一 映射成外键
一对多 映射成外键
多对多 拆分成2个一对多,再映射成外键
自关联 映射成外键

注意:
1.外键一般出现在表多的一方,映射具有方向性.
2.实体类中没有外键的概念,数据库表中才有外键的概念.
-----------------------------------------------------------

数据库的三大范式,也就是数据库设计的原则:

作用:是用来指导数据库设计人员的一种设计思想,保证数据库的设计
是具备可拓展性,无数据的冗余,结构清晰简单明了.

1NF:原子性,也就是说表中任何一个列都是唯一的,不可再拆分的
如:
R(id,name,age),此关系中name可以分为first_name和last_name,
所以,此设计不符合1NF,应该重新设计为:
R(id,first_name,last_name,age)


2NF:在1NF的基础上,不存在非关键列部分依赖于关键列,也就是说
所有的非关键列都必须完全依赖于关键列.
如:
R(sno,sname,cname,score)
1 张三 数学 90
由于以上不管是以哪一列作为关键列,都存在其他非关键列部分依赖
于关键列,所以此设计不符合2NF,应该重新设计为:
R1(sno,sname),存放学生信息的表
R2(cno,cname),存放课程信息的表
R3(sno,cno,score),存放学生与课程考试成绩的中间表


3NF:在2NF的基础上,不存在非关键列传递函数依赖于关键列,也就是
说,所有的非关键列都必须直接依赖于关键列
如:
如果A依赖于B,B依赖于C,我们就说A传递函数依赖于C

学生考入某所大学的信息表:
R(sno,sname,uno,uname,phone,address)
如果以sno作为关键列,sanme,uno是直接依赖于sno,但是uname,phone,
address这三列都是直接依赖于uno,间接依赖于sno,所以此设计不符合
3NF,应该重新设计为:
R1(sno,sname,uno),存放学生信息的表
R2(uno,uname,phone,address),存放大学信息的表
------------------------------------------------------------

DDL(create,drop,alter,rename to)

创建表格分为2个步骤:
1.定义列和数据类型
2.添加约束


约束(constraint)
作用:相对于数据类型而言,用来进一步限定表中数据,使得添加到
表中的数据都是合法有效,符合业务需求的数据,不会出现无效数据.

Oracle中的5种约束类型:
1.primary key PK P 含义:主键约束,非空唯一(表中最多只能含有一个主键约束)
2.not null NN C 含义:非空
3.unique UK U 含义:唯一
4.check CK C 含义:自定义约束
5.foreign key FK R 含义:外键约束(表示此列的值是引用自其它表的主键)

注意:能够被外键所引用的列,其本身也必须是主键约束或者唯一性约束.


创建表格的第一种方法:
语法
create table table_name(
列名 数据类型 [default 默认值] [约束],
列名 数据类型 [default 默认值] [约束],
...
列名 数据类型 [default 默认值] [约束]
);


约束的名字:
在创建表格时,如果不定义约束的名字,则数据库会给约束提供默认名,
并且把这些内容存储到数据字典中

--查询当前用户下所有的约束名
select constraint_name from user_constraints;


添加约束名字的语法:
constraint 约束名 约束类型;

约束名的命名规则:
table_name_列名_约束类型缩写


例如:
--创建用户表
create table tbl_user(
id number(5) constraint tbl_user_id_pk primary key,
username varchar2(25) constraint tbl_user_uname_uk unique not null,
password varchar2(25) default '000000'
);


添加约束的2种方法:
1.列级语法添加--边定义列,边添加约束

--创建账户表
create table tbl_account(
id number(5) constraint tbl_acc_id_pk primary key,
accountno varchar2(25) constraint tbl_acc_accno_uk unique not null,
realname varchar2(25),
password char(6) default '000000',
balance number(10,2) constraint tbl_acc_bal_ck check(balance>=0)
);

2.表级语法添加--先定义列,然后再添加约束
create table tbl_account(
id number(5),
accountNo varchar2(25) constraint tbl_acc_ano_nn not null,
realName varchar2(25),
password char(6) default '000000',
balance number(10,2),
constraint tbl_acc_id_pk primary key(id),
constraint tbl_acc_ano_uk unique(accountNo),
constraint tbl_acc_bal_ck check(balance>=0)
);

注意:not null约束不支持表级语法,只能采用列级语法添加!!!
当然,也可以采用如下方法变通实现
create table tbl_account(
id number(5),
accountNo varchar2(25),
realName varchar2(25),
password char(6) default '000000',
balance number(10,2),
constraint tbl_acc_id_pk primary key(id),
constraint tbl_acc_ano_nn check(accountNo is not null),
constraint tbl_acc_ano_uk unique(accountNo),
constraint tbl_acc_bal_ck check(balance>=0)
);


--创建员工表(自关联)
create table tbl_emp(
id number(5),
name varchar2(25) constraint tbl_emp_name_nn not null,
title varchar2(25),
salary number(10,2),
manager_id number(5),
constraint tbl_emp_id_pk primary key(id),
constraint tbl_emp_title_ck check(title in('经理','办事','销售','财务')),
constraint tbl_emp_sal_ck check(salary>1850),
constraint tbl_emp_mgr_fk foreign key(manager_id)
references tbl_emp(id)
);


补充:
创建表格的第二种方法(根据已经存在的某张表创建一张新表格):

--复制了某张表格的指定列,构建一张新表格(拷贝了数据)
create table 新表格名 as select 列,列,... from 原表格;

例如:创建一张s_emp表格中id,first_name,salary三列数据的新表
create table new_emp as select id,first_name,salary from s_emp;


--复制了某张表格的指定列,构建一张新表格(不拷贝数据,取表格结构)
create table 新表格名 as select 列,列,... from 原表格
where 恒假条件;

例如:创建一张s_emp表格中id,first_name,salary三列的新表,不需要数据
create table new_emp as select id,first_name,salary from s_emp
where 1=2;

注意:此处1=2表示恒false,则数据不会被拷贝,只能得到一张空表

删除表格(注意约束控制):

drop table table_name [cascade constraints];

注意:cascade constraints表示连带约束一起删除,如果不添加,
则如果有外键引用,则删除不成功.


修改表格:

--列相关
1.添加列
alter table 表名 add 列名 数据类型 默认值 约束;

--给tbl_user表添加一列(age)
alter table tbl_user add age number(3) default 18 not null;

2.删除列
alter table 表名 drop column 列名;

--删除tbl_user表中的age列
alter table tbl_user drop column age;

3.修改列(修改列的数据类型和约束)
alter table 表名 modify 原列名 新数据类型 新默认值 新约束;

--修改tbl_user表中password列为char(6) 默认值'00000' 非空
alter table tbl_user modify password char(6) default '000000' not null;

4.修改列名
alter table 表名 rename column 原列名 to 新列名;

--修改tbl_user表中password列名为pwd
alter table tbl_user rename column password to pwd;


--约束相关
1.添加约束
alter table 表名 add constraint 约束名 约束类型(列名);

注意:如果是添加非空约束,则:
alter table 表名 add constraint 表名_列名_nn check(x is not null);

**********************************************************************
--删除表格
drop table tbl_emp;

--创建表格
create table tbl_emp(
id number(5),
name varchar2(25) constraint tbl_emp_name_nn not null,
title varchar2(25),
salary number(10,2),
manager_id number(5)
);

--添加约束
alter table tbl_emp add constraint tbl_emp_id_pk primary key(id);
alter table tbl_emp add constraint tbl_emp_title_ck check(title in('经理','办事','销售','财务'));
alter table tbl_emp add constraint tbl_emp_sal_ck check(salary>1850);
alter table tbl_emp add constraint tbl_emp_mgr_fk foreign key(manager_id)
references tbl_emp(id);

--初始化数据
insert into tbl_emp values(...);
insert into tbl_emp values(...);
insert into tbl_emp values(...);

--提交数据
commit;

**********************************************************************

2.删除约束
alter table 表名 drop constraint 约束名;

3.使约束生效
alter table 表名 enable constraint 约束名;

4.使约束失效
alter table 表名 disable constraint 约束名;

------------------------------------------------------------

DML(insert,update,delete)

1.插入数据
insert into 表名(列,列,列,...) values(值,值,值...);

//当插入的数据时与表格列一一对应的话,则列可以省略
insert into 表名 values(值,值,值...);

例如:
--给tbl_user表添加一条记录
insert into tbl_user (id,username,password) values(1,'jack','123456');
insert into tbl_user values(2,'tom','456789');


2.更新数据
update 表名 set 列=新值 where 条件;

例如:
--修改tbl_user表中第二行记录中的用户名
update tbl_user set username='ben' where id=2;

--提高当前订单表中所有状态是未付款的订单价格10%
update tbl_order set price=price*1.1 where status='unpayment';

注意:修改操作千万要注意条件!!!


3.删除数据
delete 表名 where 条件;

例如:
--删除用户表第三行数据
delete tbl_user where id=3;

注意:此处不能违反约束

---------------------------------------------------------

DML语句和DDL语句的不同:

1.DML语句不会自动提交,也就是说当运行完DML语句后,数据库中
真实的数据还没有发生变化,当前自己事务中看到的仅仅是内存
中的情况,所以此时,另一个事务是无法看到修改结果的.

如果要把修改后的结果同步到数据库中,则必须手动使用如下指令:

--提交数据,把内存中的数据同步到数据库中
commit;

--回滚操作,撤销还没有提交的操作
rollback [to 回滚点名字];

--设置回滚点
savepoint 回滚点名字;

即:一个事务无法读取到另一个事务还没有提交的数据!!!

注意:navicat中默认情况下DML语句也会自动提交


2.DDL语句是自动提交的

---------------------------------------------------------

数据字典

定义:数据字典就是用来描述用户对象的对象.

Oracle是一个用表(系统表)来维护表(用户表)的数据库系统,这里
的系统表就是我们所说的数据字典.

Oracle数据库定义的数据字典都遵循一定的命名规则,它内置了上
千张表.

命名规则:
前缀_数据库对象+s/es

常用的前缀:
user
all
dba
$

常用的数据库对象:
table,user,constraint,sequence,index,view...

常用的数据字典:
user_users 存放用户的信息
user_tables 存放用户表的信息
user_constraints 存放表中约束的信息
user_sequences 存放序列的信息
user_indexes 存放索引的信息
user_views 存放视图的信息
user_cons_columns 存放约束与列对应关系的信息


--查看当前用户下有哪些约束
select * from user_constraints;

--查看当前用户下有哪些表格
select table_name from user_tables;

--查看TBL_EMP表中所有的约束名和约束类型
select constraint_name,constraint_type from user_constraints
where table_name='TBL_EMP';

--查看当前用户(jsd1510)下所有状态为不可用的约束名字,以及该约束作用的表格名
select constraint_name,table_name,status from user_constraints
where status='DISABLED' and owner='JSD1510';


注意:此处表名一定要大写!
-------------------------------------------------------------

DTL,数据事务语言

事务的定义:
就是指一组相关的SQL操作,我们所有的操作都是处在事务中的.

注意:在数据库中,执行业务的基本单位是事务,不是以某一条SQL.
数据库在默认情况下,事务都是打开的,也就是说它是一直
处在事务中的,一个事务的结束,代表着下一个事务的开启.
执行commit或者rollback指令时,会结束当前事务.

作用:用来保证数据的平稳性和可预测性.

例如:银行转账业务

A帐号向B帐号转账10000
SQL1:
update tbl_account set balance=balance-10000 where accountNo=A帐号;

SQL2:
update tbl_account set balance=balance+10000 where accountNo=B帐号;

SQL1和SQL2必须处在同一个事务中,从而保证同时成功或者同时失败.


事务的四大特性(ACID):
atomic,原子性,事务是不可再分割的,要么同时成功,要么同时失败
consistency,一致性,事务一旦结束,内存中的数据和数据库中的数据是保持一致的
isolation,隔离性,事务之间互不干扰,一个事务的结束意味着下一个事务的开启
duration,持久性,事务一旦提交,则数据持久化到数据库中,永久保存

在Oracle中,操作事务的命令:
1.commit,提交事务
把事务中所有的数据持久化到磁盘中

2.rollback [TO 回滚点],回滚事务
把事务中所做的操作全部取消,回到初始状态

3.savepoint 回滚点,设置回滚点
事务回滚时,回到的起点


总结:
1.目前主流的数据库都是支持事务的,而且其中Oracle支持的最好,
2.一个事务不能读取到另一个事务还没有提交的数据.
3.DDL语句都会自动提交事务
4.DML语句不会自动提交事务,需要手动commit
---------------------------------------------------------

多事务的并发处理机制:

原因:多个事务同时操作一个表中的同一行数据,如果这些操作是
修改操作的话,就会产生并发问题,如果不处理,则会造成数
据不一致的情况.

数据库可能产生的并发问题包括:
1.脏读
是指一个事务正在访问数据,并且对这个数据进行修改,而这种修改
还没有提交到数据库中,而另一个事务也访问了这个数据,并且使用
了这个数据.

解决方法:一个事务在修改数据时,该数据不能被其他事务访问

2.不可重复读
是指一个事务多次读取同一条记录,如果此时另一个事务也访问并且
修改了该数据,则就会出现多次读取出现数据不一致的情况,原来的
数据变成了不可重复读取的数据

解决方法:只有在修改事务完全提交过后才可以读取到数据

3.幻读
是指一个事务修改表中的多行记录,但是此时另一个事务对该表格进行
了插入数据的操作,则第一个事务会发现表格中会出现没有被修改的行,
就像发生了幻觉一样.

解决方法:在一个事务提交数据之前,其他事务不能添加数据


Oracle中采用'锁'来做并发处理:
1.表级排它锁(X) exclusive mode
2.表级共享锁(S) share mode
3.表中行级排它锁(SRX) share row exclusive
注:这三种锁是通过专门的命令来申请的

语法:
lock table table_name in mode;

例如:
--以共享锁锁表
lock table tbl_emp in share mode;

--以排它锁锁表
lock table tbl_emp in exlusive mode;

4.行级共享锁(RS) row share
5.行级排它锁(RX) row exclusive
注:这两种锁无需通过专门命令申请,而是通过DQL和DML来自动申请的

注意:
1.所有的DQL语句默认情况下都会自动申请RS锁
2.所有的DML语句默认情况下都会自动申请RX锁,每一行记录都会唯一的RX锁
3.在项目中,为了满足业务要求,一般select语句需要申请RX锁

select语句通过for update来申请RX锁:
select * from s_emp for update;
select * from s_emp for update wait 5;//等待5秒钟
select * from s_emp for update nowait;
-----------------------------------------------------------

其他数据库对象:
序列(SEQUENCE)
索引(INDEX)
视图(VIEW)

1.序列(SEQUENCE)
对应的数据字典:user_sequences

作用:用来产生唯一性值的数据库特殊对象

创建序列的语法:
create sequence 序列名
[start with n] --表示从几开始,默认值是1
[increment by n] --每计数一次增加多少,默认是1
[MAXVALUE n] --序列最高峰值n
[MINVALUE n] --序列最低峰值n
[cache n] --提供n个预分配的序列,保存在内存中
[cycle | nocycle] --是否循环
[order | noorder] --有序还是无序序列

例如:
--给员工表创建一个序列
create sequence tbl_emp_id start with 4;


如何使用序列?
nextval:取序列的下一个值(tbl_emp_id.nextval)
currval:取序列的当前的值(tbl_emp_id.currval)

在插入数据时使用:
insert into tbl_emp values(tbl_emp_id.nextval,.........);

删除序列:
drop sequence 序列名;

补充:MySQL
create table tbl_user(
id int auto increment,
username varchar(25),
password varchar(25)
);
---------------------------------------------------------

2.索引(INDEX)
对应的数据字典:user_indexes

它是一个比较重要的数据库对象,作用是可以有效的提高数据库的
查询效率 (数据库性能优化).

创建索引的2种方式:
1.自动创建
当表中的列添加了主键约束或者唯一性约束时,则系统会自动为此列
创建唯一性的索引,索引名就是约束名.

2.手动创建
语法:
create index 索引名 on 表名(列名...);

注意:
1.索引有自己独立的存储空间和命名空间
2.创建索引也会相对牺牲一些数据库性能


索引的原理:
1.默认情况下,索引是采用BTree(二叉树)的数据结构
2.伪列(rowid),存放的数据行记录的真正"物理地址".

--根据物理地址查询某一行记录
//先获取行记录的rowid
select rowid from s_emp where id=1;//AAADXqAABAAAI16AAA

//根据行记录rowid查找相应的记录
select * from s_emp where rowid='AAADXqAABAAAI16AAA';


==>子查询
select * from s_emp where rowid=
(select rowid from s_emp where first_name='Carmen');


3.索引建立的原理:
把创建索引的列值与rowid合成一个键值对,这个键值对就是索引,
然后把它们存放到指定的数据结构中(二叉树,位图)中,并且是独
立的索引空间.

4.索引查询的原理:
当我们的查询语句中where条件的列建立了索引,则查询分为以下2步:
a.先查索引,在句列中的值直接找到rowid
b.根据第一步得到的rowid直接定位到相应的行记录结束查询

5.建立索引的策略:
a.主键列和唯一性列 适合
b.不经常发生改变的列 适合
c.满足以上2个条件,经常作为查询条件的列 适合
d.重复值太多的列 不合适
e.null值太多的列 不合适

6.删除索引
drop index 索引名;

-----------------------------------------------------------

视图(VIEW)
对应的数据字典:user_views

它是一个数据库对象,它的表格的一个"窗口",用来保存查询语句
的对象,视图时依附于表的,并且他与表格共享存储空间.

定义:本质就是一条合法的查询语句


作用:
1.配合权限,根据业务来做分级管理
2.减少复杂性,增强数据的安全性


创建视图的语法:
create [or replace] view 视图名 as 字句;
[with read only] -- 此视图只读
[with check option [constraint 约束名]]

例如:
create view view_name as
select id,salary from s_emp with read only;

注意:
操作视图必须拥有一定的权限!!!


删除视图:
drop view 视图名;


视图的分类:
关系视图,内嵌视图,对象视图,物化视图

----------------------------------------------------------

补充:
--查询当前用户可执行的主要操作
select * from session_privs;

--查询某个权限可执行的所有操作
select * from DBA_SYS_PRIVS where grantee='DBA';(需要DBA)

--查询当前用户被赋予的系统角色
select * from SESSION_ROLES order by role;

授予权限的两种方式:
1.grant create any view to 用户名;
2.grant connect,resource,dba to 用户名;


练习:
--创建41部门所有员工的简单视图(必须拥有操作视图的权限!)
create view view_emp_41 as select * from s_emp where dept_id=41;

原文地址:https://www.cnblogs.com/tarek/p/5590533.html