Oracle总结与ATM实战演示

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   in  1..nine_line loop
     for  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  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  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.



  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值