1. 程式人生 > 其它 >Oracle資料庫學習記錄

Oracle資料庫學習記錄

Oracle資料庫

一、資料庫分類

  • 關係型(模型)資料庫

​ MySQL、Oracle、SQLServer、H2(記憶體資料庫,單元測試使用)

  • 非關係型資料庫

​ NoSQL、MongoDB、Redis(不用SQL語言操作的資料庫)、MemCache(記憶體資料庫)

關係:表,表和表聯絡

主鍵:唯一區分一條記錄,一個列或多列組合

外來鍵:在別的表中的主鍵

面試點:Database DBMS 伺服器

三正規化:壓縮資料庫空間,正規化太多會增加查詢的時間

  • 第一正規化:欄位不能再拆分了。
  • 第二正規化:有主鍵,能唯一區分一條記錄
  • 第三正規化:不存在其他表中的非主鍵欄位(外來鍵)

二、Oracle物理儲存結構檔案

  • 控制檔案*.ctl sqlplus 查詢 select name from v$controlfile;
  • 資料檔案*.dbf 一個數據檔案只屬於一個表空間 一個表空間多個數據檔案
    • select file_name,tablespace_name,bytes,autoextensible from dba_data_files;
  • 日誌檔案*.log 恢復資料庫資料(備份檔案,歸檔重做檔案(日誌檔案寫滿後歸檔),引數檔案pfile(parameter file) ,spfile(server parameter file))

三、邏輯結構

表空間 :

system系統表空間,資料字典 sysaux輔助系統表空間 temp臨時表空間(排序等一些臨時資料使用)users使用者表空間,undotbs1回退表空間,撤銷表空間(類似於回收站)

select name from v$tablespace;

  1. 建立表空間,指定表空間的名稱,表空間的檔案位置,檔案大小,是否自動擴充套件

​ create tablespace tab1 datafile 'D:\app\Wjg\oradata\orcl\tab1.dbf' size 88k;

  1. 修改檔案大小

​ alter database datafile 'd:\app\Wjg\oradata\orcl\tab1.dbf' resize 99k;

  1. 刪除表空間

​ 刪除空表空間,包含物理檔案

​ drop tablespace tablespace_name including datafiles;
​ 刪除非空表空間,包含物理檔案
​ drop tablespace tablespace_name including contents and datafiles;

  1. 給表空間增加檔案

​ alter tablespace tab1 add datafile 'd:\app\wjg\oradata\orcl\tab1_2.dbf' size 87k;

增加表空間大小有三種方法:

  1. alter database datafile 'd:\app\Wjg\oradata\orcl\tab1.dbf' resize 99k;
  2. alter database datafile 'd:\app\Wjg\oradata\orcl\tab1_2.dbf' autoextend on next 1k maxsize 100k;
  3. alter database tempfile 'd:\app\Wjg\oradata\orcl\tab2_temp.dbf' resize 99k;
  4. alter tablespace tab1 add datafile 'd:\app\wjg\oradata\orcl\tab1_2.dbf' size 87k;

當檔案被表空間引用時,不要直接物理刪除檔案,否則會破壞資料庫

給指定表空間建立臨時表空間

用來管理資料庫排序操作以及用於儲存臨時表、中間排序結果等臨時物件

建立臨時表空間
create temporary tablespace tab1_temp tempfile 'D:\app\wjg\oradata\orcl\tab1_temp1.dbf' size 3m;
建立使用者(預設的表空間是user表空間)
create user ychs1 identified by 123456;
刪除使用者
drop user ychs1;
建立使用者並繫結指定的表空間和臨時表空間
create user ychs1 identified by 123456 default tablespace tab1_data temporary tablespace tab1_temp;
授權使用者
grant create session,create table,unlimited tablespace to ychs1;
使用者登入
sqlplus ychs1/123456
使用者建立表
create table t1(id int);
刪除表空間,刪除臨時表空間
drop tablespace tab1_data including contents and datafiles;

drop tablespace tab1_temp including contents and datafiles;
建立一個表空間
create tablespace tab2_data datafile 'd:\app\wjg\oradata\orcl\tab2_data.dbf' size 1m;
建立一個臨時表空間
 create temporary tablespace tab2_temp tempfile 'd:\app\wjg\oradata\orcl\tab2_temp.dbf' size 3m;

為什麼要建立臨時表空間https://www.cnblogs.com/zhangyingai/p/7082594.html

更改使用者ychs1的表空間和臨時表空間
 alter user ychs1 default tablespace tab2_data;

 alter user ychs1 temporary tablespace tab2_temp;

段:

1、資料段 2、索引段 3、回滾段 4、臨時段

在執行CREATE INDEX ,select order by, select distinct , select group by等SQL語句的時候Oracle資料庫會為這些語句分配一個臨時段,

在資料庫管理中,若要經常執行上面這類SQL語句時,最好調整SORT_AREA_SIZE初始化引數,來增大排序區

資料區:磁碟分配最小單位

塊:最小的邏輯單位

檢視塊的大小 :show parameters block

set line 100

四、記憶體結構

SGA(系統全域性區)

各種池,各種快取,資料字典,主要和系統有關

PGA(程式全域性區)

使用者會話,主要和使用者有關

五、SQL(結構化查詢語言)

面試題

SQL執行順序,from,where,group by,having,select,distinct,order by,limit

Structured Query Language

資料型別:

NUMBER(數值型別)38位、number(m,n)小數位,整數位m-n;

CHAR(定長字串)2000Byte、

VARCHAR2(變長字串)4000Byte、

DATE(日期)、

long(變長最長2G)、

TIMESTAMP時間戳、

BLOB(大資料型別)(4G-1)db_block_size、

CLOB(大資料型別)

如果給定長的字串設定了太大的空間,若要使其變短使用trim(屬性)函式

​ 先:update t1 set name=trim(name);

​ 再:alter table t1 modify name char(10);

DDL:

資料定義語言(data define language),create 、drop 、alter;主要是用來操作物件的,不針對資料

資料型別

使用者的表:select table_name,tablespace_name from all_tables where owner='YCHS1'; select table_name from user_tables;

建立一個表並指定主鍵: create table t_student(id number(10) primary key);

給表增加屬性:

alter table t1 add name char(10);
alter table t1 add address varchar2(100) not null;

檢視屬性: desc t1;

修改表的欄位型別:alter table t1 modify address varchar2(20);不能小於已知長度的最大值

為此列指定的允許精度 alter table t1 add chinese number(4,1);總共四位,小數會進行四捨五入

刪除屬性:alter table t1 drop column math;

刪除多個屬性:alter table t1 drop (math,english);

表名重新命名:alter table t1 rename to t2;

屬性重新命名:alter table t1 rename column chinese to chinese_score;

序列器

建立序列器 create sequence seq_1;

  • 取下一個新值nextval
  • 取當前值currval

DML:

資料操作語言(data manipulation language),insert、update、delete(清空記錄不釋放空間)、truncate(清空記錄釋放空間);

往表中插入一條記錄:insert into t1(id,name,address) values(1,'name1','add1');

更新一條記錄: update t1 set birthday=sysdate where id=1;

更新屬性的長度去除空格:update t1 set name=trim(name);

插入一行:insert into t1(id,name,address,chinese,math,birthday) values(seq_1.nextval,'name','address',100,100,sysdate);

刪除表資訊:delete from t_student;產生回滾資訊

截斷表資訊:truncate table t_student;物理刪除

DQL:

檢視所有的表

select table_name from user_tables;

資料查詢語言(data query language ),select;主要查詢;

檢視屬性的字元長度:select length(name),length(address) from t1;

別名:

sqlplus命令:col ename heading 姓名

select id as 編號,clazz 班級,join_date 報道時間 from t_student;

不是表中的列,時計算出來的結果列,列名是一個較長的表示式,用一個別名錶示。(帶有表示式和別名的select語句)

select comm+sal as money from emp;

多表查詢時,給表起別名,可以簡化表名。

篩選查詢:

查30部門的人:

select * from emp where deptno=30;

查30部門職位是銷售員:(包含邏輯篩選)

select * from emp where deptno=30 and job='SALESMAN';

查30部門職位是銷售員的員工,或部門是20的員工:

select * from emp where deptno=20 or (deptno=30 and job='SALESMAN');

distinct顯示不重複記錄

select distinct deptno from emp;

選擇每個部門下的職位

select distinct deptno,job from emp;

去重和分組功能相同,分組可以統計。

模糊查詢

like關鍵字
  1. 兩個萬用字元 _(代表有且僅有一個字元) %(代表0個或多個字元)
select ename from emp where ename like 'K___';
select ename from emp where ename like '%K%';

模糊查詢對效能的影響

模糊匹配放在後面影響不大,放在前面失去索引效果,全表掃描。

in關鍵字

匹配一組資訊中的某一個時用到

select empno,ename,job from emp where job in('PRESIDENT','MANAGER');
not關鍵字
between關鍵字

大於等於第一個值,小於等於第二個值

is null關鍵字
select * from emp where comm is null;

分組查詢:

一般和聚集函式一起使用,為了使用沒有歧義,建議select後面的列名,和group by後面的列名一致。

group by欄位

select deptno,job from emp group by deptno,job;

排序

按照部門升序,工資降序排列

select deptno,sal from emp order by deptno,sal desc;

where 、group by、 order by 的順序

選擇工資大於800,的部門,按照升序排列;

  1. 先where過濾
  2. 再通過部門分組
  3. 最後通過部門號排序
select deptno from emp where sal>800 group by deptno order by deptno desc;
select deptno,avg(sal) avg_sal from emp group by deptno having avg(sal)>2000;
  1. 先按照部門分組

  2. 統計平均工資,別名不產生

  3. having過濾

  4. 返回結果此時生成別名。

(面試點)偽列rowid、rownum:

select rownum from emp;

生成偽列號:

select t.*,rownum from (select * from emp) t;

對偽列號進行查詢(有分頁的效果):

select * from (select t.*,rownum rn from (select * from emp) t) where rn>(pagenumber-1)*5 and rn<=pagesize*5;

UNION 、UNION ALL

select empno from emp union select empno from emp;

UNION 去掉相同的記錄

select empno from emp union all select empno from emp;

UNION ALL 不去掉相同的記錄

select empno from emp union select sal from emp;資料型別和列數量一致就可以

多表關聯查詢:

某個欄位在多張表中有同樣的名稱,SQL語句中應該在欄位前加表名來區分

內連線:(有限制條件的交叉連線)inner join

select * from emp inner join dept on dept.deptno=emp.deptno;
select * from emp,dept where emp.deptno=dept.deptno;

外連線:(左連線left join就是以左為主,右邊right join的記錄可能為空, 右連線就是以右為主左邊的可能為空)

select empno,emp.deptno,dname from emp left join dept on emp.deptno=dept.deptno;

​ 外連線的Oracle寫法,誰有加號就以誰為主,另外一邊會有空值出現。

select e.empno,e.deptno,d.dname from emp e,dept d where e.deptno=d.deptno(+);

完全外連線:(相當於左連線和右連線的並集)full join

自連線:(顯示僱員和管理者之間的對應關係)inner join提問

select em2.ename 上層管理,em1.ename 下屬員工 from emp em1 join emp em2 on em1.mgr=em2.empno;

交叉連線:(笛卡爾積連線) 理解多表關聯查詢的原理cross join

select e.deptno 員工表的部門編號,e.empno 員工編號,e.ename 員工姓名,d.deptno 部門編號 from emp e cross join dept d;

子查詢:

單行子查詢:返回一行資料的子查詢語句。

select empno,ename,sal from emp where sal>(select min(sal) from emp) and sal<(select max(sal) from emp);

多行子查詢:

  1. 使用in運算子

  2. 使用any運算子 大於查詢結果的最小值

查詢工資大於10號部門的任意一個員工工資的其他部門的員工資訊。意思是大於10號部門員工的最低工資

select deptno,ename,sal from emp where sal > any(select sal from emp where deptno=10) and deptno!=10;
  1. 使用all運算子 大於查詢結果的最大值

關聯子查詢:

使用關聯子查詢檢索工資大於同職位的平均工資的員工資訊

select empno,ename,sal from emp f where sal>(select avg(sal) from emp where job=f.job) order by job;

巢狀查詢:

select * from emp,(select deptno from emp where job='PRESIDENT') emp2 where emp.deptno=emp2.deptno;
select * from emp where deptno=(select deptno from emp where job='PRESIDENT');

面試點:將子查詢in轉換成exists

select * from emp where deptno in (select deptno from emp where job in ('PRESIDENT','CLERK'));

exists:只有裡面的SQL語句被滿足的時候才可以顯示一條記錄

select * from emp where exists(select 4 from emp emp2 where emp2.job='PRESIDENT' and emp.deptno=emp2.deptno);

如何將重複的電話號碼去除;

  1. 找到重複的電話號
select phone from t_student group by phone having count(phone)>1;
  1. 將重複的記錄刪除
delete from t_student where id in(select id from t_student t1,(select min(id) min,phone from t_student group by phone having count(phone)>1) t2 where t1.phone=t2.phone and t1.id!=t2.min);

刪除有兩遍重複的emp2表

delete from emp2 where rowid not in (select min(rowid) from emp2 group by empno);

DQL中的函式:

函式(字元 LOWER/UPPER/LENGTH/SUBSTR/INSTR/TRIM);
select lower (ename) from emp;
select upper(lower(ename)) from emp;

substr:第一個引數是字串內容,第二個引數從第幾個開始,第三個引數表示選幾位數。

select substr(ename,0,3) from emp;

instr(字串內容,待檢查的字元)返回引數2在引數1中的位置

選擇字元A再屬性中出現的位置

select instr(ename,'A') from emp;
數學:
select round(1.1),round(1.5),trunc(1.1),trunc(1.9),mod(5,2),mod(5,3) from dual;

員工表中員工入職年限:

select sysdate-hiredate,(sysdate-hiredate)/365,trunc((sysdate-hiredate)/365) year,hiredate from emp;
日期

SYSDATE、ADD_MONTHS、LAST_DAY、MONTHS_BETWEEN

ADD_MONTHS(當前日期,增加的月份)

select add_months(sysdate,1),last_day(sysdate) from dual;
select hiredate,months_between(sysdate,hiredate),months_between(sysdate,hiredate)/12 from emp;
轉化函式

TO_DATE/TO_CHAR/TO_NUMBER/NVL/NVL2

日期格式:yyyy-mm-dd HH24:mi:ss

select sysdate,to_char(sysdate,'yyyy-mm-dd HH24:mi:ss') from dual;

alter session set nls_date_format = 'yyyy"年"mm"月"dd"日"';

字元轉日期:

insert into t1 values(to_date('2022-01-08 16:26:29','yyyy-mm-dd HH24:mi:ss'));

null值的轉化:

員工工資+獎金 nvl(p1,p2),如果p1為空返回p2,否則返回p1

select sal,nvl(comm,0) comm,sal+nvl(comm,0) money from emp;

員工獎金不為空 則加100,否則給200 nvl2(p1,p2,p3)如果p1為空,則返回p3,否則返回p2

select sal,comm,nvl2(comm,comm+100,200) from emp;
decode

decode(p1,p2,p3,...,pn)p1是資料來源,如果p1=p2 返回p3,p1=p4 返回p5... 如果都不滿足返回pn

select decode(job,'CLERK','文員','SALESMAN','銷售員','MANAGER','經理','PRESIDENT','董事長','其他') from emp;

行轉列學生成績表

select name,sum(decode(subject,'MBA',score)) MBA,sum(decode(subject,'數學',score)) 數學 from t_score group by name;

min/max/avg/sun/count/having

按照部門顯示各部門的工資

select min(sal),max(sal),sum(sal),avg(sal) from emp;

group by 和 統計函式的綜合

select deptno, min(sal),max(sal),sum(sal),avg(sal) from emp group by deptno having avg(sal)>2000;

DCL:

資料控制語言(data control language),grant、revoke;授權和回收授權

create user ychs2 identified by 123456 default tablespace tab2_data temporary tablespace tab2_temp;

user YCHS2 lacks CREATE SESSION privilege; logon denied

grant create session to ychs2;
revoke create session from ychs2;

角色

dba_role_prives 使用者角色表

dba_sys_privs 角色許可權表

dba_roles角色

查詢一個使用者有哪些角色

select * from dba_role_prives where grantee='SCOTT';

自定義角色

create role myrole;

在系統字典中查詢角色

select * from dba_roles where role='MYROLE';

為角色授權

grant create session to myrole;

查詢角色的許可權

select * from dba_sys_privs where grantee='MYROLE';

給使用者賦予角色

grant resource to ychs2;

撤銷使用者的角色

revoke resource from ychs2;

刪除角色

drop role myrole;

給使用者賦予賦予權力的權力

grant create session to ychs2 with admin option;

with admin/grant option

with admin option A使用者給B使用者授權,A使用者的許可權被收回,不影響B使用者。對於系統自帶的許可權(如connect)只能通過with admin option來授權。

with grant option A使用者給B使用者授權,A使用者的許可權被收回,B使用者也失去許可權。A使用者給B使用者授權,只能由A收回,其他人不能收回,誰給的許可權,誰來收回。

TCL:

事務控制語言(transaction control language),commit、rollback;針對臨時表空間裡面的資料。

commit:

儲存資料

savepoint、rollback to:

insert into t_student(id,name) values(4,'name4');
savepoint a4;
insert into t_student(id,name) values(5,'name5');
rollback to a4;

rollback

回退到commit 儲存前的位置

六、約束

檢視一個使用者有哪些約束

select constraint_name from user_constraints;
select constraint_name from user_constraints where table_name='EMP';

主鍵

建立表時的約束(系統預設給約束名稱)主鍵預設不能為空

create table t_student(id number(10) primary key);

檢視預設建表時的約束

select constraint_name,constraint_type from all_constraint where table_name='T_STUDENT';

建立表後的約束

alter table t_student add constraint pk_t1 primary key(id);

刪除約束

alter table t_student drop constraint pk_t1;

外來鍵

新增外來鍵:

alter table t_student add constraint fk_student_clazz foreign key(clazz) references t_clazz(id);

唯一約束

alter table t_class add constraint uniq_clazz_name unique(name);

為空的時候並沒有約束

為空約束

 alter table t_clazz modify name null;

非空約束

 alter table t_clazz modify name not null;

預設值

alter table t_student add join_date default sysdate;
alter table t_student add join_date default sysdate not null;

保證欄位永遠有值

檢查:應該放到SQL語句的最後面

無自定義名稱

alter table t_student add sex number(1) check(sex=0 or sex=1);

有自定義名稱

alter table t_student add constraint chk_student_sex check(sex in(0,1));

檢查範圍

alter table t_student add constraint chk_student_age check(age>=18 and age<30);
alter table t_student add constraint chk_student_age check(age between 18 and 30);

七、索引

是資料庫提供的一種機制,可以提高資料效能

  1. 全表掃描
  2. 索引掃描、必須要使用索引欄位

主鍵索引:建主鍵時生成

唯一索引:建唯一約束時生成

普通索引:需要手動建立

create index idx_clazz_stunumber on t_clazz(stunumber);

select * from t_student 等價於 select id,clazz,join_date,sex,age from t_student;後者節省效能;

drop index idx_clazz_stunumber;

八、檢視

通過檢視修改資料時實際上,就是在修改基本表中的資料。反之對應

虛擬的:

由sql語句組成,不佔用空間,不儲存資料。需要基表(檢視的資料來源)

簡單\複雜檢視

簡單檢視可以維護資料(插入),

create or replace view v_emp_simple as select empno,ename from emp;

with read only

create or replace view v_emp_check as select empno,ename,deptno from emp where deptno=10 with read only;

無法對只讀檢視執行 DML 操作

複雜檢視

複雜檢視是指包含函式、表示式或分組資料的檢視,當檢視子查詢包含函式或表示式時,必須為其定義列別名。

統計所有部門人數,最小,最大平均工資(獎金)

select e.deptno,d.dname,count(*) people_num, min(sal+nvl(comm,0)) min_sal,max(sal+nvl(comm,0)) max_sal,avg(sal+nvl(comm,0)) avg_sal from emp e,dept d where e.deptno=d.deptno group by e.deptno,d.dname;

建立檢視的權利

grant create view to scott;

建立檢視

create view v_emp_sal_stat as select e.deptno,d.dname,count(*) people_num, min(sal+nvl(comm,0)) min_sal,max(sal+nvl(comm,0)) max_sal,avg(sal+nvl(comm,0)) avg_sal from emp e,dept d where e.deptno=d.deptno group by e.deptno,d.dname;

查詢檢視

select * from v_emp_sal_stat;

物化檢視

佔用空間儲存檢視。手動更新資料\自動更新資料

select table_name from user_tables;

on demand手動更新

create materialized view mv_emp as select empno from emp;

更新emp表

delete from emp where empno in(1,2);已刪除2行。

物化檢視沒有變化 select * form mv_emp;

重新整理在檢視exec dbms_mview.refresh('MV_EMP');

on commit自動更新

create materialized view mv_emp refresh force on commit as select * from emp;

檢視作用

1.簡化查詢

2.靈活控制權限 可以控制到列,可以控制到一定條件下的記錄

create or replace view v_emp_basic as select empno,ename from emp where deptno=10;

相關引數

force關鍵字強制檢視

t_teacher表還未建立

create or replace force view v_teacher as select * from t_teacher;

警告: 建立的檢視帶有編譯錯誤。

檢查檢視 with check option 主要用來檢測是否滿足where條件

create or replace view v_emp_check as select empno,ename,deptno from emp where deptno=10 with check option;

往檢查檢視中插入資料插入成功

insert into v_emp_check(empno,ename,deptno) values(2,'name1',10);

插入失敗

insert into v_emp_check(empno,ename,deptno) values(3,'name2',20);

檢視 WITH CHECK OPTION where 子句違規

使用者下的檢視

select view_name from dba_views where owner='SCOTT';

在當前使用者下檢視有哪些檢視

select view_name from user_views;

九、程式

dbms_output.put_line('hello')

if else

-- Created on 2021/12/14 by WJG 
declare 
  -- Local variables here
  i integer;
begin
  -- Test statements here
  if i is null then
    dbms_output.put_line('i is null');
  else
    dbms_output.put_line(i);
  end if;
end;

if elsif else

-- Created on 2021/12/14 by WJG 
declare 
  -- Local variables here
  i integer;
begin
  -- Test statements here
  i:=99;
  if i<60 then
    dbms_output.put_line('不及格');
  elsif i<=80 then
    dbms_output.put_line('及格');
  elsif i<=90 then
    dbms_output.put_line('良');
  else 
    dbms_output.put_line('優秀');
  end if;
end;

搜尋case

select name,subject,case when score between 0 and 59 then '不及格'
                         when score between 60 and 79 then '及格'
                         when score between 80 and 89 then '良'
                         else '優秀' end
from t_score;

簡單case

select ename,case job when 'CLERK' then '文員'
                      when 'SALESMAN' then '售貨員'
                      when 'PRESIDENT' then '銷售員'
                      when 'MANAGER' then '經理'
                      else '分析師' end
from emp;

while loop

declare 
  -- Local variables here
  --i integer default 0;
  i integer:=0;
begin
  -- Test statements here
  while i<5 loop
    dbms_output.put_line('i='||i);
    i:=i+1;
  end loop;
end;

for loop

declare 
  -- Local variables here
  --i integer default 0;
begin
  -- Test statements here
  for i in 0..5 loop
    dbms_output.put_line('i='||i);
  end loop;
end;

do loop

declare 
  -- Local variables here
  --i integer default 0;
  i integer:=0;
begin
  -- Test statements here
  loop
    dbms_output.put(' '||i);
    i:=i+1;
    exit when i=5;
  end loop;
  dbms_output.put_line('');
end;

刪除字串[1,2,3,4]中對應資料庫id的值

declare 
  -- Local variables here
  ids varchar(20) :='3,5,9';--要刪除記錄的id[1,2,3,4]
  i integer;
  v_id varchar(2);-- 每一個待刪除的記錄id
  p integer; -- 逗號的位置
begin
  -- 初始化資料
  delete from t_teacher;
  for i in 1..10 loop
    insert into t_teacher values(i);
  end loop;
  -- 初始化資料結束
  -- 對ids進行處理
  if ids is null then
    dbms_output.put_line('ids is null');
  else
    loop
      p:=instr(ids,',');
      if p=0 then
        -- ids此時只有一個數據
        delete from t_teacher where id=ids;-- ids=3
        exit;
      else
        -- ids中有多個數據
        v_id:=substr(ids,1,p-1);-- 獲取待刪除的記錄id
        delete from t_teacher where id=v_id;
        ids:=substr(ids,p+1);-- 擷取刪除id之後的字串,不包含逗號
      end if;
    end loop;
  end if;
  -- 提交資料
  commit;
end;

十、遊標

顯示

通過程式碼生成

靜態\動態

授權 grant debug connect session to scott;

declare
-- 靜態遊標
   cursor c is select * from emp;
   emp_row emp%rowtype;
begin
  -- 開啟遊標
  open c;
  loop
    fetch c into emp_row;
    exit when c%notfound;-- 退出
    dbms_output.put_line(emp_row.empno||''||emp_row.ename);
  end loop;
  -- 關閉遊標
  close c;
end;
declare
-- 靜態遊標
   cursor c is select * from emp;
begin
  for emp_row in c loop
    dbms_output.put_line(emp_row.empno||''||emp_row.ename);
  end loop;
end;
declare
  -- 帶引數的遊標
  cursor c(v_empno emp.empno%type,v_ename emp.ename%type) is select empno,ename from emp where empno=v_empno; 
  v_empno emp.empno%type;
  v_ename emp.ename%type;
begin
  open c(7788,'SCOTT');
  loop
    fetch c into v_empno,v_ename;
    exit when c%notfound;
    dbms_output.put_line(v_empno||' '||v_ename);
  end loop;
  close c;
end;

---------------
declare 
  cursor cur_emp (v_job emp.job%type) is 
  select empno,ename,sal 
  from emp 
  where job=v_job;
  
  type record_emp is record
  (
    v_empno emp.empno%type,
    v_ename emp.ename%type,
    v_sal emp.sal%type
  );
  emp_row record_emp;
begin
  open cur_emp('MANAGER');
  loop
    fetch cur_emp into emp_row;
    exit when cur_emp%notfound;
    dbms_output.put_line(emp_row.v_empno||' '||emp_row.v_ename||' '||emp_row.v_sal);
  end loop;
  close cur_emp;
end;

動態遊標

declare
   -- 動態遊標型別
   type dync is ref cursor;
   -- 動態遊標
   c dync;
   emp_row emp%rowtype;
   
begin
  open c for select * from emp;
  loop
    fetch c into emp_row;
    exit when c%notfound;
    dbms_output.put_line(emp_row.empno||''||emp_row.ename);
  end loop;
  close c;
end;

隱式

在平常在進行SELECT查詢、DML操作Oracle都會自動建立宣告“隱式遊標”來處理結果資料;

declare
   
begin
  -- 執行完成後,打開了系統的隱式遊標
  update t_score set score=score+1;
  if SQL%found then
    dbms_output.put_line('更新了資料'||sql%rowcount||'條');
  end if;
  commit;
  
  if SQL%notfound then
    dbms_output.put_line('沒有資料');
  end if;
  
  if SQL%isopen=false then
    dbms_output.put_line('關閉');
  end if;
  
end;

%found , %notfound,%rowcount,%isopen

declare
  teacher_row t_teacher%rowtype;  
begin
  delete from t_teacher where id=4;
  if sql %rowcount=1 then
    dbms_output.put_line('刪除成功');
  else
    dbms_output.put_line('刪除失敗');
  end if;
end;

十一、4種可以儲存的PL/SQL程式塊

1.存貯過程procedures

帶有名稱的程式段。儲存在資料庫中的一段程式,處理一些業務,比SQL語句功能強大且靈活。

處理業務邏輯,重點是處理業務,不在於返回值,儲存過程不能被sql語句直接執行或呼叫,只能通過execute執行

備表

create table emp2 as select * from emp;

只拷貝資料不建立表

insert into emp2 select * from emp;

p_emp_sal_upgrade

create or replace procedure p_emp_sal_upgrade(i_empno  emp.empno%type,
                                              o_result out boolean) is
   -- 變數定義區
   v_sal emp.sal%type;
begin
  select case when sal between 700 and 1200 then 500
              when sal between 1201 and 1400 then 400
              when sal between 1401 and 2000 then 300
              when sal between 2001 and 3000 then 200
         else 100 end into v_sal from emp where empno=i_empno;
         
  update emp set sal=sal+v_sal where empno=i_empno;
  -- 或者
  /*if v_sal>=700 and v_sal<=1200 then 
    update emp set sal=sal+500 where empno=i_empno;
  elsif v_sal>=1201 and v_sal<=1400 then 
    update emp set sal=sal+400 where empno=i_empno;
  elsif v_sal>=1401 and v_sal<=2000 then 
    update emp set sal=sal+300 where empno=i_empno;
  elsif v_sal>=2001 and v_sal<=3000 then 
    update emp set sal=sal+200 where empno=i_empno;
  else
    update emp set sal=sal+100 where empno=i_empno;
  end if;
  */
  if sql%rowcount = 1 then 
    o_result:=true;
  else
    o_result:=false;
  end if;
  commit;
end p_emp_sal_upgrade;

2.自定義函式

重點在於返回值,可以在select語句中呼叫

create or replace function f_emp_sal_grade(i_empno emp.empno%type) return salgrade.grade%type is
  FunctionResult salgrade.grade%type;
begin
  select s.grade into FunctionResult from emp e,salgrade s where e.empno=i_empno 
  and e.sal between s.losal and s.hisal;
  return(FunctionResult);
end f_emp_sal_grade;

編譯

使用

select f_emp_sal_grade(7788) from dual;

3.程式包

把相關的儲存過程和函式放在一個包下

包頭--規範--定義物件 類似於介面

create or replace package pak_emp is

  min_sal number;
  max_sal number;
  avg_sal number;

  function f_emp_min_sal(i_deptno emp.deptno%type) return number;
  function f_emp_max_sal(i_deptno emp.deptno%type) return number;
  function f_emp_avg_sal(i_deptno emp.deptno%type) return number;

  procedure p_emp_stat(i_deptno emp.deptno%type,
                       min_sal  out number,
                       avg_sal  out number,
                       max_sal  out number);

end pak_emp;

包體--實現了規範--儲存過程和函式 類似於實現類

create or replace package body pak_emp is

  function f_emp_min_sal(i_deptno emp.deptno%type) return number is
    result number;
  begin
    select min(sal) into result from emp where deptno = i_deptno;
    return(result);
  end f_emp_min_sal;

  function f_emp_max_sal(i_deptno emp.deptno%type) return number is
    result number;
  begin
    select max(sal) into result from emp where deptno = i_deptno;
    return(result);
  end f_emp_max_sal;

  function f_emp_avg_sal(i_deptno emp.deptno%type) return number is
    result number;
  begin
    select avg(sal) into result from emp where deptno = i_deptno;
    return(result);
  end f_emp_avg_sal;

  procedure p_emp_stat(i_deptno emp.deptno%type,
                       min_sal  out number,
                       avg_sal  out number,
                       max_sal  out number) is
  begin
    select min(sal), max(sal), avg(sal)
      into min_sal, max_sal, avg_sal
      from emp
     where deptno=i_deptno;
  end p_emp_stat;

end pak_emp;

檢視

異常

系統自動丟擲,程式被動處理

-- 針對某一個員工增加指定工資,結果成功或失敗
create or replace procedure p_empno_sal_upgrade(i_empno emp.empno%type,
                                      i_money integer,
                                      o_result out varchar) is
  v_sal emp.sal%type;                                  
begin
  -- 沒有記錄系統會儲存,查出大於一條系統也會報錯
  select sal into v_sal from emp where empno=i_empno;
  update emp set sal=sal+i_money where empno=i_empno;
  if sql%rowcount = 1 then
    o_result:='success';
  else
    o_result:='failure';
  end if;  
  commit;
  exception when others then
    o_result:='error';
    dbms_output.put_line(sqlcode||'-'||sqlerrm);
  
end;

自定義異常

sqlcode/sqlerrm

-20000/ORA-20000:

sqlcode/sqlerrm

1/User-Defined Exception

create or replace procedure p_empno_sal_upgrade(i_empno emp.empno%type,
                                      i_money integer,
                                      o_result out varchar) is
  v_sal emp.sal%type;
  money_illeg exception;-- 增加工資不合理異常
  pragma exception_init(money_illeg,-20000); --關聯錯誤號和異常變數名
  param_null exception;-- 引數為空異常
begin
  -- 自定義異常
  if i_empno=7788 then
    raise_application_error(-20001,'7788使用者不允許漲工資');
  end if;
  if i_money is null or i_empno is null then
    raise param_null;
  end if;
  -- 非預定義異常
  -- 如果增加的工資不合理,則拋異常
  if i_money<100 or i_money>1000 then
    raise money_illeg;
  end if;
  -- 沒有記錄系統會儲存,查出大於一條系統也會報錯
  select sal into v_sal from emp where empno=i_empno;
  update emp set sal=sal+i_money where empno=i_empno;
  if sql%rowcount = 1 then
    o_result:='success';
  else
    o_result:='failure';
  end if;
  commit;
  exception
    -- 自定義異常
    when param_null then
      o_result:='error';
      dbms_output.put_line('引數為空'); 
      dbms_output.put_line(sqlcode||'/'||sqlerrm);
    --  非預定義異常
    when money_illeg then
      o_result:='error';
      dbms_output.put_line('引數不合理,100-1000之間'); 
      dbms_output.put_line(sqlcode||'/'||sqlerrm); 
    -- 預定義異常
    when no_data_found then
      o_result:='error';
      dbms_output.put_line('沒有資料');
    when others then
    o_result:='error';
    dbms_output.put_line(sqlcode||'/'||sqlerrm);

end;

4.觸發器

語句級觸發器

建立一個語句級的觸發器,將使用者對dept表的操作資訊儲存到dept_log表中

先建立一個表

create table dept_log(operate_tag varchar2(10),operate_time date);

建立一個觸發器,名為tri_dept

create or replace trigger tri_dept --建立一個觸發器,名為tri_dept
  before insert or update or delete -- 在查入,更新,或刪除前執行
  on  dept
declare
  -- local variables here
  var_tag dept_log.operate_tag%type;-- 建立一個變數用來記錄當前操作的型別
begin
  if inserting then
    var_tag:='插入';
  elsif updating then
    var_tag:='更新';
  elsif deleting then
    var_tag:='刪除';
  end if;
  insert into dept_log values(var_tag,sysdate); -- 執行插入操作 
end tri_dept;

行級觸發器

將t_student表中更新或插入一條資料時,自動設定反向的電話號碼

當update和insert一條資料時,觸發器啟動

列識別符號 :new.id 或:old.id 表示新插入的id值或更改前的id值

create or replace trigger trg_phone_reverse
  before insert or update 
  on t_student
  for each row
    
declare

begin
  select reverse(:new.phone) into :new.phone_reverse from dual;
end trg_phone_reverse;

alter trigger 名稱 disable/enable

替代觸發器

它的觸發時機關鍵字是instead of

與其他型別的觸發器不同的是,替代觸發器是定義在檢視上的。由於檢視有些是由多個基表連線組成的邏輯結構,所以一般不允許使用者進行DML操作(insert update delete) 使用者通過替代觸發器編寫程式碼就可以實現對構成檢視基表的操作。

建立一個多表連線檢視

create view view_emp_dept
 as select empno,ename,dept.deptno,dname,job,hiredate
 from emp,dept
 where emp.deptno = dept.deptno;

建立一個觸發器

在下面的觸發器中,如果新插入的部門編號不在dept表中,則首先向dept中插入關於新部門編號的資料行,然後再向emp表中插入記錄行

create or replace trigger tri_insert_view
  instead of insert 
  on view_emp_dept
  for each row
declare
  -- local variables here
  row_dept dept%rowtype;
begin
  select * into row_dept from dept where deptno =:new.deptno;
  /*if sql%notfound then
    insert into dept(deptno,dname)
    values(:new.deptno,:new.dname);
  end if;*/
  exception
    when no_data_found then
      insert into dept(deptno,dname) values(:new.deptno,:new.dname);
  insert into emp(empno,ename,deptno,job,hiredate)
  values(:new.empno,:new.ename,:new.deptno,:new.job,:new.hiredate);
  
end tri_insert_view;

建立一個檢視

create or replace view v_student_score as 
select st.id,st.name,st.phone,st.phone_reverse,sc.subject,sc.score 
from t_student st,t_score sc 
where st.id=sc.id;
-- 給檢視的觸發器要帶instead of
create or replace trigger trg_view_student
instead of insert
on v_student_score
for each row
declare
begin
insert into t_student(id,name,phone,phone_reverse)
values(:new.id,:new.name,:new.phone,:new.phone_reverse);
insert into t_student(id,name,subject,score)
values(:new.id,:new.name,:new.subject,:new.score);
end;

使用者事件觸發器

create or replace trigger trg_logon
after logon
on database
begin
  insert into t_log(username,logon_time) values(user,sysdate);
end;

create or replace trigger trg_logoff
before logoff
on database
begin
  update t_log set logoff_time=sysdate where username=user and logoff_time is null;
end;

十二、分割槽表

提高效能,不用全表掃描,掃描分割槽表,用到了很多表空間,用到了多個數據檔案

按照值的範圍分割槽

按照number分割槽

create table customer(id number not null primary key,
                      name varchar2(10),
                      status char(1)) 
                      
PARTITION by range(id)(
  partition customer_p1 values less than (10) tablespace tab3_data,
  partition customer_p2 values less than (20) tablespace tab4_data
);

插入資料

insert into customer(id,name,status) values(1,'name1',1);
insert into customer(id,name,status) values(2,'name2',1);
insert into customer(id,name,status) values(11,'name11',1);

檢視效果

select * from customer;

select * from customer partition(customer_p1) where id=1;
insert into customer(id,name,status) values(21,'name21',1);

按照日期分割槽

order_date

create table order_activities(id number not null primary key,
                              order_date date,
                              money number) 
                              partition by range(order_date)(
                                partition order_activities_p1 values less than (to_date('2021-12-01','yyyy-mm-dd')) tablespace tab3_data,
                                partition order_activities_p2 values less than (to_date('2021-12-22','yyyy-mm-dd')) tablespace tab4_data,
                                partition order_activities_p3 values less than (maxvalue) tablespace tab5_data
                              );  

往不同分割槽中插入資料

insert into order_activities(id,order_date,money) values(1,to_date('2021-11-01','yyyy-mm-dd'),1000);

insert into order_activities(id,order_date,money) values(2,add_months(sysdate,-1),1000);

insert into order_activities(id,order_date,money) values(3,sysdate-2,1000);

insert into order_activities(id,order_date,money) values(4,sysdate,1000);

檢視結果

select * from order_activities;

select * from order_activities partition(order_activities_p1);

select * from order_activities partition(order_activities_p2);

select * from order_activities partition(order_activities_p3);

按照值的內容分割槽,內容固定類似於列舉

按照列表分割槽

address

create table product(id number not null primary key,
                     name varchar2(20),
                     address varchar2(20))
                     partition by list(address)(
                       partition product_t1 values('heilongjiang','jilin','liaoning') tablespace tab5_data,
                       partition product_t2 values('hebei','shandong','shanxi') tablespace tab6_data
                     );

查入資料

insert into product values(1,'name1','heilongjiang');
insert into product values(2,'name2','liaoning');
insert into product values(3,'name3','hebei');

檢視分割槽

select * from product partition(product_t2);
select * from product partition(product_t1);

沒有規律採用的是雜湊演算法

create table sales(id number not null primary key,
                   name varchar2(10))
                   
                   partition by hash(name)(
                     partition sales_p1 tablespace tab3_data,
                     partition sales_p2 tablespace tab3_data
                   );
insert into sales values(1,'name1');
insert into sales values(2,'name2');
insert into sales values(3,'name3');
insert into sales values(4,'name4');
insert into sales values(5,'name5');
insert into sales values(6,'name6');
select * from sales partition(sales_p1);
select * from sales partition(sales_p2);

把上面的分割槽策略組合使用組合分割槽

操作

  1. 新增分割槽
  2. 刪除分割槽

不懂閱讀部落格https://blog.51cto.com/tianzt/171759分割槽

十三、事務

ACID:原子性,一致性,隔離性,永續性

遞迴

home表

create table home(id number not null primary key,
                  name varchar2(20),pid number);
                  
insert into home values(1,'name1',null);
insert into home values(2,'name2',1);
insert into home values(3,'name2',1);

insert into home values(4,'name4',2);
insert into home values(5,'name5',2);
insert into home values(6,'name6',3);


insert into home values(7,'name7',6);

insert into home values(8,'name8',7);
-- 找子樹
select * from home start with id=6 
connect by prior id = pid;
-- 找祖先
select * from home start with id=8 
connect by prior pid=id;

上面的sql語句可以理解為 從home表中