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
12=2 22=4
13=3 23=6 33=9
14=4 24=8 34=12 44=16
15=5 25=10 35=15 45=20 55=25
16=6 26=12 36=18 46=24 56=30 66=36
17=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;