通过select...into 从数据库中查数据对变量进行赋值
declare
name varchar2(50) default 'null';
begin
select sname into name from student where sno='120001'
dbms_output.put_line(name);
end;
使用常量计算圆面积
declare
pi constant number:=3.14;
radius number default 5;
area number;
begin
area:=pi*radius*radius;
dbms_output.put_line(area);
end;
获取当前日期时间
declare
today varchar2(20);
begin
today:=To_CHAR(SYSDATE,'Day');
dbms_output.put_line(‘今天是:’||today);
end;
其中||为字符串连接符
将时间进行格式化输出
select to_char(sysdate,'yyyy-mm-dd hh24:mi:ss') from dual;
将指定的时间字符串格式化输出
select to_char(to_date('2018-07-30','yyyy-mm-dd'),'yyyy-mm-dd hh24:mi:ss am') from dual;
student%rowtype 表示其为数据表student的一行
declare
mystu student%rowtype;
begin
select * into mystu from student where sno ='120004';
dbms_output.put_line(mystu.sname);
dbms_output.put_line(mystu.sage);
end;
创建基本表
create table stu(
sno varchar2(10) not null,
sname varchar2(10),
sgentle varchar2(2),
sage number(2),
sbirth date,
sdept varchar2(20)
)
增加表字段
alter table stu add major varchar2 (20) default '计算机科学与技术' not null
修改表字段
alter table stu modify sname varchar2(15) default '无' not null
重命名基本表
rename stu to stu1
alter stu rename to stu1
复制相同结构的表
create table stu1
as
select * from where 1=2
创建主键约束
alter table stu add constraint pk_stu primary key(sno)
创建外键约束
alter table stu1 add constraint fk_stu foreign key(sno) references stu(sno)
删除约束
alter table stu drop constant pk_stu;
根据查询结果创建和复制表
create table stu1
as
select * from stu
where sage = 22;
(如果只复制表结构不复制表数据,可以使用where rownum<1)
使用escape和转义字符
select * from class
where ms like '%0!%' escape '!'
查找并替换字符串
select replace(sdept,'计算机','computer') 所在班级 from stu;
获取字符的ascii码
select ascii('A') A,ascii('Z') Z,ascii('0') zero from dual;
使用round函数精确度数
select round(123.456),
round(123.456, 2),
round(123.456, -2),
round(163.456, -2)
from dual;
add_months函数
select add_months(sbirth,12) from stu;(日期推迟1年)
提取日期特定部分
select sysdate 当前日期,extract(year from sysdate) 年份,
extract(month from sysdate)月份,
extract(day from sysdate) 日,
extract(hour from systimestamp)+8 小时,
extract(minute from systimestamp) 分,
extract(second from systimestamp) 秒
from dual;
(使用extract 函数提取当前系统时间的小时值时,hour匹配的结果中没有加上时区,所以小8小时)
数据转换类型:
to_char(数字类型转换为字符串类型)
to_number(字符串类型转换为数字类型)
select to_char(1234.567) from dual;
select to_number('1234.567') from dual;
获取日期中的某个星期
select to_char(sysdate,’day’) from dual;
一年中的第几天
select to_char(sysdate,'DDD') from dual;
计算时间差
相差多少天
select floor(to_number(sysdate-to_date('2018-03-04','YYYY-MM-DD'))) as spandays from dual;
相差几个月
select ceil(months_between(sysdate,to_date('2018-03-04','YYYY-MM-DD'))) as spanmonths from dual;
相差年份
select floor(to_number(sysdate-to_date('2014-03-04','YYYY-MM-DD'))/365) as spanyears from dual;
获取某月的第一天和最后一天
select trunc(trunc(sysdate, 'month') - 1, 'month') 上月的第一天,
trunc(sysdate,'month')-1/86400 上月的最后一天,
trunc(trunc(sysdate, 'month'), 'month') 本月的第一天,
last_day(trunc(sysdate,'month'))+1-1/86400 本月的最后一天
from dual;
获取某一年的天数
select add_months(trunc(sysdate,'year'),12) -trunc(sysdate,'year') from dual
nvl函数
nvl(x,value)如果x为空返回value,否则返回x
nvl(x,value1,value2)如果x非空返回value1,否则返回value2
找出没有成绩的学生的编号
select sno from stu
minus
select sno from grade;
intersect(返回两个查询共有的记录)
union all(返回各个查询的所有记录,包括重复记录)
union(返回各个查询的所有记录,不包含重复记录)
minus(返回第一个查询检索出的记录减去第二个查询检索出的记录)
一个表进行自连接
(在stu表中,找出所有年龄相同的学生信息)
select distinct s1.sno,s1.sname,s1.sage
from stu s1 inner join stu s2 on s1.sage = s2.sage
where s1.sno<> s2.sno order by s1.sage desc;
创建外键约束
alter table grade add constraint con_grade foreign key(sno) references stu(sno)
删除级联数据行
alter table grade drop constraint con_grade
在grade表中创建一个包含
在grade表上重新创建一个包含”on delete cascade”参数的外键约束
alter table grade add constraint con_grade foreign key(sno)
references stu(sno) on delete cascade
当再次删除学生生信息时,成绩表的数据也会连同删除掉
防止出现重复值
alter table stu add constraint un_sno unique(sno)
控制语句比两个变量的大小
declare
num1 integer:= 10;
num2 integer:= 20;
begin
if num1 > num2 then
dbms_output.put_line('num1大于num2');
else
dbms_output.put_line('num1小于num2');
end if;
end;
条件嵌套语句
declare
score integer:=50;
begin
if score>=80 then
if score>=90 then
dbms_output.put_line('优秀');
else
dbms_output.put_line('良好');
end if;
else
if score>=70 then
dbms_output.put_line('中等');
else
if score>=60 then
dbms_output.put_line('及格');
else
dbms_output.put_line('不及格');
end if;
end if;
end if;
end;
使用case语句
declare
score integer:=50;
begin
case
when score>=90 then
dbms_output.put_line('优秀');
when (score<90 and score>=80) then
dbms_output.put_line('良好');
when (score<80 and score>=70)then
dbms_output.put_line('中等');
when (score<70 and score>=60) then
dbms_output.put_line('及格');
else
dbms_output.put_line('不及格');
end case;
end;
loop语句实现累加
declare
k integer:=0;
s integer:=0;
begin
loop
s:=s+k;
if k=100 then
exit;
else
k:=k+1;
end if;
end loop;
dbms_output.put_line('共循环次数:'||k);
dbms_output.put_line('计算结果:'||s);
end;
for 语句实现累加
declare
k integer:=1;
s integer:=0;
j integer:=0;
begin
for k in 1..100 loop
s:=s+k;
j:=j+1;
end loop;
dbms_output.put_line('共循环次数:'||j);
dbms_output.put_line('计算结果:'||s);
end;
创建不带参数的存储过程
create or replace procedure cnum
as
cnumber int;
begin
select count(*) into cnumber from stu;
dbms_output.put_line('stu共有'||cnumber||'行记录');
end cnum;
查询名称为cnum的存储过程内容
select text from user_source where type='procedure' and name='cnum';
调用存储过程:
set serveroutput on;
exec cnum;
创建带参数的存储过程:
create or replace procedure getclass(snum number)
as
sclass stu.sdept%type;
begin
select sdept into sclass from stu where sno =snum;
dbms_output.put_line(snum||'的学生所在班级'||sclass);
exception
when no_data_found then
dbms_output.put_line('没有找到学号');
end;
调用带参数的存储过程:
exec getclass(12005);
call getclass(12005);
begin
getclass(12005);
end;
创建并调用带输出参数的存储过程
create or replace procedure getage(snum in number,age out number)
as
begin
select sage into age from stu where sno = snum;
exception
when no_data_found then
dbms_output.put_line('没有找到学号');
end;
declare
age integer;
begin
getage(120005,age);
dbms_output.put_line(age);
end;
查看存储过程的内容:
select text from user_resource
where type=’procedure’ and (name=’getage’ or name =’addstu’)
重新编译存储过程
alter procedure getage compile
删除存储过程
drop procedure addstu;
动态sql执行ddl语句
create or replace procedure proc_test
(
table_name in varchar2,
field1 in varchar2,
datatype1 in varchar2,
field2 in varchar2,
datatype2 in varchar2
)as
str_sql varchar(500);
begin
str_sql:= 'create table '||table_name||'('||field1||' '||datatype1||','||field2||' '||datatype2||')';
execute immediate str_sql;
exception
when others then
null;
end;
调用存储过程创建表
exec proc_test('testsql','id','number(8) not null','name','varchar2(100)');
动态sql执行dml语句
create or replace procedure proc_insert
(
id in number,
name in varchar2
)as
str_sql varchar(500);
begin
str_sql:='insert into testsql values(:1,:2)';
execute immediate str_sql using id,name;
exception
when others then
null;
end;
execute proc_insert(1,'LiLy');
创建触发器
create or replace trigger tri_yj
before update on stu
begin
if updating then
dbms_output.put_line('除非语句触发器');
end if;
end;
语句触发触发器
set serveroutput on ;
update stu set sage = age +1 where sage =26;
查看触发器:
select trigger_name,trigger_type from user_triggers
where table_name =’stu’
select text from user_source where type = ‘trigger’
and name=’tri_yj’;
创建并触发行触发器
create or replace trigger tri_hj
after update on stu
for each row
begin
if updating then
dbms_output.put_line('改行记录已更新');
end if;
end;
insert触发器
create or replace trigger tri_in
after insert on stu
begin
dbms_output.put_line('已插入一行');
end;
update指定列触发器
create or replace trigger tr_up
before update of sno
on stu
for each row
begin
raise_application_error(-20001,'不能修改sno列的数据')
end;
创建instead of 触发器
create or replace trigger tri_inof
instead of insert on v_grade
for
each row
begin
insert into stu(sno,sname.sage,sdept)
values(:NEW.学号,:NEW.姓名,:NEW.年龄,'12计算机')
insert into grade(sno,cname,score)
values(:NEW.学号,:NEW.课程,:NEW.成绩);
end;
insert into v_grade values(‘120008’,’XX’,21,’XX’,88);
创建用户事件触发器
create or replace trigger tri_yh
after ddl on schema
begin
dbms_output.put_line('执行了ddl语句');
end;
创建系统事件触发器
create table logon_event
(user_name varchar2(10),
address varchar2(20),
logon_date timestamp,
logoff_date timestamp);
create or replace trigger tri_xt
after logon on database
begin
insert into logon_event(user_name,address,logon_date)
values(ora_login_user,ora_client_ip_address,systimestamp);
end;
禁用和启用触发器
alter trigger tri_yj disable;
alter trigger tri_yj enable;
将删除记录自动写入记录表
create table del_tmp as
select * from stu where 1=2;
create or replace trigger tri_del_tmp
before delete
on stu
for each row
begin
insert into del_tmp
values(:OLD.sno,:OLD.SNAME)
end;
限制非工作时间操作数据
create or replace trigger tri_sjxz
before insert or delete or update
on stu
begin
if(to_char(sysdate,'day')in('星期六','星期天'))or
(to_char(sysdate,'HH24:MI') not between '9:00' and '18:00')
then raise_application_error(-20001,'不是上班时间,不能修改stu表');
end if;
end;
实现级联更新
create or replace trigger tr_jlgx
after update of sno
on stu
for each row
begin
dbms_output.put_line('旧值'||:OLD.sno||'新值'||:NEW.sno);
update grade set sno = :NEW.sno
where sno = :OLD.sno;
end;
创建数据对象结果操作事件日志
create table ddl_event
(crt_event timestamp primary key,
event_name varchar2(20),
user_name varchar2(10),
obj_type varchar2(20),
obj_name varchar2(20));
create or replace trigger tr_ddl
after ddl on schema
begin
insert into ddl_event values
(systimestamp,ora_sysevent,ora_login_user,
ora_dict_obj_type,ora_dict_obj_name);
end;
删除触发器
drop trigger tri_yj
事务提交:
commit;
事务回滚:
rollback;
设置保存点:
savepoint UPDATEPOINT(这样每次可以回滚到某个保存点,不比回滚整个事务,
即使设置了保存点,也并不代表保存点之前的操作不需要commit)
回滚部分事务
insert ...
update ...
savepoint updatepoin
delete ...
rollback to updatepoin
将会发现删除的数据恢复,插入和更新依然执行
DDL语句对事务的影响
alter table 语句是一条DDL语句(DDL,DCL语句能够对未提交的事务进行提交)
知识小科普
DDL:数据库模式定义语言,关键字:create COMMIT DROP
DML:数据操纵语言,关键字:Insert、delete、update
DCL:数据库控制语言 ,关键字:grant、remove
DQL:数据库查询语言,关键字:select
exit命令断开数据库连接会自动提交事务
如果用户非正常断开服务器,事务会被自动回滚。
select * from stu where sname=’李遂’ for update
(select 语句后加for update 参数 ,表示该语句查询到的记录上加锁,这时第二个SQL*Plus用户试图对该记录进行update 操作时就被卡住了)
锁定数据表(当其他用户想操作该表就会被锁住)
lock table stu in exclusive mode;
查看被锁定的数据对象
select oracle_username,object_id,session_id
from v$locked_object;
select object_name,object_type from dba_objects where object_id=''
为被锁定表解锁:
select sid,serial#,program from V$SESSION WHERE SID = '';
ALTER SYSTEM KILL SESSION '148,1009' ;(sid,SERIAL#)
重复执行步骤1,2,直到试图V$LOCKED_OBJECT 中被锁定stu表所对应的session_id全部被结束为止。
只要不是异常关闭数据库连接,锁定的数据对象会在重新连接后自动解锁。
create or replace package stu_pkg
is
stu_rec stu%rowtype;
procedure delete_stu(sno in number);
procedure query_stu(sno in number);
end stu_pkg;
create or replace package body stu_pkg
is
procedure delete_stu(sno in number)
is
begin
delete from stu where sno = sno;
if sql%found then
dbms_output.put_line('数据库中没有学号为'||sno||'的学生');
else
dbms_output.put_line('删除成功');
end if;
exception
when others then
dbms_output.put_line('操作异常')
end delete_stu;
procedure query_stu(sno in number)
is
begin
select * into stu_rec from stu where sno = sno;
exception
when no_data_found then
dbms_output.put_line('数据库中没有学号为'||sno||'的学生');
when too_many_rows then
dbms_output.put_line('程序运行错误');
when others then
dbms_output.put_line(SQLCODE||'----'||SQLERRM);
end query_stu;
begin
null;
end stu_pkg;
使用程序包:
set serveroutput on
begin
stu_pkg.query_stu('12001');
dbms_output.put_line(stu_pkg.stu_rec.sno);
stu_pkg.delete_stu('120001');
end;
删除程序包:
drop package stu_pkg;(删除整个包)
删除包体
drop package body stu_pkg;
游标
声明游标:
declare
cursor cur_stu
is
select * from stu;
begin
dbms_output.put_line('游标已声明');
end;
打开游标
open cur_stu;
提取数据:
declare
c_no stu.sno%type;
c_name stu.sname%type;
cursor cur_stu
is
select sno,sname from stu order by sno;
begin
open cur_stu;
fetch cur_stu into c_no,c_name;
dbms_output.put_line('学号'||c_no||'姓名'||c_name);
fetch cur_stu into c_no,c_name;
dbms_output.put_line('学号'||c_no||'姓名'||c_name);
fetch cur_stu into c_no,c_name;
dbms_output.put_line('学号'||c_no||'姓名'||c_name);
end;
关闭游标
close cur_stu;
循环语句中的游标使用:
declare
c_no stu.sno%type;
c_name stu.sname%type;
cursor cur_stu
is
select sno,sname from stu order by sno;
begin
open cur_stu;
loop
fetch cur_stu into c_no,c_name;
exit when cur_stu%notfound;
dbms_output.put_line('学号'||c_no||'姓名'||c_name);
end loop;
close cur_stu;
end;
游标提取整行数据:
declare
stu_row stu%rowtype;
cursor cur_stu
is
select * form stu;
begin
open cur_stu;
loop
fetch cur_stu into stu_row;
exit when cur_stu%notfound;
dbms_output.put_line(stu_row.sno||' '||stu_row.sname);
end loop;
close cur_stu;
end;
带参数的游标:
declare
cursor cur_stu(dept varchar2)
is
select * form stu where sdept = dept;
stu_row cur_stu%rowtype;
begin
open cur_stu('12工商管理');
loop
fetch cur_stu into stu_row;
exit when cur_stu%notfound;
dbms_output.put_line(stu_row.sno||' '||stu_row.sname);
end loop;
close cur_stu;
end;
使用参数为变量的游标:
declare
cursor cur_stu
is
select sno
from stu order by sno;
cursor cur_grade(c_sno varchar2)
is
select * from grade;
c_sno stu.sno%type;
g_row grade%rowtype;
begin
open cur_stu;
loop
fetch cur_stu into c_sno;
exit when cur_stu%notfound;
open cur_grade(c_sno);
loop
fetch cur_grade into g_row;
exit when cur_grade%notfound;
dbms_output.put_line(g_row.sno||' '||g_row.sname);
end loop;
close cur_grade;
end loop;
close cur_stu;
end;
使用游标for循环
declare
cursor cur_stu
is
select sno,sname,sdept from stu;
stu_row stu%rowtype;
begin
for stu_row in cur_stu
loop
dbms_output.put_line(stu_row.sno||' '||stu_row.sname);
end loop;
end;
使用游标更新数据:
declare
cursor cur_stu
is
select * from stu for update;
stu_row stu%rowtype;
begin
open cur_stu;
loop
fetch cur_stu into stu_row;
exit when cur_stu%notfound;
update stu set sage = sage +1 where current of cur_stu;
end loop;
close cur_stu;
end;
使用游标删除数据:
declare
cursor cur_stu
is
select * from stu for update;
stu_row stu%rowtype;
begin
open cur_stu;
loop
fetch cur_stu into stu_row;
exit when cur_stu%notfound;
if stu_row.sno ='120008' then
delete from stu where current of cur_stu;
end loop;
close cur_stu;
end;
游标的属性
%found %notfound %isopen %rowcount(游标读取的记录数)
使用rowid高效删除重复记录
delete from stu s
where s.rowid >(selectmin(T.rowid) from stu t
where t.sno = s.sno)
使用truncate代替delete删除记录
truncate table stu;
高效统计记录行数:
select table_name,num_rows
from user_tables
where table_name =’stu’
使用exists代替in(exists的子查询不返回任何实际数据,只返回逻辑值true,false,而in子查询返回集合数据)
用>=代替>运算符
oralce在执行>=运算符时,直接跳到第一个日期等于当天时间的记录再进行统计,而>运算符,首先定位到日期大于当天的记录并且向前扫描到第一个日期小于当天的记录为止,>=无须逐行比较。
避免distinct(distinct 用二重循环查询来实现消除重复记录,这需要对数据表中的每个记录都进行比较)
用索引提高效率(oracle 使用的是一个复杂的子平衡B-tree结构)
创建索引
create index index_sage on stu(sage);
select * from stu where sage >22;
注:使用索引 需要空间来存储,也需要定期维护,每当有记录在表中增减或索引列被修改时,索引本身也会被修改,因此需定期重构。
避免在索引列上进行运算
如果在select语句的where 子句中,索引列是函数的一部分,或者是在索引列上进行了运算,Oracle优化器将不用索引而仍使用全表扫描,此时索引将失效。
在Oracle中,如果用户需要找出哪些语句执行效率极差,从而有针对性地进行优化,一般可以对正在运行的语句查看执行计划。
explain plan for select * from test1;
select * from table(dbms_xplan.display);
在索引列上用union替换or(使用索引列使用or运算符将进行全表扫描)
select * from stu where sno=’12222’ or sage>22;
select * from stu where sno=’12001’ union select * from stu where sage>22;
避免在索引列上用is null,is not null.
使用where 子句优化group by
select * from log_event group by user_name having user_name =’sys’ or user_name=’sys’
select * from log_event where user_name =’sys’ or user_name=’sys’
group by user_name
处理预定义异常:
declare
stu_rec stu%rowtype
begin
select * into stu_rec from stu where sage>22;
exception
when too_many_rows then
dbms_output.put_line('返回的记录太多了');
when others then
dbms_output.put_line('未知异常');
end;
处理自定义异常:
declare
stusage stu.sage%type;
myexp exception;
begin
select sage into stusage from stu where sno='120001';
if stusage =22 then
raise myexp;
end if;
exception
when on_data_found then
dbms_output.put_line('没有找到该学生');
when myexp then
dbms_output.put_line('该学生年龄错误');
end;