Oracle-PL/SQL基础
概述
PL/SQL簡介
pl/sql(procedural language/sql)是Oracle在標準的sql語言上的擴展,pl/sql不僅允許嵌入Sql語言,還可以定義變量和常量,允許使用條件語句和循環語句,允許使用例外處理各種錯誤,這樣使得它的功能變得更加強大。
pl/sql是面向過程的語言。
不同數據庫的SQL擴展
- PL/SQL 是Oracle數據的SQL擴展。
- SQL/PL 是DB2數據庫的SQL擴展。
- T-SQL 是SQL Server數據庫的SQL擴展。
PL/SQL的必要性:
1、提高應用程序的運行性能。
2、模塊化的設計思想。
3、減少網絡傳輸量。
4、提高安全性。
PL/SQL編寫規范
1、注釋
單行注釋–
sql>select * from emp where empno=7788; –取得員工信息
多行注釋
/………………../
2、標識符號的命名規范
(1)當定義變量時,建議用v_作為前綴 v_sal。
(2)當定義常量時,建議用c_作為前綴 c_rate。
(3)當定義游標時,建議用_cursor作為后綴emp_cursor。
(4)當定義例外時,建議用e_作為前綴 e_error。
PL/SQL塊
塊(block)是pl/sql的基本程序單元,編寫pl/sql程序實際上 就是編寫pl/sql塊。要完成相對簡單的應用功能,可能只需要編寫一個pl/sql塊;但是如果想要實現復雜的功能,可能需要在一個pl/sql塊中嵌套其他的pl/sql塊。
塊結構示意圖
pl/sql塊由三個部分構成:定義部分、執行部分、例外處理部分。
declear:定義部分是從declare開始的,這部分是可選的。定義常量、變量、游標例外、復雜數據類型。
begin:執行部分是從begin開始的,這部分是必須的。
exception:例外處理部分是exception開始的,該部分可選的。
第一個PL/SQL程序
PLSQL中的命令窗口
SQL> set serveroutput on ;--打開輸出 SQL> declare --定義部分 ,如果沒有定義,declare可以省略。 可選 ,定義常量、變量、游標、例外、復雜數據類型2 begin --程序3 dbms_output.put_line('66666666');4 end;5 /66666666PL/SQL procedure successfully completedSQL> / --表示執行上一個PL/SQL塊。66666666PL/SQL procedure successfully completedPL/SQL基礎語法
程序結構
declare說明部分(變量說明\游標聲明\例外說明) begin語句序列(DML語句) exception例外處理語句 end ; /基本變量類型
定義
基本變量類型:
char 、 varchar2、date、number、boolean、long
舉例:
var1 char(20); married boolean := false ; psal number(7,2);栗子
SQL> set serveroutput on ; SQL> SQL> declare2 -- 定義基本變量類型3 v_name varchar2(20);--varchar2 字符串類型4 v_num number(7,2);--number 數字類型5 v_date date ;--date 日期類型6 7 begin8 9 v_name :='小工匠';10 dbms_output.put_line('name:'||v_name);11 12 v_num :=999;13 dbms_output.put_line('num:'||v_num);14 15 v_date :=sysdate;16 dbms_output.put_line('數據庫時間:'||v_date);17 18 dbms_output.put_line('明天的時間:'||(v_date+1));19 end ;20 /name:小工匠 num:999 數據庫時間:22-JUN-16 明天的時間:23-JUN-16PL/SQL procedure successfully completedSQL>引用型變量 %type
定義
使用%TYPE類型的變量
舉例
v_name emp.ename%type ;栗子
打印7369員工的姓名和薪水信息
SQL> set serveroutput on ; SQL> declare2 --打印 7369 員工的姓名和薪水信息3 /* 可以使用基本類型來定義變量的類型,推薦使用引用型變量來定義變量類型*/4 --vname varchar2(20);5 --v_sal number;6 7 --定義引用變量8 v_name emp.ename%type ;9 v_sal emp.sal%type;10 begin11 --業務操作,變量賦值 (兩種賦值方式 1. v_sal := 20 第二種 into的方式 )12 select ename, sal into v_name, v_sal from emp e where e.empno = 7369;13 --輸出信息14 dbms_output.put_line(v_name||'的薪水是'||v_sal);15 end;16 /SMITH的薪水是800PL/SQL procedure successfully completedSQL>記錄型變量 %rowtype
定義
%rowtype 記錄型變量 代表表中的一行,而一行中有很多列。
舉例
emp_rec emp%rowtype記錄型變量分量的引用
手工賦值 emp_rec.ename := 'ADMIN' 或者 into 賦值之后, 使用 emp_rec.enmae獲取栗子
SQL> set serveroutput on ; SQL> declare2 --打印 7369 員工的姓名和薪水信息3 4 --定義記錄型變量 ,得到 7369一行的所有列的信息5 v_emp_rec emp%rowtype ;6 begin7 --業務sql8 select * into v_emp_rec from emp a where a.empno=7369;9 --獲取姓名和薪水,并打印10 dbms_output.put_line(v_emp_rec.ename||'的薪水是'||v_emp_rec.sal);11 end ;12 /SMITH的薪水是800PL/SQL procedure successfully completedSQL>if語句的使用
形式一
if 條件 then 語句1; 語句2; end if;形式2
if 條件 then 語句1; else 語句2; end if;形式3 注意elsif
elsif沒有e 且是連在一起的
if 條件 then 語句; elsif 條件 then 語句; else 語句; end if;栗子
/* 判斷用戶從鍵盤輸入的數字 1、如何使用if語句 2、接收一個鍵盤輸入(從鍵盤上獲取的都是字符串) */ set serveroutput on ; --接收一個鍵盤輸入 --num :地址值,含義是:在該地址上保存了輸入的值 ,獲取地址值上對應的值,需要使用 & accept num prompt'請輸入一個數字';declare--定義變量保存用戶從鍵盤輸入的數字 這里并沒有對異常進行處理,輸入字符串,會拋異常pnum number := # begin--執行if 語句進行條件判斷 提示信息 不能使用雙引號,必須使用單引號,否則報錯if pnum = 0 then dbms_output.put_line('輸入的數字是'||pnum);elsif pnum = 1 then dbms_output.put_line('輸入的數字是'||pnum);elsif pnum = 2 then dbms_output.put_line('輸入的數字是'||pnum);else dbms_output.put_line('其他數字'||pnum);end if; end; /循環語句的使用
形式1
while 條件 loop ..... end loop;當條件滿足時,執行循環體,不滿足時,退出循環體。
set serveroutput on ;declare --定義循環變量 初始值為1 記得加上變量的類型 否則報錯 v_num number :=1 ; begin while v_num <=5 loop --打印 v_numdbms_output.put_line(v_num);--變量+1 不能使用v_num++的形式,oracle不支持這種寫法v_num :=v_num+1 ;end loop; end; /形式2
loop exit [when 條件]; ..... end loop;條件成立時,退出循環體,不成立時執行循環體。
set serveroutput on ; declare --定義循環變量v_num number :=1 ; begin loop exit when v_num>10 ; --記得加 標點符號 dbms_output.put_line(v_num);v_num := v_num+1;end loop ; end; /推薦使用第二種,因為在控制游標的時候比較方便。
形式3
for i in 1 .. 10 loop 語句; end loop; set serveroutput on ; declare v_num number :=1 ; begin for i in 1 .. 5 loop -- .. 前后可以有空格 也可以沒有dbms_output.put_line(v_num);v_num := v_num+1;end loop; end; /游標
游標的概念
游標(游標):一個結果集
不能把一個集合賦值給一個基本型變量,否則會拋出 too many rows的異常。
可帶參數 ,可不帶參數。
游標的語法
cursor 游標名 [(參數名 數據類型[,參數名,數據類型]...)] is select 語句;比如
cursor c1 is select enama from emp;操作游標的步驟
打開游標
open c1;(打開游標,執行查詢 即執行游標的查詢語句)取一行游標的值
fetch c1 into v_name;( 取一行到變量中)最開始時,游標指向集合的第一條記錄,記錄返回后,指針移動到下一條記錄。
關閉游標
close c1 ;(關閉游標釋放資源)栗子
使用游標查詢員工姓名和工資,并打印。
Loop循環游標
推薦寫法
....Loop Fetch 游標名 InTo 臨時記錄或屬性類型變量;Exit When 游標名%NotFound;End Loop;..... /*使用游標查詢員工姓名和工資,并打印*/ set serveroutput on ;declare --定義 游標 推薦將游標和游標對應的變量寫在一塊,比較好維護 cursor cemp is select e.ename , e.sal from emp e ; --定義游標對應的變量 這里使用引用型變量 v_name emp.ename%type ; v_sal emp.sal%type;begin --打開游標open cemp ;--循環獲取游標中的值loop --取一條數據fetch cemp into v_name,v_sal ; -- into 后變量的順序一定要和定義游標時select的字段對應exit when cemp%notfound ;--打印dbms_output.put_line(v_name||'的工資是'||v_sal);end loop;--關閉游標if cemp%isopen thenclose cemp;dbms_output.put_line('Closing...');end if; end; /或者
For 循環游標
循環游標隱式打開游標,自動滾動獲取一條記錄,并自動創建臨時記錄類型變量存儲記錄。處理完后自動關閉游標。
For 變量名 In 游標名 Loop數據處理語句;End Loop; /*使用游標查詢員工姓名和工資,并打印*/ set serveroutput on ;declare --定義 游標 推薦將游標和游標對應的變量寫在一塊,比較好維護 cursor cemp is select e.ename , e.sal from emp e ; --定義游標對應的變量 這里使用引用型變量 v_name emp.ename%type ; v_sal emp.sal%type;begin --循環獲取游標中的值for c in cemp LOOPselect c.ename , c.sal into v_name,v_sal from dual;dbms_output.put_line(v_name||'的工資是'||v_sal);end loop;end; /實例:給員工漲工資
/* 給員工漲工資。總裁漲1000,經理漲800,普通員工漲400; */ declare --定義游標cursor c_emp is select empno,empjob from emp;--定義游標對應的變量p_empno emp.empno%type;p_empjob emp.empjob%type; begin--打開游標open c_emp;--取出一個員工loopfetch c_emp into p_empno,p_empjob;exit when c_emp%notfound;--判斷職位if p_empjob = 'PRESIDENT' then update emp set sal=sal+1000 where empno=p_empno;elsif p_empjob = 'MANAGER' then update emp set sal= sal+800 where empno = p_empno;else update emp set sal=sal+400 where empno=p_empno;end if;end loop;--關閉游標 if c_emp%isopen then close c_emp;end if ;--提交事務,oracle默認的隔離級別是read committed ,不同連接只能讀取提交之后的;commit;dbms_output.put_line('漲工資完成'); end; /游標的屬性
游標的4個屬性
- %Found :Fetch語句(獲取記錄)執行情況 True or False
- %NotFound : 最后一條記錄是否提取出 True or False
- %ISOpen : 游標是否打開True or False
- %RowCount :游標當前提取的行數
游標數的限制
Oracle默認的一個會話最多可以打開300個游標.
可以通過使用 show parameter cursors; (表示模糊查詢 %cursors%)
查看包含cursors的參數設置
修改游標數的限制
使用DBA權限的用戶
alter system set open_cursors=400 scope = both;其中scope的取值:both,memory,spfile
- memory:表示只更改當前實例,不更改參數文件
- spfile:表示只更改參數文件,不更改當前示例,數據庫服務需要重啟
- both:表示上邊兩個同時更改
帶參數的游標
注意 定義(帶參數) 和打開游標(傳遞參數)時的區別。其余的和無參的游標一樣。
set serveroutput on ;declare --定義帶參數的游標 cursor cemp (dno number) is select ename from emp where deptno =dno; --定義游標中對應的變量 v_name emp.ename%type;begin--打開游標 傳入對應的入參open cemp(10);--loop循環 遍歷游標loop fetch cemp into v_name;exit when cemp%notfound;dbms_output.put_line(v_name);end loop;close cemp; end; /顯式游標和隱式游標
上面介紹的是顯式游標,下面說下隱式游標
DML操作和單行SELECT語句會使用隱式游標,它們是:
- 插入操作:INSERT
- 更新操作:UPDATE
- 刪除操作:DELETE
- 單行查詢操作:SELECT … INTO …
隱式游標的名字為SQL,這是由ORACLE 系統定義的。
對于隱式游標的操作,如定義、打開、取值及關閉操作,都由ORACLE 系統自動地完成,無需用戶進行處理。用戶只能通過隱式游標的相關屬性,來完成相應的操作。
格式調用為: SQL%
隱式游標可以使用名字SQL來訪問,但要注意,通過SQL游標名總是只能訪問前一個DML操作或單行SELECT操作的游標屬性。所以通常在剛剛執行完操作之后,立即使用SQL游標名來訪問屬性。
游標的屬性有四種,如下所示。
隱式游標的屬性 返回值類型 意 義 SQL%ROWCOUNT 整型 代表DML語句成功執行的數據行數 SQL%FOUND 布爾型 值為TRUE代表插入、刪除、更新或單行查詢操作成功 SQL%NOTFOUND 布爾型 與SQL%FOUND屬性返回值相反 SQL%ISOPEN 布爾型 DML執行過程中為真,結束后為假 DECLARE v_rows NUMBER; BEGIN --更新數據 UPDATE employees SET salary = 30000 WHERE department_id = 90 AND job_id = 'AD_VP'; --獲取默認游標的屬性值 v_rows := SQL%ROWCOUNT; DBMS_OUTPUT.PUT_LINE('更新了'||v_rows||'個雇員的工資'); --刪除指定雇員;如果部門中沒有雇員,則刪除部門 DELETE FROM employees WHERE department_id=v_deptno; IF SQL%NOTFOUND THEN DELETE FROM departments WHERE department_id=v_deptno; END IF; END;例外
例外的概念
在oracle中錯誤被叫做例外:分為系統例外和自定義例外。
系統例外 比如:
- No_data_found 沒有找到數據
- Too_many_rows select..into語句匹配多個行
- Zero_Divide 被零除
- Value_error 算數或轉換錯誤,算術錯誤比如說負數開平方
- Timeout_on_resource 在等待資源時發生超時,常見于分部署數據庫。
系統例外之no_data_found
/*系統例外 no_data_found*/ set serveroutput on ; --查詢empno為222的姓名 declare --定義引用型變量 v_name emp.ename%type;begin --業務sqlselect ename into v_name from emp where empno=222; exception when no_data_found then dbms_output.put_line('no data found');when others then dbms_output.put_line('others exception'); end; /系統例外之too_many_rows
/*系統例外 too_many_rows */ set serveroutput on ; --查詢 10號部門的員工 declare --定義引用型變量 v_name emp.ename%type;begin --業務sqlselect ename into v_name from emp where deptno=10; exception when too_many_rows then dbms_output.put_line('too_many_rows');when others then dbms_output.put_line('others exception'); end; /系統例外之zero_divide
/*系統例外 zero_divide */ set serveroutput on ; declare --定義引用型變量 v_num number;begin --業務sqlv_num := 1/0;exception when zero_divide then dbms_output.put_line('zero_divide'); dbms_output.put_line('0不能做除數');when others then dbms_output.put_line('others exception'); end; /系統例外之value_error
/*系統例外 value_error */ set serveroutput on ; declare --定義引用型變量 v_num number;begin --業務sqlv_num := 'xiaogongjiang';exception when value_error then dbms_output.put_line('value_error');when others then dbms_output.put_line('others exception'); end; /自定義例外
步驟
栗子
--自定義例外:(沒有查找到的例外no_emp_found) declare cursor c_emp is select ename from emp where deptno=50;p_ename emp.ename%type;--定義一個例外no_emp_found exception; beginopen c_emp;--獲取一條記錄fetch c_emp into p_ename;--如果沒有查到則拋出自定義例外if c_emp%notfound then raise no_emp_found;end if;--此處,當前一句拋出例外執行完exception后,--oracle會自動啟動一個pmon(process monitor)的一個進程--將pl/sql程序中未關閉的資源釋放--所以 close c_emp; 還是會執行的close c_emp;--捕獲例外exception when no_emp_found then dbms_output.put_line('沒有該部門下的員工');when others then dbms_output.put_line('其他例外'); end; /PL/SQL調測
可以在pl/sql工具中 新建測試窗口,調測過程和調測存過的方式一樣,可以一步一步的跟蹤sql執行的過程。
案例
運用瀑布模型完成PLSQL程序的設計
瀑布模型
- 1.需求分析
- 2.設計
- 2.1概要設計
- 2.2詳細設計
- 3.編碼coding
- 4.測試Testing
- 5.上線(部署)
拿到一個需求后,不找著急寫程序,先分析明白了
- sql語句
- 變量初始值
- 變量如何獲取
- ….
案例:統計每年入職的員工人數
分析過程:
每年入職的員工人數
1.所有的年份集合–>定義cursor保存
2.每個員工的入職年份–>定義v_hiredate保存
3.每年入住的人數總和–>定義v_count_XX保存
先把大框架打起來
declare ---定義變量balabala begin ---業務邏輯 end; /然后再一步一步的填充業務邏輯。
set serveroutput on ; declare --入職年份的游標 cursor hiredate_cursor is select to_char(hiredate,'yyyy') from emp;--定義入職年份 v_hiredate varchar2(4);--定義每個年份入職人員總數 v_count_80 number :=0; v_count_81 number :=0; v_count_82 number :=0; v_count_87 number :=0;begin--打開游標open hiredate_cursor ;--loop循環遍歷游標loop --取值fetch hiredate_cursor into v_hiredate ;exit when hiredate_cursor%notfound ;if v_hiredate = '1980' then v_count_80 :=v_count_80+1 ;elsif v_hiredate = '1981' then v_count_81 :=v_count_81+1 ;elsif v_hiredate ='1982' then v_count_82 :=v_count_82+1;elsif v_hiredate ='1987' then v_count_87 :=v_count_87+1;end if ;end loop;--關閉游標close hiredate_cursor;dbms_output.put_line('總共入職人數:'||(v_count_80+v_count_81+v_count_82+v_count_87));--加括號dbms_output.put_line('80入職的:'||v_count_80);dbms_output.put_line('81入職的:'||v_count_81);dbms_output.put_line('82入職的:'||v_count_82);dbms_output.put_line('87入職的:'||v_count_87); end; /案例:員工漲工資問題
案例2:漲工資問題,從最低工資的員工開始漲起,沒人漲10%,工資總額不能超過50000,返回漲工資的人數和漲后的工資總額.
/* 分析: 1、用到的sql語句:select empno,sal from emp order by sal;select sum(sal) into totalsal from emp; 2、需要聲明的變量:工資總額:totalsal 漲工資人數:count 3、循環推出的條件:工資總額>5W or 全部員工都漲完工資*/declarecursor cemp is select empno,sal from emp order by sal;p_no emp.empno%type;p_sal emp.sal%type;countemp number:=0;--漲工資人數totalsal emp.sal%type; begin--獲取初始工資總額select sum(sal) into totalsal from emp;open cemp;--判斷當前工資總額是否大于5Wif totalsal<50000 then loopfetch cemp into p_no,p_sal;exit when cemp%notfound;--獲取當前員工漲工資后的工資總額--如果工資總額超過5W直接退出循環exit when (totalsal+p_sal*0.1)>50000;update emp set sal=sal*1.1 where empno=p_no;--漲工資人數加1countemp:=countemp+1;end loop;end if;close cemp;commit;dbms_output.put_line('共有'countemp'人漲工資,工資總額為:'totalsal); end; /案例:涉及兩張表的員工漲工資問題
declare--獲取所有部門cursor c_dept is select deptno from dept;--各部門編號p_dno dept.deptno%type;--各部門總金額p_totalsal number;--各工資分段人數:num1 number;num2 number;num3 number;--定義一個游標存放該部門下所有員工(帶參數)cursor c_emp(dno number) is select sal from emp where deptno = dno;--員工的薪水p_sal number; begin--打開部門游標open c_dept;loop--部門循環fetch c_deptinto p_dno;exit when c_dept%notfound;--初始化變量:p_totalsal := 0;num1 := 0;num2 := 0;num3 := 0;-- --獲取本部門下所有員工,打開員工游標open c_emp(p_dno);loop--員工循環fetch c_emp into p_sal;exit when c_emp%notfound;if p_sal < 3000 thennum1 := num1 + 1;elsif p_sal >= 3000 and p_sal <= 6000 thennum2 := num2 + 1;elsif p_sal > 6000 thennum3 := num3 + 1;end if;--獲取總金額p_totalsal := p_totalsal + p_sal;end loop;close c_emp;--保存統計結果到sal_msginsert into sal_msg values (p_dno, num1, num2, num3, p_totalsal);end loop;close c_dept;commit;dbms_output.put_line('統計完成'); end; /總結
以上是生活随笔為你收集整理的Oracle-PL/SQL基础的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Oracle-trigger触发器解读
- 下一篇: Oracle优化01-引起数据库性能问题