Oracle总结
一.用户的有关操作。
- 创建用户
1
|
create user scott
identified by 123456; |
- 给用户分配权限
1
2
3
|
grant connect ,resource to scott; grant create view to scott; grant create synonym to scott; |
- 撤销用户权限
1
2
3
|
revoke connect ,resource from scott; revoke create view from scott; revoke create synonym from scott; |
- 删除用户
1
|
drop user scott cascade ; |
- 修改用户密码
1
2
3
4
5
6
7
|
alter user scott
identified by 123456; --命令修改 conn
scott/123456 password ; --命令可视化修改1 connect scott/123456 password ; --命令可视化修改2 |
- 设置用户是否锁定
1
2
|
alter user scott
account lock; alter user scott
account unlock; |
二.表空间的有关操作。
- 创建表空间
1
2
3
4
|
create tablespace
mysapce datafile 'D:a.ora' size 10M --绝对路径和大小 extent
management local uniform size 1M; --每个分区的大小 |
- 扩展表空间
1
2
|
alter tablespace
mysapce add datafile 'D:b.ora' size 10M; |
- 为ORACLE对象指定表空间
1
2
|
create user space_text
identified by 123456
account unlock default tablespace
mysapce; --创建表、索引也可以指定表空间;一旦指定,表空间无法修改。 |
- 删除表空间
1
|
drop tablespace
mysapce; |
三.DDL的有关操作。
- 表table
1
2
3
4
5
6
7
8
9
10
11
|
--创建员工表 CREATE TABLE EMP( EMPNO
NUMBER(4) CONSTRAINT PK_EMP PRIMARY KEY , ENAME
VARCHAR2(10), JOB
VARCHAR2(9), MGR VARCHAR (10), --上司 HIREDATE DATE , --入职日期 SAL
NUMBER(7,2), --薪水 COMM
NUMBER(7,2), --津贴 DEPTNO
NUMBER(2) CONSTRAINT FK_DEPTNO REFERENCES DEPT ); |
1
2
3
4
5
6
|
--创建部门表 CREATE TABLE DEPT( DEPTNO
NUMBER(2) CONSTRAINT PK_DEPT PRIMARY KEY , DNAME
VARCHAR2(14) , LOC
VARCHAR2(13) --地址 ); |
1
2
3
4
5
6
|
--创建工资等级表 CREATE TABLE SALGRADE( GRADE
NUMBER, --等级 LOSAL
NUMBER, --等级中最低的薪水 HISAL
NUMBER --等级中最高的薪水 ); |
- 视图view
1
2
3
4
5
6
7
8
|
--为emp表的empno,ename,sal和dept表的dname和salgrade表的grade创建一个视图 create view emp_dept_salgrade as select e.empno,e.ename,e.sal,d.dname,s.grade from emp
e inner join dept
d using(deptno) inner join salgrade
s on e.sal between s.losal and s.hisal; select * from emp_dept_salgrade; --通过视图查询 |
- 序列sequence
1
2
3
4
5
6
7
8
9
10
|
--为员工表的EMPNO创建一个序列 create sequence emp_empno_seq start with 1001 increment by 1 nomaxvalue nocycle cache
10; select emp_empno_seq.currval from dual;<span
style= "color:
#008000;" >查询序列的当前值</span> select emp_empno_seq.nextval from dual;<span
style= "color:
#008000;" >查询序列的下一个值</span> |
- 同义词synonym
1
2
3
4
5
6
|
--为视图emp_dept_salgrade创建同义词 create synonym
eds for emp_dept_salgrade; select * from eds;<span
style= "color:
#008000;" >通过视图的同义词来查询视图中的数据 </span> |
- 触发器trigger
1
2
3
4
5
6
7
|
--为员工表的empno创建一个自动插入的触发器 create or replace trigger emp_empno_tri before insert on emp for each
row begin :new.empno:=emp_empno_seq.nextval;<span
style= "color:
#008000;" > --语句级(for
each row)触发器里面可以:new.列名来给进行操作。</span> end ; |
- 存储过程procedure
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
--创建一个可以控制行数的乘法表的过程。 create or replace procedure nine_nine(nine_line in number) as begin for i in 1..nine_line
loop for j in 1..i
loop dbms_output.put(i|| '*' ||j|| '=' ||i*j|| '
' ); end loop; dbms_output.put_line( '' ); end loop; end ; --调用这个乘法过程 set serveroutput on ; execute nine_nine(9); |
- 存储函数function
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
--创建一个求1!+2!+..+20!的值的存储函数 create or replace function one_tw return number as value_sum
number:=0; value_loop
number:=1; begin for i in 1..20
loop value_loop:=value_loop*i; value_sum:=value_sum+value_loop; end loop; return value_sum; end ; select one_tw() from dual;<span
style= "color:
#008000;" > --调用函数</span> <span
style= "color:
#008000;" >备注:存储函数的调用可以放在表达式的位置,即表达式在哪里成立,它就可以在哪里调用。</span> |
- 事务rollback、commit、savepoint
三.常用的结构查询。
- 查询用户和用户的信息
1
2
|
select username,user_id, password ,default_tablespace from dba_users; select * from dba_users; |
- 查询用户所拥有的角色
1
2
|
select * from user_role_privs; --系统用户 select * from session_roles; --普通用户 |
- 查询用户的权限
1
|
select * from user_sys_privs;普通用户和系统用户都可以 |
- 查看表中列的字符长度和字节长度
1
|
select length(ename),lengthb(ename) from emp; |
- 查询表的相关信息
1
2
|
SELECT table_name,
tablespace_name, temporary FROM user_tables; |
- 查询表中列的相关信息
1
2
|
SELECT table_name,column_name,
data_type, data_length, data_precision, data_scale FROM user_tab_columns; |
- 对表进行重命名
1
|
rename
student to mystudent; |
- 给表添加备注
1
|
comment on table student is '我的练习' ; |
- 给表中列添加备注
1
|
comment on column student.sno is '学生号' ; |
- 查看表和视图的备注信息
1
|
select * from user_tab_comments where table_name= 'STUDENT' ; |
- 查看表和视图中列的备注信息
1
|
select * from user_col_comments where table_name= 'STUDENT' ; |
- 查看表的结构
1
|
describe
student; |
- 截断表
1
|
truncate table student; |
- 使用连接运算符
1
2
|
select empno||ename as employees from emp; select concat(empno,ename) as employees from emp; |
- 查看表的约束信息
1
|
select * from user_constraints where table_name= 'EMP' ; |
- 查看列的约束信息
1
|
select * from user_cons_columns where column_name= 'SNO' ; |
- 查看序列的信息
1
|
select * from user_sequences where sequence_name= 'EMP_EMPNO_SEQ' ; |
- 查看索引的信息
1
|
select * from user_indexes; |
- 查看视图的信息
1
|
select * from user_views; |
- 查看同义词
1
|
select * from user_synonyms; |
- 查看触发器
1
|
select * from user_triggers; |
- 查看存储过程
1
|
select * from user_procedures; |
四.DML的有关操作。
- 插入数据insert
1
2
3
4
5
6
7
8
9
10
11
|
--dept-- INSERT INTO DEPT select 10, 'ACCOUNTING' , 'NEW
YORK' from dual union select 20, 'RESEARCH' , 'DALLAS' from dual union select 30, 'SALES' , 'CHICAGO' from dual union select 40, 'OPERATIONS' , 'BOSTON' from dual; commit ;
<span style= "color:
#008000;" > --使用Oracle中的多行插入方法,关键字union,select自己想要的数据,与dual伪表组建一个完整的结构。 </span> |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
--emp-- INSERT INTO EMP(ename,job,mgr,hiredate,sal,comm,deptno) select 'SMITH' , 'CLERK' ,1009,to_date( '17-12-1980' , 'dd-mm-yyyy' ),800, NULL ,20 from dual union select 'ALLEN' , 'SALESMAN' ,1006,to_date( '20-2-1981' , 'dd-mm-yyyy' ),1600,300,30 from dual union select 'WARD' , 'SALESMAN' ,1006,to_date( '22-2-1981' , 'dd-mm-yyyy' ),1250,500,30 from dual union select 'JONES' , 'MANAGER' ,1009,to_date( '2-4-1981' , 'dd-mm-yyyy' ),2975, NULL ,20 from dual union select 'MARTIN' , 'SALESMAN' ,1006,to_date( '28-9-1981' , 'dd-mm-yyyy' ),1250,1400,30 from dual union select 'BLAKE' , 'MANAGER' ,1009,to_date( '1-5-1981' , 'dd-mm-yyyy' ),2850, NULL ,30 from dual union select 'CLARK' , 'MANAGER' ,1009,to_date( '9-6-1981' , 'dd-mm-yyyy' ),2450, NULL ,10 from dual union select 'SCOTT' , 'ANALYST' ,1004,to_date( '13-10-87' , 'dd-mm-rr' )-85,3000, NULL ,20 from dual union select 'KING' , 'PRESIDENT' ,1007,to_date( '17-11-1981' , 'dd-mm-yyyy' ),5000, NULL ,10 from dual union select 'TURNER' , 'SALESMAN' ,1006,to_date( '8-9-1981' , 'dd-mm-yyyy' ),1500,0,30 from dual union select 'ADAMS' , 'CLERK' ,1009,to_date( '13-10-87' , 'dd-mm-rr' )-51,1100, NULL ,20 from dual union select 'JAMES' , 'CLERK' ,1009,to_date( '3-12-1981' , 'dd-mm-yyyy' ),950, NULL ,30 from dual union select 'FORD' , 'ANALYST' ,1004,to_date( '3-12-1981' , 'dd-mm-yyyy' ),3000, NULL ,20 from dual union select 'MILLER' , 'CLERK' ,1004,to_date( '23-1-1982' , 'dd-mm-yyyy' ),1300, NULL ,10 from dual; commit ;
<span style= "color:
#008000;" > --这里使用了触发器emp_empno_tri来自动插入emp表的empno员工编号</span> |
1
2
3
4
5
6
7
|
--salgrade-- INSERT INTO SALGRADE VALUES (1,700,1200); INSERT INTO SALGRADE VALUES (2,1200,1400); INSERT INTO SALGRADE VALUES (3,1400,2000); INSERT INTO SALGRADE VALUES (4,2000,3000); INSERT INTO SALGRADE VALUES (5,3000,9999); commit ; |
- 更新数据update
1
|
update emp set sal=3000 where empno=1004; |
- 删除数据delete
1
|
delete from emp where empno=1004;<span
style= "color:
#008000;" > --from可以省略</span> |
- 查询数据select
查询数据是DML语句中最关键的部分,也是最难的部分,在这里有许多围绕scott用户的实例,都是稍微复杂一点的查询,简单的就没必要写了。
1.最常用。
1
2
3
|
select * from emp; select * from dept; select * from salgrade; |
2.内部连接。
2-1.查询每个员工所在的部门,使用where连接.
1
|
select e.empno,e.ename,d.dname from emp
e,dept d where e.deptno=d.deptno; |
2-2.inner join on连接.
1
|
select e.empno,e.ename,d.dname from emp
e inner join dept
d on e.deptno=d.deptno; |
2-3.inner join using连接.
1
|
select e.empno,e.ename,d.dname from emp
e inner join dept
d using(deptno); |
3.外部连接。
3-1.左外连接:例如:查询出部门的员工的情况(显示所有部门).
1
|
select e.ename,d.dname from emp
e left join dept
d using(deptno); |
3-2.右外连接用(+).
1
|
select e.ename,d.dname from emp
e,dept d where e.deptno=d.deptno(+); |
3-3.右外连接:例如:查询出所有的员工的部门情况(显示了所有员工).
1
|
select e.ename,d.dname from emp
e right join dept
d using(deptno); |
3-4.右外连接用(+).
1
|
select e.ename,d.dname from emp
e,dept d where e.deptno(+)=d.deptno; |
4.自连接。
4-1.查询出员工及他的上级。
1
2
3
|
select a.ename as 员工,b.ename as 上级 from emp
a ,emp b where a.mgr=b.empno; select a.ename as 上级,b.ename as 上级 from emp
a inner join emp
b on a.mgr=b.empno; |
5.子查询。
5-1.查询工资高于平均工资的员工信息.
1
|
select * from emp where sal>( select avg (sal) from emp); |
5-2.使用ANY查询任意满足工资低于最低档工资的员工信息.
1
|
select * from emp where sal< any ( select losal from salgrade); |
5-3.查询所有员工所属部门.
1
|
select dname from ( select distinct dname from dept); |
5-4.查询满足大于每个部门的最低工资的员工信息.
1
|
select * from emp where sal> all ( select min (sal) from emp group by deptno); |
5-5.查询出每个部门中,高出本部门平均工资的员工的雇员号和姓名.
1
2
|
select empno as 雇员号,ename as 姓名 from emp outer where sal> ( select avg (sal) from emp inner where inner .deptno= outer .deptno
); |
5-6.查询不在部门10的员工信息:注意子查询中的1,由于只关心子查询是否返回TRUE值,使用1可以提高查询的效率.
5-6.1.EXISTS子查询效率高于IN子查询.
1
|
select * from emp
a where not exists
( select 1 from emp
b where a.deptno=10); |
5-6.2.in的效率低,但比较好理解.
1
|
select * from emp where deptno not in 10; |
5-7.查询emp表中可以管理别的员工的员工.
1
|
select ename from emp
a where exists( select ename from emp
b where a.empno=b.mgr); |
5-8.删除中部门重复行.
1
|
delete emp where rowid not in ( select min (rowid) from emp group by deptno); |
5-9.查找emp表第6-10条记录.
1
2
|
select * from ( select rownum
m,ename,sal,deptno from emp where rownum<=10) where m>5; |
ATM取款机的数据库模拟开发和实战总结
一.ATM实战开发的简介。
学习了几天的Oracle,开始着手用数据库PL/SQL语言做一个简单的ATM取款机业务,主要是为了巩固数据库的知识,并非真正的去实现高端的业务。有兴趣的可以看看,希望对同胞们都有用。
- ATM的表。它有四张表,用户信息表userinfo,卡号信息表cardinfo,交易信息表tradeinfo,存款类型表deposit。
用户信息表userInfo | |||
customerID | int | 客户编号 | 主键 |
customerName | varchar(10) | 用户名 | not null |
personID | varcahr(20) | 身份证号 |
not null unique 只能是15位 或者18位符合实际的身份证号 |
telephone | varcahr(20) | 联系电话 |
not null,格式为xxxx-xxxxxxxx或者xxx-xxxxxxxx 或者11手机号 |
address | varchar(30) | 居住地址 | 可选 |
1
2
3
4
5
6
7
8
9
10
11
12
13
|
--创建userinfo表-- create table UserInfo( customerID int primary key , customerName varchar (10) not null , personID varchar (20) not null unique , telephone varchar (20) not null , address varchar (30) ); --为身份证号和电话增加正则表达式约束-- alter table userinfo add constraint CK_TELEPHONE check (regexp_like(telephone, '^1[3,4,5,7,8][0-9]-[0-9]{8}$|^[0-9]{3,4}-[0-9]{8}$' )); alter table userinfo add constraint CK_PERSONID check (regexp_like(personid, '^[0-9]{15}$|^[0-9]{17}[0-9,x]$' )); |
卡号信息表cardInfo | |||
cardID | varchar(30) | 卡号 |
主键,如1010 3576 xxxx xxxx, 每4位后面有空格,卡号随机产生。 |
curID | varchar(5) | 货币种类 | 必填,默认为RMB |
savingID | varchar(5) | 存款类型 | 外键,必填。 |
openDate | date | 开户日期 | 必填。默认为当前时间 |
openMoney | decimal(10,2) | 开户金额 | 必填,不低于1. |
balance | decimal(10,2) | 余额 | 必填,不低于1. |
pwd | varchar(10) | 密码 | 必填,6位数字。默认为888888 |
isReportLoss | char(2) | 是否挂失 | 必填,只能是'是'或'否'。默认为'否' |
customerid int 开户编号 外键,必填。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
--创建cardinfo表-- create table CardInfo( cardID varchar (30) primary key , curID varchar (5) default 'RMB' not null , savingID varchar (5) not null , openDate date default sysdate not null , openMoney decimal (10,2) not null check (openMoney>=1), balance decimal (10,2) not null check (balance>=1), pwd varchar (10) default '888888' not null , IsReportLoss char (2) default '否' not null , customerID int not null references UserInfo(customerID) ); --为卡号和密码增加正则表达式约束-- alter table cardinfo add constraint CK_PWD check (regexp_like(pwd, '^[0-9]{6}$' )); alter table cardinfo add constraint CK_CARDID check (regexp_like(cardid, '^1010[[:space:]]3576[[:space:]][0-9]{4}[[:space:]][0-9]{4}$' )); |
交易信息表tradeInfo | |||
transdate | date | 交易日期 | 必填。默认为系统时间 |
cardID | varchar(30) | 卡号 | 外键,必填。 |
transType | varchar(10) | 交易类型 |
必填,只能是存入或者支取 |
transmoney | decimal(10,2) | 交易金额 | 必填,大于0 |
remark | varchar(50) | 备注 | 可选 |
1
2
3
4
5
6
7
8
|
--创建tradeinfo表-- create table TradeInfo( transDate date default sysdate not null , cardID varchar (30) not null references CardInfo(cardID), transType varchar (10) not null , transMoney decimal (10,2) not null , remark varchar (50) ); |
--为transtype增加约束--
alter table tradeinfo add constraint CK_TRANSTYPE check (transtype in('支取','存入'));
存款类型表deposit | |||
savingID | int | 类型编号 | 主键 |
savingName | varchar(20) | 存款类型名称 | not null unique |
- ATM模拟实现的业务。
1.修改密码。
2.挂失。
3.查询本周开户的卡号。
4.查询本月一次性交易金额最高的卡号。
5.查询卡号挂失的用户的信息。
6.开户。
7.存款或者取款。
二.插入数据。
- 为deposit表插入数据。
插入数据前我们应该有这样一个认识,与客户无关的信息表先插入数据,与客户有关的次之。因为你开户的时候,客户存款类型必须与存款类型表deposit表的一条记录匹配才行。这就像一个银行一样,不管有多少客户,你银行本身的数据以及功能是必须有的。所以,先插入与银行的信息有关与客户无关的表的数据。
为deposit插入数据的时候,存款类型编号我们一般也不会自己去输入,所以要创建一个插入类型名称时,自动插入类型编号的触发器。
首先创建一个savingid的序列。
1
2
3
4
5
6
7
|
--创建savingid序列-- create sequence savingid_incr --创建序列不能加or
replace start with 1 increment by 1 nomaxvalue nocycle cache
30; |
然后创建savingid的触发器。
1
2
3
4
5
6
7
8
|
--创建savingid的触发器-- create or replace trigger savingid_insert before insert on deposit for each
row declare begin :new.savingid:=savingid_incr.nextval; end ; |
现在就可以插入数据了。
1
2
3
|
insert into deposit(savingname) values ( '定期' ); insert into deposit(savingname) values ( '活期期' ); insert into deposit(savingname) values ( '定活两便' ); |
检测数据。select * from deposit;可以看到三条savingid自动产生并插入的记录。
- 为userinfo表和cardinfo表插入数据:
对于userinfo表,在插入数据的时候,我们不可能每次都去插入客户编号,所以要为客户编号创建一个插入其他数据时的客户编号自动插入的触发器;还要为卡号创建一个随机产生的过程,并且开户的时候通过过程去插入,因为卡号是随机产生并且不能重复,所以我把这个判断写入了开户的业务逻辑中。
在这里的话,我直接去实现开户,然后通过开户为userinfo表和cardinfo表插入数据,东西很多,我们一步一步来。
先为customerid创建序列。
1
2
3
4
5
6
|
create sequence id_incr --创建序列不能加or
replace start with 1001 increment by 1 nomaxvalue nocycle cache
30; |
再为customerid创键触发器。
1
2
3
4
5
6
7
8
|
create or replace trigger id_insert before insert on userinfo for each
row declare next_customerid
userinfo.customerid%type; begin :new.customerid:=id_incr.nextval; end ; |
为cardid创建随机产生的过程。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
create or replace procedure r_cardid(out_id out varchar2) as r_num
number; front_id
varchar2(4); back_id
varchar2(4); real_id
varchar2(20); begin select lpad(trunc(dbms_random.value*100000000),8,0) into r_num from dual; front_id:=to_char(substr(r_num,1,4)); back_id:=to_char(substr(r_num,5,4)); real_id:= '1010
3576 ' ||front_id|| '
' ||back_id; out_id:=real_id; end ;<br> |
开户的时候,除了customerid和cardid以及表给的默认值,其他都是与用户开户有关的信息。所以准备工作做好之后,我们就可以实现开户的业务了。
*****************************************************开户******************************************************
******开户的数据*********
开户时需要用户输入的有:
身份证号——personid
存款类型——savingid
存款金额——openmoney
本卡密码——pwd
(在开户时如果用户是第一次开户的话,就又需要的输入的有:)
姓名——customername
联系方式——telephone
地址——address
系统自动赋予的值有:
用户号——customerid(触发器触发)
卡号——cardid(调用r_cardid(outid)过程)
其他都为系统给的默认值。
******开户的存储过程逻辑******
1.先判断用户是不是第一个开卡,如果是,那么先创建用户信息,再开户。
2.用户创建之后或者用户已存在时,调用卡号随机产生的存储过程,产生一个随机产生的不重复的卡号。
3.卡号产生之后,判断用户输入的存款类型是否正确,正确,就可以开户了。不正确则撤销之前所有的操作并且提示开户终止。
4.开户就是插入一条符合逻辑的cardinfo记录,并且提示开户的最终信息。
******开户的存储过程************
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
|
create or replace procedure openAccount( temp_personid in userinfo.personid%type, temp_savingname in deposit.savingname%type, temp_openmoney in cardinfo.openmoney%type, temp_pwd in cardinfo.pwd%type, temp_customername in userinfo.customername%type, temp_telephone in userinfo.telephone%type, temp_address in userinfo.address%type) as isnullpersonid
userinfo.personid%type; --select
into判断身份证号是否存在。 temp_cardid
cardinfo.cardid%type; --select
into判断新产生的卡号是否存在,并且在后来是要用的。 temp_savingid
cardinfo.savingid%type; --select
into 判断用户输入的存款类型是否可用。 temp_customerid
userinfo.customerid%type; begin begin --判断用户是否存在 select personid into isnullpersonid from userinfo where personid=temp_personid; exception when no_data_found then -----创建用户---- insert into userinfo(customername,personid,telephone,address) values (temp_customername,temp_personid,temp_telephone,temp_address); end ; begin while
1=1 loop --产生一个唯一不重复的卡号 r_cardid(temp_cardid); select cardid into temp_cardid from cardinfo where cardid=temp_cardid; --如果没有找到,则证明新产生的卡号是唯一的,进入exception继续完成操作。 end loop; exception when no_data_found then --来到这里说明产生的卡号是可用的,接下来就应该判断存款类型temp_savingid. begin select savingid into temp_savingid from deposit where savingname=temp_savingname; --如果存在,那么就可以开户了,如果不存在,则撤销之前的所有操作,用事务。 --customerid是之前就有或者在开户中自动产生的,所以这里要通过SQL找到它。 select customerid into temp_customerid from userinfo where personid=temp_personid; --开户--- insert into cardinfo(cardid,savingid,openmoney,balance,pwd,customerid) values (temp_cardid,temp_savingid,temp_openmoney,temp_openmoney,temp_pwd,temp_customerid); dbms_output.put_line( '
开户成功!' ); dbms_output.put_line( '您的银行卡号为:' ||temp_cardid); dbms_output.put_line( '开户日期:' ||sysdate|| '
开户金额 ' ||temp_openmoney); exception when no_data_found then rollback ; --撤销之前的所有操作 raise_application_error(-20000, '存款类型不正确,开户终止!' ); end ; end ; end ; |
***************利用开户存储过程来插入数据******************
插入一条用户第一次开户的数据:
set serveroutput on;
execute openAccount(410181199308084016,'定期',50000,762723,'徐万轩','151-03891462','河南省郑州市');
插入一条老用户开户的数据:
set serveroutput on;
execute openAccount(410181199308084016,'活期',50000,762723,'徐万轩','151-03891462','河南省郑州市');
此时查表就会发现有两条cardinfo记录,一条userinfo记录。
- 为tradeinfo表插入数据。
tradeinfo表示记录交易信息的,所以我们可以调用存取款的过程来为tradeinfo表插入数据。
*****************************************************存取款存储过程*******************************************
******存取款需要用户的数据**********
需要用户输入的信息:
存款或者取款的金额:temp_money
存款或者取款的类型:temp_transtype
银行卡号:temp_cardid
******存取款的实现逻辑*************
1.首先判断用户输入的卡号是否存在,不存在则退出操作。
2.卡号存在,再判断卡是否挂失,如果挂失,提醒并退出操作。
3.没有挂失的话,判断存取款类型是否正确,如果正确,就可以存取款了。
4.存取款时,更新cardinfo表卡的balance余额,并且插入一条交易信息表。
5.如果取款之后,余额小于1,就会发生检查约束错误,捕获错误并且利用事务的原理rollback之前的操作。
*******存取款的存储过程**************
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
|
create or replace procedure inout_money( temp_money in number, temp_transtype in tradeinfo.transtype%type, temp_cardid in cardinfo.cardid%type ) as isnulltranstype
tradeinfo.transtype%type; --判断存取款类型是否正确 isnullcardid
cardinfo.cardid%type; --判断银行卡是否存在 isnullloss
cardinfo.isreportloss%type; --判断银行卡是否冻结 begin begin --判断卡号是否存在 select cardid into isnullcardid from cardinfo where cardid=temp_cardid; exception when no_data_found then begin raise_application_error(-20000, '卡号不存在!' ); end ; end ; begin --先判断卡号是否冻结 select isreportloss into isnullloss from cardinfo where cardid=temp_cardid; if
isnullloss= '是' then raise_application_error(-20001, '该卡已冻结,不能执行该操作!' ); end if; --判断存取款类型是否存在 select distinct transtype into isnulltranstype from tradeinfo where transtype=temp_transtype; if
temp_transtype= '支取' then update cardinfo set balance=balance-temp_money where cardid=temp_cardid; insert into tradeinfo(cardid,transtype,transmoney) values (temp_cardid,temp_transtype,temp_money); dbms_output.put_line( '取出' ||temp_money|| 'RMB!' ); elsif
temp_transtype= '存入' then update cardinfo set balance=balance+temp_money where cardid=temp_cardid; insert into tradeinfo(cardid,transtype,transmoney) values (temp_cardid,temp_transtype,temp_money); dbms_output.put_line( '存入' ||temp_money|| 'RMB!' ); end if; exception when no_data_found then rollback ; raise_application_error(-20002, '存取款类型不正确!' ); when others then dbms_output.put_line( '余额不能少于1' ); rollback ; end ; end ; |
*************利用存取款存储过程来插入tradeinfo数据*****************
set serveroutput on;
execute inout_money(500,'存入','1010 3576 1685 3672');
set serveroutput on;
execute inout_money(500,'支取','1010 3576 1685 3672');
这时,trande表会有两条记录,一个是1010 3576 1685 3672支取的记录,一个是1010 3576 1685 3672的存入记录。
其实,开户和存取款的过程与插入数据时两回事,但是我们却可以利用这两个过程来实现数据的插入,本人在这里也是懒省事。其实,这样插入数据的话,思路上也比较好理解。毕竟,对于练习来说,我们在实现业务之前的数据只是为了检查这些数据的插入是否满足表的约束和它的合理性,但是,在实际的开发过程中,一个业务的完成的前提条件是你必须有实现这个业务的功能,所以先实现业务,通过业务来插入数据时最为合理的。不过这样对于初学者或者基础不太扎实的同胞可能就有些难以理解了,没事,慢慢来吧。
三.实现业务逻辑。
- 挂失。
********挂失需要用户输入的信息**********
1.卡号。
2.密码。
3.账户ID。
******挂失的存储过程*********
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
create or replace procedure lose(temp_cardid in cardinfo.cardid%type, temp_pwd in number,temp_customerid in varchar2) as row_info
cardinfo%rowtype; islose
varchar2(5); begin select isreportloss into islose from cardinfo where cardid=temp_cardid; if
islose= '是' then dbms_output.put_line( '此卡已经挂失!' ); goto last_point; end if; select * into row_info from cardinfo where cardid=temp_cardid; if
row_info.pwd=temp_pwd and row_info.customerid=temp_customerid then update cardinfo set IsReportLoss= '是' where cardid=temp_cardid; dbms_output.put_line( '挂失成功!' ); else dbms_output.put_line( '对不起,您输入卡的信息不正确,不能挂失!' ); end if; <<last_point>> null ; exception when NO_DATA_FOUND then dbms_output.put_line( '您输入的卡号不存在!' ); end ; |
**********测试***********
set serveroutput on;
execute lose('1010 3576 4654 1134','888866','1001');
- 修改密码。
********修改密码所需的用户的信息********
1.卡号
2.密码
**********修改密码的存储过程**********
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
create or replace procedure up_pwd(temp_cardid in varchar2, temp_newpwd in number) as temp varchar2(30); BEGIN select cardid into temp from cardinfo where cardid=temp_cardid; update cardinfo set pwd=temp_newpwd where cardid=temp_cardid; dbms_output.put_line( '修改密码成功!' ); EXCEPTION when no_data_found then dbms_output.put_line( '输入的卡号不存在!' ); when others then dbms_output.put_line( '违反检查约束' ); END ; |
*********修改密码的测试*************
set serveroutput on;
execute up_pwd('1010 3576 0030 0000','666652');
-
查询本周开户的卡号。
1
|
select * from cardinfo where opendate>=trunc(sysdate, 'day' ); |
-
查询本月一次性交易金额最高的卡号。
1
2
3
4
|
select * from tradeinfo; --from后面跟本月的搜索结果,where处控制transmoney为最大值。 select distinct * from ( select * from tradeinfo where transdate>trunc(sysdate, 'month' )) where transmoney in ( select max (transmoney) from tradeinfo); |
-
查询卡号挂失的用户的信息。
1
2
3
4
|
select u.customername,u.customerid,u.personid,u.telephone,u.address from userinfo
u inner join cardinfo
c on c.customerid=u.customerid where c.isreportloss= '是' ; |
四.开发中遇到的一些问题。
在开发的过程中,遇到了许多问题,因为我学习Oracle数据库也就十天左右的时间,对于一些基本的还不是很熟悉。
遇到的问题:
1.创建过程的时候不能有declare关键字。
2.goto流程控制不能goto到exception执行体中。
3.select into语句千万别返回多行语句,在编译的时候是不会出错的,调用的时候出错也会提示较多的错误,修改很麻烦。比如,查看存款类型是否合法的时候,就需要在select语句前加上distinct来确保返回的是一条语句。
4.创建序列不可以使用 or replace。
5.添加行级触发器的时候,赋值用 :new.属性值来赋值。如果select into语句中也赋值的话,就会用两次序列自动产生的值,所以编译器不会报错,但是你的序列的增长率却是你想要的二倍。
6.在像开户这样的逻辑实现过程中,需要多次去用select去判断。而且它还有判断之后需要共同实现的部分,所以对于我一些新手来说,还是比较难于理解begin end的嵌套结构。bengin end里面可以嵌套begin end;exception之后的when then也可以接着begin end并且也可以嵌套。
7.日期函数不是很熟悉。
tranc(date,day)日期date所在周的周日的日期。
interval '2' month将2作为month来运算。日期的加减默认是天。
last_day(date),date日期所在月的最后一天的日期。
add_months(date,2) date日期两个月之后的日期。
8.数值函数不是很熟悉。
dbms_random.value产生0-1之间的小数,精确到很多位。
lpad(对象,位数,0)向对象左侧填充0,知道对象的位数=输出的位数。
trunc(数值对象[,截取精度]),如果截取精度>0,则截取到小数点后第几位,如果截取精度<0,则截取到小数点前第几位,截取的部分用0填充。如果截取精度=0,则去掉小数部分。截取精度不写的话默认为0.