存储过程详解

1.程序块结构

declare
--变量申明
begin 
 --执行
end;

2、变量申明与赋值

declare
v_long number;  --变量申明
v_wide number; --变量申明
v_perimeter number; --变量申明 
begin 
v_long:=5;  --赋值
v_wide:=3;  --赋值
v_perimeter:=(v_long+v_wide)*2;  --执行
dbms_output.put_line('v_perimeter='||v_perimeter);  --输出
end; 

-------申明变量可以直接赋初始值--------

declare
v_long number:=5;  
v_wide number:=3; 
v_perimeter number; 
begin 
v_perimeter:=(v_long+v_wide)*2;  --执行
dbms_output.put_line('v_perimeter='||v_perimeter);  --输出
end; 

--------变量是可变的-----------

declare
v_long number:=5;  
v_wide number:=3;   
v_perimeter number; 
begin   
v_long := 8;  --执行时取离他最近的变量
v_perimeter:=(v_long+v_wide)*2;  --执行
dbms_output.put_line('v_perimeter='||v_perimeter);  --输出
end;

3、定义常量constant

已知半径求面积—

declare
v_pi constant number:=3.14;   --定义常量需要加关键字constant
v_r number:=3;  
v_area number;
begin 
v_area:=v_pi*v_r*v_r;  --执行
dbms_output.put_line('v_area='||v_area);  --输出
end;

—特性 常量必须赋初始值,常量是不可变的—

declare
v_pi constant number; --必须赋初始值
v_r number:=3;      
v_area number;
begin 
v_pi:=3.14; --不可赋值
v_area:=v_pi*v_r*v_r;  --执行
dbms_output.put_line('v_area='||v_area);  --输出
end;

4、字符/日期数据类型的变量

declare 
v_name varchar2(30):='张三';
v_date date := sysdate;
begin 
v_date := to_date('2021-02-01','yyyy-mm-dd');
dbms_output.put_line('v_name='||v_name);
dbms_output.put_line('v_date='||v_date);
end; 

5、隐式游标及其特性 只能返回一行数据 返回多行或者没有数据都会报错

--select into--
declare 
v_name varchar2(30);
v_sal number;
begin 
select ename,sal into v_name,v_sal from scott.emp where empno=7499;  --查询ename这个名字到v_name这个变量
dbms_output.put_line('v_name='||v_name);
dbms_output.put_line(v_name||'薪水是'||v_sal);
end;    

—特性1 返回多行数据会报错—

declare 
v_name varchar2(30);
v_sal number;
begin 
select ename,sal into v_name,v_sal from scott.emp; 
dbms_output.put_line('v_name='||v_name);
dbms_output.put_line(v_name||'薪水是'||v_sal);
end;  
---特性2 没有数据也会报错--- 
declare 
v_name varchar2(30);
v_sal number;
begin 
select ename,sal into v_name,v_sal from scott.emp where empno=1;
dbms_output.put_line('v_name='||v_name);
dbms_output.put_line(v_name||'薪水是'||v_sal);
end; 

—防报错 加聚合函数

declare 
v_name varchar2(30);
v_sal number;
begin 
select max(ename),max(sal) into v_name,v_sal from scott.emp; 
dbms_output.put_line('v_name='||v_name);
dbms_output.put_line(v_name||'薪水是'||v_sal);
end; 

6、%type/%rowtype 定义数据类型

%type:引用数据库中的某列的数据类型或某个变量的数据类型

declare 
v_name scott.emp.ename%type;  --应用ename字段的数据类型
begin 
select ename into v_name from scott.emp where empno=7499; 
dbms_output.put_line('v_name='||v_name);
end; 

–%rowtype:引用数据库中的一行(所有字段)作为数据类型。

declare 
v_all scott.emp%rowtype;  --用emp表所有字段类型
begin 
select ename,sal into v_all.ename,v_all.sal from scott.emp where empno=7499; 
dbms_output.put_line(v_all.ename);
dbms_output.put_line(v_all.sal);
end;

7、if判断

if 条件 then 结果 end if
–一个分支如果smith的工资小于1000输出努力

declare 
v_sal number;
begin 
select sal into v_sal from scott.emp where ename='SMITH';
if v_sal<1000 
   then dbms_output.put_line('努力'); 
   end if; 
end;

–两个分支如果smith的工资小于500输出努力 否则输出很棒

declare 
v_sal number;
begin 
select sal into v_sal from scott.emp where ename='SMITH';
if v_sal<500 
   then dbms_output.put_line('努力'); 
   else dbms_output.put_line('很棒'); 
   end if; 
end;

–尽量少些else 用elseif

declare 
v_sal number;
begin 
select sal into v_sal from scott.emp where ename='SMITH';
if v_sal<500 
   then dbms_output.put_line('努力'); 
   elsif v_sal >=500
   then dbms_output.put_line('很棒'); 
   end if; 
end;

–多个分支,如果smith的工资小于500输出努力 工资[500,2000]输出可以 工资大于2000输出很棒

declare 
v_sal number;
begin 
select sal into v_sal from scott.emp where ename='SMITH';
if v_sal<500 
   then dbms_output.put_line('努力'); 
   elsif v_sal >=500 and v_sal <2000
   then dbms_output.put_line('可以'); 
   elsif v_sal>2000 
   then dbms_output.put_line('很棒'); 
   end if; 
end; 

–换一种写法

declare 
v_sal number;
begin 
select sal into v_sal from scott.emp where ename='SMITH';
if v_sal<500 
   then dbms_output.put_line('努力'); 
   elsif v_sal >=500 and v_sal <2000
   then dbms_output.put_line('可以'); 
   else dbms_output.put_line('很棒'); 
   end if; 
end;    

–比较难的写法

declare 
v_sal number;
begin 
select sal into v_sal from scott.emp where ename='SMITH';
if v_sal<500 
   then dbms_output.put_line('努力'); 
   elsif v_sal <2000
   then dbms_output.put_line('可以'); 
   elsif v_sal > 2000
   else dbms_output.put_line('很棒'); 
   end if; 
end;  

–case判断
–一个分支如果smith的工资小于1000输出努力

declare 
v_sal number; 
begin 
select sal into v_sal from scott.emp where ename='SMITH';
case when v_sal<1000 
   then dbms_output.put_line('努力'); 
   end case;   
end;   

–两个分支如果smith的工资小于500输出努力 否则输出很棒

declare 
v_sal number;
begin 
select sal into v_sal from scott.emp where ename='SMITH';
case when v_sal<500 
   then dbms_output.put_line('努力'); 
   else dbms_output.put_line('很棒'); 
   end case; 
end;

–多个分支,如果smith的工资小于500输出努力 工资[500,2000]输出可以 工资大于2000输出很棒

declare 
v_sal number;
begin 
select sal into v_sal from scott.emp where ename='SMITH';
case when v_sal<500 
       then dbms_output.put_line('努力'); 
     when v_sal >=500 and v_sal <2000
      then dbms_output.put_line('可以'); 
   when v_sal>2000 
     then dbms_output.put_line('很棒'); 
   end case; 
end; 

–建表造数

create table emp_0705 as select * from scott.emp
select * from emp_0705

—条件后面 你想做什么事情就做什么事情
–把smith的奖金改成3000—

declare 
v_sal number;
begin 
select sal into v_sal from emp_0705 where ename='SMITH';
if v_sal>500 
   then update emp_0705 set comm=3000 where ename='SMITH';
   commit; 
 end if;
end; 

—删除30部门的数据

declare 
v_sal number;
begin 
select sal into v_sal from emp_0705 where ename='SMITH';
if v_sal>500   
   then delete from emp_0705 where deptno=30;
   commit;  
 end if;  
end; 

8、循环的语法 loop end loop

declare 

begin
  loop
    end loop;
end;   

—死循环 无条件循环,没有退出语句

declare 

begin
  loop
    null;
    end loop;
end;   

–for 循环
–自然数求和 1…100

declare
 v_a number :=0;
 v_sum number :=0;
begin
  for i in 1..100 loop  --循环1-100次
    v_a:=v_a+1;  --每循环一次加1   
    v_sum:=v_sum+v_a;     
    end loop;
dbms_output.put_line (v_sum);
end;

–看过程

declare
 v_a number :=0;
 v_sum number :=0;
begin
  for i in 1..100 loop  --循环1-100次
    v_a:=v_a+1;  --每循环一次加1
    v_sum:=v_sum+v_a; 
dbms_output.put_line(v_a||' '||v_sum); --看他过程
    end loop;
dbms_output.put_line (v_sum);
end;  

–简化

declare
 v_sum number :=0;
begin
  for i in 1..100 loop  --循环1-100次
    v_sum:=v_sum+i; 
    end loop;
dbms_output.put_line (v_sum);
end;    

9、while循环 基本上没有人这么写

declare
 v_va number :=0;
 v_sum number :=0;
begin   
  while v_va<100 loop
        v_va:=v_va+1;
        v_sum:=v_sum+v_va; 
    end loop;       
dbms_output.put_line (v_sum);
end;  

–无条件循环 exit退出条件循环体里面

declare
 v_va number :=0;
 v_sum number :=0;
begin          --没有条件
   loop
        v_va:=v_va+1;
        v_sum:=v_sum+v_va; 
     --exit when v_va = 100;  第一种退出方式
     if v_va =100 then exit ; --第二种退出方式
     end if ; 
    end loop;       
dbms_output.put_line (v_sum);
end;   

–10、两数交换

declare 
 a number:=10;
 b number:=5;
 c number;
begin
 c := a; --c=10,a=10
 a := b;  --a=5,b=5,c=10
 b := c;  --a:=5 ,b=10,c=10  
dbms_output.put_line('a='||a);
dbms_output.put_line('b='||b);
end;

–两表交换
–建表造数

create table emp_0705  as select * from scott.emp;
create table emp_0705_bak as select * from scott.emp;
delete from emp_0705_bak where deptno= 20 ;
select * from emp_0705;
select * from emp_0705_bak

–交换表名

alter table emp_0705 rename to emp_0705_tmp;   
alter table emp_0705_bak rename to emp_0705;
alter table emp_0705_tmp rename to emp_0705_bak;

11、自然数求和

奇数偶数和 结合判断语句–

declare 
v_jishu number:=0; 
v_oushu number:=0;
begin 
  for i in 1..100 loop
   if mod(i,2)=1  --mod求余
     then v_jishu := v_jishu+i;
   elsif mod(i,2)=0 
     then v_oushu :=  v_oushu+i;
    end if;
   end loop; 
dbms_output.put_line('v_jishu='||v_jishu);
dbms_output.put_line('v_oushu='||v_oushu);  
end;

–3的倍数求和,5的倍数求和

declare 
v_sum3 number:=0;
v_sum5 number:=0; 
begin 
  for i in 1..100 loop
   if mod(i,3)=1  --mod求余
     then v_sum3 := v_sum3+i;
   elsif mod(i,3)=0 
     then v_sum5 :=  v_sum5+i;
    end if;  
   end loop; 
dbms_output.put_line('v_sum3='||v_sum3);
dbms_output.put_line('v_sum5='||v_sum5);  
end;   

–自然数求和 1…100以及奇数偶数和

declare 
v_jishu number:=0; 
v_oushu number:=0;
v_sum number:=0;
begin 
  for i in 1..100 loop
   if mod(i,2)=1  --mod求余
     then v_jishu := v_jishu+i;
   elsif mod(i,2)=0 
     then v_oushu :=  v_oushu+i;
    --elsif 1=1   1=1表示无条件成立
      --then  v_sum = v_sum+i;  虽然不报错,但是没有意义,判断语句是从上到下满足一个条件后,就不向下判断了
    end if;
    v_sum = v_sum+i; --写在判断外面就可以,程序依次从上到下执行
   end loop; 
dbms_output.put_line('v_jishu='||v_jishu);
dbms_output.put_line('v_oushu='||v_oushu);  
end;

12、水仙花数

水仙花数是指一个3位数,它的每个位上的数字的3次幂之和等于它本身 1^3 + 5^3 +3^3 =153
–思路:判断每一个数字是否满足水仙花数的条件 如果满足则输出

–简化代码

declare 
begin  
 for i in 100..999 loop 
   if i= power(substr(i,1,1),3) + power(substr(i,2,1),3) +power(substr(i,3,1),3) 
     then dbms_output.put_line(i);
   end if;  
   end loop; 
end; 

–计算一下有多少个非水仙花数

declare
v_sum number:=0;
begin
 for i in 100..999 loop 
   if i= power(substr(i,1,1),3) + power(substr(i,2,1),3) +power(substr(i,3,1),3) 
     then dbms_output.put_line(i);
   else v_sum:= v_sum+1;       
   end if;
 end loop;   
dbms_output.put_line('v_sum='||v_sum);
end; 

–嵌套循环

declare
begin
 for i in 1..9 loop 
   for j in 0..9 loop 
    for k in 0..9 loop 
     if i*100 +j*10 +k = power(i,3)+power(j,3)+power(k,3)
       then  dbms_output.put_line(i||j||k);
     end if;    
    end loop;         
   end loop;   
 end loop;  
end;

–循环路径:
100-109
110-119
120-129

190-199
200-209
210-219

–延伸一下,sql求出水仙花数

with tmp as(
select level-1 rn from dual connect by level <=10)

select * from tmp a
join tmp b on 1=1 
join tmp c on 1=1          
where a.rn>=1   
and power(a.rn,3)+ power(b.rn,3)+ power(c.rn,3)= a.rn||b.rn||c.rn 

—另一种求法

with tmp as(
select level-1 rn from dual connect by level <=999)

select * from tmp a         
where a.rn>=100 
and power(substr(a.rn,1,1),3)+ power(substr(a.rn,2,1),3)+ power(substr(a.rn,3,1),3)= a.rn

13、乘法口诀

declare
v_str varchar2(2000);
begin
 for i in 1..9 loop 
   v_str:= null;
   for j in 1..i loop 
    v_str:= v_str||' '||j||'*'||i||'='||i*j;
   end loop;      
 dbms_output.put_line(v_str);   
 end loop;     
end; 

11=1
1
2=2 22=4
1
3=3 23=6 33=9
14=4 24=8 34=12 44=16
15=5 25=10 35=15 45=20 55=25
1
6=6 26=12 36=18 46=24 56=30 66=36
1
7=7 27=14 37=21 47=28 57=35 67=42 77=49
18=8 28=16 38=24 48=32 58=40 68=48 78=56 88=64
19=9 29=18 39=27 49=36 59=45 69=54 79=63 89=72 9*9=81

14、冒泡排序

一个不重复的数字排序 68495237
1、6与后面的数字8495237依次比大小取出最小的2,还剩6849537
2、再去剩下的里面取出最小的3还剩684957
3、循环上面的操作每次取出最小的

declare
v_str number:=68495237;
v_result number;
v_long number; 
a      number;
begin 
  v_long:=length(v_str);  
  for i in 1..v_long loop
    a:=substr(v_str,1,1); --取出6   
   for j in 1..v_long-i loop      
      if a< substr(v_str,j+1,1)     
       then a:=a;      
         else a:=substr(v_str,j+1,1);   --谁小取谁
         end if;      
     end loop;  
   v_result:= v_result||a; 
   v_str:=replace(v_str,a,'');
   dbms_output.put_line('v_result='||v_result);
   dbms_output.put_line('v_str='||v_str); 
  end loop;   
  dbms_output.put_line(v_result);
  end; 

–一条sql解决冒泡排序
–思路 :先拆成8行数据然后排序后再拼接起来

with tmp as (select substr(68495237,level,1)rn from dual connect by level <= (select length(68495237) from dual)
)
select listagg(rn)within group (order by rn) from tmp

15、游标 CURSOR

隐式游标 处理一行数据
–显式游标 处理多行数据 ;需要一行一行处理数据(所以需要循环)

declare 
 cursor c_emp  --定义游标名称
   is 
 select ename,sal from scott.emp;   ---数据放到游标里面
 v_row c_emp%rowtype;
 begin 
   open c_emp; --打开游标
    loop   
     fetch c_emp into v_row; --提取一行数据到变量,想怎么处理就怎么处理
     exit when c_emp%notfound;  ---当数据循环完找不到数据退出
     dbms_output.put_line(rpad(v_row.ename,10,' ')||' 薪资 '||v_row.sal);
   end loop;
   close c_emp; --关闭游标
 end; 

–简写for循环 无需打开关闭游标,fetch、exit

 declare 
 cursor c_emp  
   is 
 select ename,sal from scott.emp;  
 begin 
   for x in c_emp loop  --c_emp放到x里  
     dbms_output.put_line(rpad(x.ename,10,' ')||' 薪资 '||x.sal);
  end loop;
 end;

–想怎么处理就怎么处理
–把取出来的数据放在一张新表里面

drop table emp_1207;
create table emp_1207 as select empno,sal,comm from scott.emp where 1=2;
select * from emp_1207;
 declare 
 cursor c_emp   
   is  
 select empno,sal from scott.emp;   
 begin  
   for x in c_emp loop  --c_emp放到x里  
   insert into emp_1207(empno,sal)values(x.empno,x.sal);
   commit;
  end loop; 
 end;

—如果薪水<1200 奖金+1000 [1200,2800]奖金+2000 >=2800 奖金+3000

 declare 
 cursor c_emp  
   is 
 select empno,sal from emp_1207; 
 v_empno number;
 v_sal number; 
 begin 
   for x in c_emp loop  --c_emp放到x里  
v_empno:=x.empno;
v_sal:=x.sal;
  if v_sal<1200
     then update emp_1207 set comm = nvl(comm,0)+1000 where empno=v_empno;  --空值不能做运算
     commit;
  elsif v_sal>=1200 and v_sal <2800
    then update emp_1207 set comm = nvl(comm,0)+2000 where empno=v_empno;
     commit;
   elsif v_sal>=2800
    then update emp_1207 set comm = nvl(comm,0)+3000 where empno=v_empno;
     commit;
  end if; 
  end loop;
 end;

15、动态sql execute immediate(立即执行)

drop table emp_4;
create table emp_4 as select * from scott.emp e;
select * from emp_4

--ddl,truncate 不能直接用,要用execute immediate包装起来
declare
begin 
  --truncate table emp_4
  execute immediate 'truncate table emp_4';  --工作中天天要用的
end; 

16、存储过程 procedure sp

–建表造数

drop table emp_bak;
create table emp_bak as select * from scott.emp where 1=2;
select * from emp_bak;

–最简单的存储过程

create procedure sp_emp_bak
is  --as 
begin   
  insert into emp_bak select * from scott.emp where deptno=10;
  commit;  
end;  

–第一步 编译 执行这个sp,仅仅是把这个代码存到我们数据库
–第二步 调用
1、在对象先找到要测试的sp>选择对应的sp右键>测试
2、在sql窗口选择对应的sp右键>测试
3、直接sql窗口写调用语句
begin sp_emp_bak;end;

17、全量抽数 把一个表得数据全部放到另一张表,并支持重跑

建表造数

drop table emp_bak;
create table emp_bak as select * from scott.emp where 1=2;
select * from emp_bak;

–全量抽取

create or replace procedure sp_emp_bak
is  --as 
begin 
  --先删除后插入
  execute immediate 'truncate table emp_bak' ; 
  insert into emp_bak select * from scott.emp;
  commit;  
end;   

—标准化一下 不允许写* insert后面字段也不能省略

create or replace procedure sp_emp_bak  --创建或替换存储过程
is  --as 
begin 
  --先删除后插入
  execute immediate 'truncate table emp_bak' ; 
  insert into emp_bak (
          empno,      
          ename,    
          job,     
          mgr,     
          hiredate,
          sal,     
          comm,    
          deptno)
  select  empno,   
          ename,    
          job,     
          mgr,     
          hiredate,
          sal,     
          comm,    
          deptno 
  from scott.emp;
  commit;  
end; 

18、增量抽数 公司里一个表的数据量非常大,适合做增量

建表造数
select * from ods_emp;
select * from ods_emp_bak; 

create table ods_emp  as select a.*,sysdate create_time from scott.emp a ;
update ods_emp set create_time= trunc(sysdate,'dd') where deptno=10;
update ods_emp set create_time= trunc(sysdate-1,'dd') where deptno=20;
update ods_emp set create_time= trunc(sysdate-2,'dd') where deptno=30;
commit;                  

create table ods_emp_bak as select * from ods_emp where 1=2;

create or replace procedure sp_ods_emp_bak  --创建或替换存储过程
is  --as 
begin 
  --先删除
  delete from ods_emp_bak where create_time = trunc(sysdate-1,'dd');
  --后插入
  insert into ods_emp_bak ( 
          empno,      
          ename,    
          job,     
          mgr,     
          hiredate,
          sal,     
          comm,    
          deptno,
          create_time)
  select  empno,   
          ename,    
          job,     
          mgr,     
          hiredate,
          sal,     
          comm,    
          deptno,
          create_time 
  from ods_emp
  where create_time = trunc(sysdate-1,'dd');
  commit;  
end; 

—时间是变化的,不能每次都去改代码,时间需要传参

create or replace procedure sp_ods_emp_bak(
p_start_time varchar2,   --参数类型一般都是字符类型,不需要指定长度
p_end_time varchar2)
is  --as 
v_start_time varchar2(30):= p_start_time;   --把传参的值给到变量
v_end_time   varchar2(30):= p_end_time;    
begin 
  --先删除
  delete from ods_emp_bak where create_time = to_date(v_start_time,'yyyymmdd');
  --后插入
  insert into ods_emp_bak (
          empno,      
          ename,    
          job,     
          mgr,     
          hiredate,
          sal,     
          comm,    
          deptno,
          create_time)
  select  empno,   
          ename,    
          job,     
          mgr,     
          hiredate,
          sal,     
          comm,    
          deptno,
          create_time 
  from ods_emp
  where create_time = to_date(v_start_time,'yyyymmdd');
  commit;  
end;

–性能优化 分区表改造
–建表造数

select * from ods_partition_emp_bak;
select * from ods_partition_emp_bak;   

create table ods_partition_emp_bak(
empno      number(4),
ename      varchar2(10),
job        varchar2(9),
mgr        number(4),
hiredate   date,
sal        number(7,2),
comm       number(7,2), 
deptno     number(2),  
create_time varchar2(10) 
)

partition by list(create_time)
(
 partition p_20240807 values(20240807),
 partition p_20240808 values(20240808),
 partition p_20240809 values(20240809)
);

–delete 改造成truncate

create or replace procedure sp_ods_partition_emp_bak(
p_start_time varchar2,   --参数类型一般都是字符类型
p_end_time varchar2) 
is  --as 
v_start_time varchar2(30):= p_start_time;   --把传参的值给到变量
v_end_time   varchar2(30):= p_end_time; 
v_str    varchar2(300);
begin  
  --先删除
  --delete from ods_emp_bak where create_time = to_date(v_start_time,'yyyymmdd');
 
  --不允许这么写,会报错,里面有变量
  --execute immediate 'alter table ods_partition_emp_bak truncate partition for(v_start_time)';
  需要定义一个变量,拼起来一句话
  v_str := 'alter table ods_partition_emp_bak truncate partition for ('|| v_start_time||')';
  execute immediate v_str;
  --后插入
  insert into ods_partition_emp_bak ( 
          empno,      
          ename,    
          job,     
          mgr,     
          hiredate,
          sal,     
          comm,    
          deptno,
          create_time)
  select  empno,   
          ename,    
          job,     
          mgr,     
          hiredate,
          sal,     
          comm,    
          deptno,
          create_time 
  from ods_emp
  where create_time = to_date(v_start_time,'yyyymmdd');
  commit;  
end;  

–19 merge into 匹配则更新不匹配则插入
–语法

merge into 目标表
using(增量)
on(匹配字段)
when matched then update set  --update和set之间不需要加表名
when not matched then insert values
--insert 和values之间不需要加into 表名

–建表造数

select * from ods_merge_emp;  --来源表
update ods_merge_emp set comm = 999 where deptno =10 ;
select * from ods_merge_emp_target; --目标表
create table ods_merge_emp as select * from scott.emp;
create table ods_merge_emp_target  as select * from scott.emp where deptno=10;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小莫细说linux

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值