oracle pl/sql 程序设计 历史笔记整理
20131016 周三
oracle pl/sql 程序設計 第2章 創建并運行pl/sql代碼
- sqlplus yjkhecc/yjkhecc@10.85.23.92:1521/orcl
- 在java中調用存儲過程:
?
?
create or replace procedure t_p(l_in in out number) is
begin
l_in := 5;
end;
?
?
?
@Test
????public void test() throws SQLException
????{
????????DataSource ds = DataSourceGen.getDataSourceFromXML();
????????Connection conn = ds.getConnection();
????????int inValue = 0;
????????CallableStatement cs = conn.prepareCall("{call t_p(?)}");//注意有大括號
????????cs.setInt(1, inValue);//設置傳入的值下標從1開始
????????cs.registerOutParameter(1, Types.INTEGER);//注冊傳出的值
????????cs.executeUpdate();//執行
????????int outValue = cs.getInt(1);//獲取輸出
????????System.out.println(inValue);//輸出0
????????System.out.println(outValue);//輸出5
????????conn.close();
????}
?
- 在java中調用fucntion
?
create or replace function t_f(l_in in out number) return number is
l_c number;
begin
l_in := 5;
l_c := 2;
return l_c;
end;
?
@Test
????public void test() throws SQLException
????{
????????DataSource ds = DataSourceGen.getDataSourceFromXML();
????????Connection conn = ds.getConnection();
????????int inValue = 0;
????????CallableStatement cs = conn.prepareCall("{? = call t_f(?)}");
????????cs.setInt(2, inValue);//設置傳入的值
????????cs.registerOutParameter(1, Types.INTEGER);//注冊傳出的值-通過return返回值
????????cs.registerOutParameter(2, Types.INTEGER);//注冊傳出的值-通過參數返回值
????????cs.executeUpdate();
????????int outValue1 = cs.getInt(1);
????????int outValue2 = cs.getInt(2);
????????System.out.println(inValue);//輸出0
????????System.out.println(outValue1);//輸出2
????????System.out.println(outValue2);//輸出5
????????conn.close();
????}
?
也可以通過select t_f(1) from dual;調用,但是具有out參數的函數不可以通過sql調用。
oracle pl/sql 程序設計 第3章 語言基礎
- 在plsql中調用存儲過程:
begin t_p(); end;
調用函數:
declare a number;begin a := t_f();end; - 變量,異常,模塊的作用范圍都是在聲明他們的塊內。
create or replace package scope_demo is
g_globle number;
procedure set_globle(number_in in number);
end scope_demo;
?
create or replace package body scope_demo is
procedure set_globle(number_in in number) is
l_count pls_integer;
l_name varchar2(10) := '北京';
begin
<<localscope>>--標簽 為匿名塊命名
declare
l_use char(1) := '1';
begin
select count(1)
intoset_globle.l_count
from pub_organ o
where o.organ_name likeset_globle.l_name || '%'
and o.in_use =localscope.l_use;
dbms_output.put_line(set_globle.l_count);
end localscope;
scope_demo.g_globle := set_globle.number_in;
end set_globle;
end scope_demo;
begin
scope_demo.set_globle(2);
dbms_output.put_line(scope_demo.g_globle);
end;
- 嵌套程序,完全在生命單元內聲明的局部程序。
declare
procedure local_precedure is
begin
dbms_output.put_line('dd');
end local_precedure;
begin
for i in 1..10
loop
local_precedure();
end loop;
end;
?
- 在sql中,空字符串和null是一樣的,不管是對varchar還是char。但是在plsql中,空字符串在varchar中是和null一樣的,但是在char中不是。如:
declare
l_c char(1);
begin
l_c := '';
if (l_c is null) then
dbms_output.put_line(1);
elsif (l_c = ' ') then
dbms_output.put_line(2);--輸出2 因為自動填滿了 而在sql中不會自動填滿
end if;
end;
oracle pl/sql 程序設計 第4章 條件和順序控制
- plsql對and的多個條件之間采用短路控制,后面的條件可能不會被執行。所以不應該將邏輯放在and中處理。
- 規律:if elsif when后面肯定要跟上then。
- case語句以end結束將作為一個表達式存在,以end case結束將作為一條語句存在。
- case語句中的每個when條件如果滿足,那么之后的when將不會再被執行。相當于加了個break。
- if中使用in和like
declare
l_n number := '10';
begin
?
if(l_n in ('10','20'))
then dbms_output.put_line('in');
end if;
?
if(l_n like '10%')
then dbms_output.put_line('like');
end if;
end;
20131018 周五
?
oracle pl/sql 程序設計 第5章 用循環進行迭代處理
- 簡單循環
declare
l_c pls_integer;
begin
l_c := 0;
loop
--exit when l_c > 10;
if (l_c > 10) then
exit;
end if;
dbms_output.put_line(l_c);
l_c := l_c + 1;
end loop;
end;
?
- while循環
????declare
l_c pls_integer;
begin
l_c := 0;
while l_c <= 10 loop —while的條件可以加括號 也可以不加
dbms_output.put_line(l_c);
l_c := l_c + 1;
end loop;
end;
- for循環
?
只有數值型for循環和游標型(可以是游標,也可以直接是sql查詢)for循環,這兩種循環都要有in字句。遍歷的變量不用聲明,系統會自動生成。
?
begin
for l_c in 0 .. 10 loop –不能加括號
dbms_output.put_line(l_c);
end loop;
end;
?
?
?
begin
for l_organ in (select * from pub_organ o where o.organ_name like '北京%') loop –查詢必須用括號括起來
dbms_output.put_line(l_organ.organ_name);
end loop;
end ;
?
?
- 嵌套循環 以及從內層直接跳出
declare
i pls_integer := 0;
j pls_integer := 0;
begin
<<outerloop>>
loop
dbms_output.put_line(i);
exit when i > 10;
j := 0;
<<innerloop>>
loop
dbms_output.put_line(' ' || j);
exit when j > 10;
exit outerloop when(j = 5 and i = 5);
j := j + 1;
end loop innerloop;
i := i + 1;
end loop outerloop;
end;
?
- oracle11g開始才支持continue。
oracle pl/sql 程序設計 第6章 異常處理
- 異常出現后,如果沒有被捕捉,將會自動拋出到上層。
- 遇到未處理的異常(一直到頂層都沒有被捕捉),系統會自動加上rollback。
使用下面的處理 將會忽略異常 不會滾也不會報錯繼續執行下面的語句
exception
when others then
null;
3、
create or replace procedure t_pp as
l_n number;
begin
update pub_organ o set o.organ_name = 'dddd444ddd' where o.organ_id = 'O50649821';
l_n := 'ddd';--此處出錯 本層未捕捉 自動拋出異常到上層
end;
begin
t_pp();--此處會捕捉到下層拋出的異常
exception
when others then
rollback;--回滾
commit;
end;
- 什么時候拋出異常 什么時候捕捉?
如果存儲過程是為了其他程序來調用的,并非直接面向前臺按鈕觸發的操作。那么可以將異常拋出到上層來處理。如果是直接面向前臺按鈕觸發的操作,那么需要將異常限制在本層處理掉,不要拋出到應用層去。可以返回成功失敗信息,失敗原因等錯誤信息。 - 未綁定異常號的異常:
這種異常根據變量來定位異常,定義的兩個異常變量將是兩個異常。
declare
l_ex1 exception;
l_ex2 exception;
begin
begin
raise l_ex1;
end;
exception
when l_ex2
then dbms_output.put_line('l_ex1');
end;
?
- 綁定異常號的異常,將根據異常號來捕捉異常。
declare
l_ex1 exception;
l_ex2 exception;
pragma exception_init(l_ex1,-20111);
pragma exception_init(l_ex2,-20111);
begin
begin
raise l_ex1;
end;
exception
when l_ex2
then dbms_output.put_line('l_ex1');--此處將成功打印
end;
?
- no_data_found
no_data_found的異常號是-1403,但是作為一個特殊情況,將一個自定義的異常關聯到-1403不能捕捉到no_data_found,必須關聯到100.
declare
l_organ_id varchar2(32);
l_organ_name varchar2(500);
begin
l_organ_id := '222';
select o.organ_name into l_organ_name from pub_organ o where o.organ_id = l_organ_id;
end;
?
?
?
?
declare
l_organ_id varchar2(32);
l_organ_name varchar2(500);
err_no_organ_1403 exception;
pragma exception_init(err_no_organ_1403, -1403);
begin
l_organ_id := '222';
select o.organ_name
into l_organ_name
from pub_organ o
where o.organ_id = l_organ_id;
exception
when err_no_organ_1403 then
dbms_output.put_line('err_no_organ_1403');
end;
?
?
?
declare
l_organ_id varchar2(32);
l_organ_name varchar2(500);
err_no_organ_100 exception;
pragma exception_init(err_no_organ_100, 100);
begin
l_organ_id := '222';
select o.organ_name
into l_organ_name
from pub_organ o
where o.organ_id = l_organ_id;
exception
when err_no_organ_100 then
dbms_output.put_line('err_no_organ_100');--成功打印
end;
?
- 系統預留的可以使用異常號(保證不會跟系統已有的異常的異常號沖突):
-20NNN。 - 在exception語句中可以使用SQLCODE 和SQLERRM來獲取錯誤代碼和錯誤描述。
- raise_application_error
raise_application_error可以拋出自定義的-20NNN的異常號,并附帶自定義的字符串用來存儲錯誤信息。
raise_application_error是一個系統預定義的存儲過程,它接收一個錯誤代碼和錯誤描述,然后封裝一個又此代碼和描述組成的exception對象,然后拋出。它的好處是可以封裝錯誤信息,而自定義的exception對象無法將錯誤信息和它綁定。
declare
l_ex1 exception;
pragma exception_init(l_ex1, -20111);
begin
begin
raise_application_error(-20111, 'ddd');
end;
exception
when l_ex1 then
dbms_output.put_line('l_ex1'); --此處將成功打印"l_ex1"
dbms_output.put_line(sqlerrm); --此處將成功打印"ORA-20111: ddd"
end;
- 存儲過程或者函數內部的異常被拋出到外部,那么所有的out參數的修改將被回滾。如:
create or replace procedure t_pp(p_num in out number) as
l_ex1 exception;
pragma exception_init(l_ex1, -20111);
begin
p_num := 10;
raise_application_error(-20111, 'ddd');
end;
?
?
declare
l_num number := 0;
begin
t_pp(l_num);
exception
when others then
null;
dbms_output.put_line(l_num);--輸出0
end;
?
- dbms_utility.format_call_stack()函數獲取錯誤錯誤碼和錯誤信息,dbms_utility.format_error_backtrace ()獲取錯誤的堆棧信息。
create or replace procedure t_pp as
l_n number;
begin
update pub_organ o set o.organ_name = 'dddd444ddd' where o.organ_id = 'O50649821';
update pub_organ o set o.organ_name = '' where o.organ_id = 'O50649831';
end;
?
?
?
begin
t_pp();--此處會捕捉到下層拋出的異常
exception
when others then
dbms_output.put_line(dbms_utility.format_error_stack);
dbms_output.put_line('..................................');
dbms_output.put_line(dbms_utility.format_error_backtrace);
end;
?
最終打印為:
?
ORA-01407: 無法更新 ("YJKHECC"."PUB_ORGAN"."ORGAN_NAME") 為 NULL
?
..................................
ORA-06512: 在 "YJKHECC.T_PP", line 5
ORA-06512: 在 line 2
?
?
?
- 在exception的when中可以使用or匹配多個異常。
- 在包中定義exception
create or replace package p_errors is
my_exception exception;
pragma exception_init(my_exception, -20111);
end;
?
?
?
begin
raise_application_error(-20111, 'ddd');
exception
when p_errors.my_exception then
dbms_output.put_line('-20111');
end;
?
?
?
20131106 周三
oracle pl/sql 程序設計 第7章 使用數據
- varchar2的度量單位是字節,建表時,最大字節數是4000,在plsql中最大可達3W多。字符集: oracle的默認字符是AL32UTF8,一個中文占據3個字節。而中文編碼 ZHS16GBK一個中文占據2個字節。
查看一個中文占據多少個字節:
select lengthb('中') from dual; - oracle在sql中的數值型只有number。在plsql中可以有pls_integer等。
number:浮點小數
number(2):兩位整數
number(5,2):共5位,包含2位小數。 - 日期型有date和timestamp。date可以精確到秒。timestamp可以更精確。
- 沒有布爾類型。
- BLOB CLOB
- 聲明語法:是否常量 是否為空 默認值
l_c constant varchar2(10) not null := '2';--注意順序 - oracle支持多種類型之間的隱式轉換。常用的是varchar和number之間的轉換。date和varchar之間的默認轉換需要系統配置日期格式的參數NLS_DATE_FORMAT,所以不要使用。
- subtype
局部子類型:
declare
subtype id_type is varchar(32);
emp_id id_type;
begin
select o.organ_id
into emp_id
from pub_organ o
where o.organ_name = '江西省德興市供電有限責任公司';
dbms_output.put_line(emp_id);
exception
when no_data_found then
dbms_output.put_line('no_data_found');
when too_many_rows then
dbms_output.put_line('too_many_rows');
when others then
dbms_output.put_line('others exception happen');
end;
????包子類型:
create or replace package scope_demo is
g_globle number := 0;
procedure set_globle(number_in in number);
subtype id_type is varchar(32);
end scope_demo;
?
declare
emp_id scope_demo.id_type;
begin
select o.organ_id
into emp_id
from pub_organ o
where o.organ_name = '江西省德興市供電有限責任公司';
dbms_output.put_line(emp_id);
exception
when no_data_found then
dbms_output.put_line('no_data_found');
when too_many_rows then
dbms_output.put_line('too_many_rows');
when others then
dbms_output.put_line('others exception happen');
end;
?
- 顯示轉換
顯示轉換經常用到的是to_char to_number to_date等函數。cast語法是一個sql標準的轉換語法,不過不是很實用,因為沒有轉換格式的配置。不過cast可以把一個集合轉換成另外一個類型的集合。(集合的內容必須是一樣的)。 - cast
cast轉換的源類型和目的類型必須是全局的數據類型(使用create創建的),不能是局部定義(放在declare塊中或者包中)的。如下面代碼將不能執行:
declare
type t_names is table of varchar(200);
type t_org_names is table of varchar(200);
l_names t_names;
l_org_names t_org_names;
begin
l_names := t_names('1', '2');
for l_name in (select column_value orgName
from table(cast(l_names as t_org_names))) loop
dbms_output.put_line(l_name.orgName);
end loop;
end;
?
但是,如果使用下面代碼,將會成功。
create type t_names is table of varchar(200);
create type t_org_names is table of varchar(200);
?
cast不能轉換record和object。
20131107 周四
oracle pl/sql 程序設計 第8章字符串
- 定義字符串(char和varchar2)的時候,可以通過byte和char來限定長度的類型是字節來時字符。
表中:
create table TT
(
name VARCHAR2(32 CHAR)
?
)
plsql中:
?
declare
l_name VARCHAR2(2 CHAR);
begin
l_name := '中文';
end;
?
如果沒有設置byte和char,默認值是根據系統參數NLS_LENGTH_SEMANTICS來配置的。
?
- 因為plsql中字符串的長度大于sql中字符串的長度,所以如果講一個plsql的varchar2存到表中,可能會造成截斷。
- instr
從前往后查找:instr('12345654321','1')
從后往前查找:instr('12345654321','1',-1)
得到的結果都是從1開始的 - trim ltirm rtirm
oracle pl/sql 程序設計 第9章數字
- number是十進制的,也是唯一可以在建表時使用的。小數點前位數超出會報錯,小數點后位數超出會自動四舍五入。number是高精度的十進制,所以處理金額應該使用number。
- 整型:
pls_integer:這個是為了效率而設計的。底層的運算都是采用機器語言構建的,而number是c語言。如果pls_integer要經常和number做轉換,還是一開始就采用number好。
binary_integer:在10g之后將等同于pls_integer。盡量不使用它。
simple_integer:在原生編譯模式下能夠取得極佳的性能。比上面兩個都要快很多。但是不支持null值,沒有溢出檢查。 - 浮點數:
binary_float:二進制單精度
binary_double:二進制雙精度
simple_float和simple_double和simple_integer一樣,速度快,但是不支持null,不檢查溢出。這兩個也是二進制的。在11g才有。
注意:二進制浮點數涉及到丟失的問題。
float可以到10的38次方,double可以到10的308次方。
begin
???? dbms_output.put_line(0.95f);--binary_float 9.49999988E-001
???? dbms_output.put_line(0.95d);--binary_double 9.4999999999999996E-001
???? dbms_output.put_line(0.95);--number .95
end;
?
20131108 周五
oracle pl/sql 程序設計 第10章日期
- 常用的日期格式 YYYY-MM-DD HH:MI:SS
- extract (year|month|day|hour|minute|second from sysdate) 返回number型數值
oracle pl/sql 程序設計 第11章記錄類型
- %rowtype(對表或者游標)得到的是一個記錄類型的變量。
--定義記錄
create or replace package p_records is
type organ is record(
organ_id varchar2(30),
organ_name varchar2(80)
);
type obj is record (
obj_id varchar2(30),
obj_name varchar2(80)
);
end p_records;
?
?
?
declare
l_organ p_records.organ;
l_organ1 p_records.organ;
l_obj p_records.obj;
begin
--對記錄賦值 不能new 也沒有構造函數 只能逐個屬性進行賦值
l_organ.organ_id := '01';
l_organ.organ_name := '國家電網';
l_organ1.organ_id := '01';
l_organ1.organ_name := '國家電網';
l_obj.obj_id := '01';
l_obj.obj_name := '01';
dbms_output.put_line(l_obj.obj_name);
--失敗 賦值類型不一致
--l_obj := l_organ;
--成功
l_organ := l_organ1;
/*
--失敗 不能對整個record比較
if (l_organ = l_organ1) then
dbms_output.put_line('equal');
end if;
*/
end;
?
?
?
?
--對記錄進行賦值
declare
type ee is record(
id1 VARCHAR2(2000),
dataid VARCHAR2(255),
fieldname VARCHAR2(40),
content LONG RAW);
l_e ee;
l_d editdata%rowtype;
?
begin
--into不要求字段類型一摸一樣 可以轉換即可
--定義的字段長度不一樣也可以執行 如果執行時實際長度不夠會報錯
select * into l_e from editdata t;
l_d := l_e;
l_d.id := 'newid';
if (l_d.id = l_e.id1) then
dbms_output.put_line('賦值操作是賦值引用');--不打印
else
dbms_output.put_line('賦值操作是賦值值');--打印
end if;
--插入整個記錄 按照字段聲明順序 不是根據名稱
insert into editdata values l_d;
--可以對記錄賦null 結果就會把所有字段置null
l_d := null;
dbms_output.put_line(l_d.fieldname); --輸出空字符串
--不可以對記錄判斷為空 只能對記錄的字段逐個判斷 下面注釋會報錯
--if(l_d is null)
-- then dbms_output.put_line('null');
--end if;
end;
?
- 對記錄賦值
?
create table file_test (
file_id varchar2(32),
file_name varchar2(100),
file_path varchar2(200),
file_large number
);
?
insert into FILE_TEST
(FILE_ID, FILE_NAME, FILE_PATH, FILE_LARGE)
values
('1', '1.txt', 'e:/1.txt', 121);
?
?
declare
type my_file is record(
file_id varchar2(32),
file_name varchar2(100),
file_path varchar2(200),
file_large number);
type my_image is record(
image_id varchar2(32),
image_name varchar2(100),
image_path varchar2(200),
image_large number);
l_file my_file;
l_image my_image;
l_file_rowtype file_test%rowtype;
begin
--select into的操作 只要字段類型可以匹配即可
--即使記錄字段長度與表中的字段長度不一致也可以執行 如果實際插入時的長度不夠會報錯
select * into l_file from file_test t;
dbms_output.put_line(l_file.file_name);
select * into l_image from file_test t;
dbms_output.put_line(l_image.image_name);
select * into l_file_rowtype from file_test t;
--記錄賦值 要求記錄的類型是一樣的(不是記錄的字段的類型)
--例外:rowtype類型的"偽記錄" 可以像into一樣的賦值
--l_file := l_image;--報錯
l_file := l_file_rowtype;
l_image := l_file_rowtype;
for l_filetemp in (select * from file_test) loop
l_file := l_filetemp;
l_image := l_filetemp;
end loop;
dbms_output.put_line(l_file.file_name);
dbms_output.put_line(l_image.image_name);
end;
?
- 使用記錄執行DML操作
?
?
--不嚴格要求記錄必須是%rowtype 只要record字段與表字段匹配即可
declare
type t_unit is record(
organid VARCHAR2(32),
unit_type VARCHAR2(2),
senddept1 VARCHAR2(32),
ifsend1 CHAR(1));
l_unit t_unit;
l_unit1 t_unit;
begin
l_unit.organid := '111';
l_unit.unit_type := '1';
insert into org_unit t values l_unit;
?
--使用row關鍵字update
update org_unit t
set row = l_unit
where t.organid = '11O000000000000000000000000000';
?
delete from org_unit t
where t.organid = '29O000000000000000000000000000'
returning t.organid, t.unit_type, t.senddept, t.ifsend into l_unit1; --可以returning到記錄
?
dbms_output.put_line(l_unit1.senddept1);
end;
?
?
20131211 周三
oracle pl/sql 程序設計 第12章集合
- 關聯數組 嵌套表 VARRAY
- 特征:同質的(數據類型是相同的),一維的(暫時不支持多維,不過可以集合套集合)。
- 術語:無界:無最大長度限制。有界:有最大長度限制。稀疏:中間有空隙。緊湊:中間無空隙。
- 集合的常見作用:可以作為參數(in out皆可),返回值。可以作為record或者object的屬性。可以作為表的字段類型。
- 關聯數組示例:
declare
--限制:關聯數組只能在declare中聲明 不能全局create
--限制:關聯數組的index by只能是pls_integer或者varchar2
type t_name is table of varchar2(100) index by pls_integer;
l_t_name t_name;
begin
--不需要構造方法來聲明 也不需要extend 直接通過下標插入數據
l_t_name(0) := 'a';
l_t_name(5) := 'b';
l_t_name(9) := 'c';
l_t_name(1000) := 'd';
?
--對關聯數組進行遍歷 因為是稀疏的 所以不能使用下標++的方式遍歷
declare
l_index pls_integer;
begin
--在oracle中 對于方法或者存儲過程的調用可以不加括號 表示不傳入參數
l_index := l_t_name.first;
while (l_index is not null) loop
dbms_output.put_line(l_t_name(l_index));
l_index := l_t_name.next(l_index);
end loop;
end;
end;
- 嵌套表示例:
create or replace type t_names is table of varchar(100);
?
declare
--必須通過構造函數初始化才能使用(初始化不用new關鍵字)
family t_names := t_names();
children t_names := t_names();
parents t_names := t_names();
begin
--先extend 在根據下標插入數據 oracle下標從1開始
family.extend(5);
--extend之后默認為null
--不論是嵌套表還是關聯數組 VARRAY,對于非法下標的訪問都會報no_data_found的錯誤 extend之后就不是非法的了
if (family(3) is null) then
dbms_output.put_line('extend之后默認為null');
end if;
family(1) := 'zjf';
family(2) := 'zcx';
family(3) := 'zdw';
family(4) := 'zsy';
family(5) := 'mcw';
?
--extend默認參數是1
parents.extend;
parents(1) := 'zsy';
parents.extend;
parents(2) := 'mcw';
?
--只有嵌套表可以使用集合方法
children := family multiset except parents;
?
--此時還是緊湊的 可以這樣遍歷
for l_index in children.first .. children.last loop
dbms_output.put_line(children(l_index));
end loop;
end;
- VARRAY示例:
--varray是有界的 緊湊的 這個其他兩個正好相反 除此之外 它的用法和嵌套表基本一樣
create or replace type t_names_v is varray(3) of varchar2(100);
declare
--初始化
l_names t_names_v := t_names_v();
begin
l_names.extend(2);
dbms_output.put_line(l_names.count); --輸出2
--為了保持緊湊 delete只能全部刪除(不傳參數)
l_names.delete;
dbms_output.put_line(l_names.count); --輸出0
l_names.extend(2);
--使用trim從尾部刪除也能保持緊湊
l_names.trim;
dbms_output.put_line(l_names.count); --輸出1
end;
- Collection methods make collections easier to use, and make your applications easier to maintain. These methods include COUNT, DELETE, EXISTS, EXTEND, FIRST, LAST, LIMIT, NEXT, PRIOR, and TRIM.
- EXISTS方法
DECLARE
TYPE NumList IS TABLE OF INTEGER;
n NumList := NumList(1, 3, 5, 7);
BEGIN
n.DELETE(2); -- Delete the second element
IF n.EXISTS(1) THEN
DBMS_OUTPUT.PUT_LINE('OK, element #1 exists.');
END IF;
IF n.EXISTS(2) = FALSE THEN
DBMS_OUTPUT.PUT_LINE('OK, element #2 has been deleted.');
END IF;
IF n.EXISTS(99) = FALSE THEN
DBMS_OUTPUT.PUT_LINE('OK, element #99 does not exist at all.');
END IF;
END;
- COUNT方法
- LIMIT方法:
For nested tables and associative arrays, which have no declared size, LIMIT returns NULL. For varrays, LIMIT returns the maximum number of elements that a varray can contain. You specify this limit in the type definition, and can change it later with the TRIM and EXTEND methods. - FIRST and LAST方法
- PRIOR and NEXT方法
- EXTEND Method
This procedure has three forms:- EXTEND appends one null element to a collection.
- EXTEND(n) appends n null elements to a collection.
- EXTEND(n,i) appends n copies of the ith element to a collection.
- TRIM Method
This procedure has two forms:- TRIM removes one element from the end of a collection.
- TRIM(n) removes n elements from the end of a collection.
If you want to remove all elements, use DELETE without parameters.
- Deleting Collection Elements (DELETE Method)
This procedure has various forms:
- DELETE with no parameters removes all elements from a collection, setting COUNT to 0.
- DELETE(n) removes the nth element from an associative array with a numeric key or a nested table. If the associative array has a string key, the element corresponding to the key value is deleted. If n is null, DELETE(n) does nothing.
- DELETE(m,n) removes all elements in the range m..n from an associative array or nested table. If m is larger than n or if m or n is null, DELETE(m,n) does nothing.
- 由于實現機制的原因,trim方法會以為delete的方法刪除掉的內容還未刪除,所以兩個不要混用。
- 集合賦值
declare
family t_names := t_names();
family1 t_names := t_names();
begin
?
family.extend(5);
?
family(1) := 'zjf';
family(2) := 'zcx';
family(3) := 'zdw';
family(4) := 'zsy';
family(5) := 'mcw';
--集合賦值 類似于復制 不是引用級別的操作
family1 := family;
?
end;
?
?
?
DECLARE
TYPE last_name_typ IS VARRAY(3) OF VARCHAR2(64);
TYPE surname_typ IS VARRAY(3) OF VARCHAR2(64);
-- These first two variables have the same datatype.
group1 last_name_typ := last_name_typ('Jones', 'Wong', 'Marceau');
group2 last_name_typ := last_name_typ('Klein', 'Patsos', 'Singh');
-- This third variable has a similar declaration, but is not the same type.
group3 surname_typ := surname_typ('Trevisi', 'Macleod', 'Marquez');
BEGIN
-- Allowed because they have the same datatype
group1 := group2;
-- Not allowed because they have different datatypes
-- group3 := group2; -- raises an error
END;
?
?
- 集合的行賦值
集合的行賦值取決于行的類型,如下面的列子的行是record,那么規則是基于record的。
declare
type t_organ is table of pub_organ%rowtype;
l_organs t_organ := t_organ();
begin
l_organs.extend;
--方式1 into
select *
into l_organs(1)
from pub_organ o
where o.organ_id = 'O50649765';
dbms_output.put_line(l_organs(1).organ_name);
?
--方式2 record直接賦值
for l_organ in (select * from pub_organ o where o.organ_id = 'O50649765') loop
l_organs(1) := l_organ;
end loop;
dbms_output.put_line(l_organs(1).organ_name);
?
--至今為止 對于集合 record 集合的一行 等的賦值都是值賦值 不是引用賦值
declare
l_organ pub_organ%rowtype;
begin
l_organ.organ_name := 'organ1';
l_organs(1) := l_organ;
l_organ.organ_name := 'organ2';
dbms_output.put_line(l_organ.organ_name); --organ2
dbms_output.put_line(l_organs(1).organ_name); --organ1
end;
end;
- CAST 將集合轉換為另外一個集合,MULTISET將查詢轉換為集合。TABLE將集合轉換為查詢。TABLE只能在sql中使用。
CREATE TYPE address_book_t AS TABLE OF cust_address_typ;
?
CREATE TABLE cust_address (
custno NUMBER,
street_address VARCHAR2(40),
postal_code VARCHAR2(10),
city VARCHAR2(30),
state_province VARCHAR2(10),
country_id CHAR(2));
?
CREATE TABLE cust_short (custno NUMBER, name VARCHAR2(31));
?
CREATE TABLE states (state_id NUMBER, addresses address_array_t);
?
?
SELECT s.custno, s.name,
CAST(MULTISET(SELECT ca.street_address,
ca.postal_code,
ca.city,
ca.state_province,
ca.country_id
FROM cust_address ca
WHERE s.custno = ca.custno)
AS address_book_t)
FROM cust_short s;
21、對集合的null操作
DECLARE
type t_nested is table of varchar2(30);
type t_associative is table of varchar2(30) index by pls_integer;
type t_varray is varray(10) of varchar2(30);
l_nested1 t_nested;
l_nested2 t_nested;
l_associative t_associative;
l_varray t_varray;
BEGIN
if (l_nested1 is null) then
dbms_output.put_line('nested table is automatic null!'); --成功打印
end if;
if (l_associative is null) then
dbms_output.put_line('associative array is automatic null!'); --不打印 因為關聯數組不需要通過構造函數初始化 默認不是null
end if;
if (l_varray is null) then
dbms_output.put_line(' varray is automatic null!'); --成功打印
end if;
?
l_nested1 := t_nested();
if (l_nested1 is null) then
dbms_output.put_line('nested table is automatic null!'); --不打印
end if;
?
l_nested1 := null;
if (l_nested1 is null) then
dbms_output.put_line('nested table is automatic null!'); --打印
end if;
?
END;
?
?
- 對集合的=比較 只對嵌套表有效
?
DECLARE
TYPE dnames_tab IS TABLE OF VARCHAR2(30);
dept_names1 dnames_tab := dnames_tab('Shipping','Sales','Finance','Payroll');
dept_names2 dnames_tab := dnames_tab('Sales','Finance','Shipping','Payroll');
dept_names3 dnames_tab := dnames_tab('Sales','Finance','Payroll');
BEGIN
-- We can use = or !=, but not < or >.
-- Notice that these 2 are equal even though the members are in different order.
IF dept_names1 = dept_names2 THEN
DBMS_OUTPUT.PUT_LINE('dept_names1 and dept_names2 have the same members.');
END IF;
IF dept_names2 != dept_names3 THEN
DBMS_OUTPUT.PUT_LINE('dept_names2 and dept_names3 have different members.');
END IF;
END;
?
- You can use operators such as SET, MULTISET UNION, MULTISET INTERSECT, and MULTISET EXCEPT to transform nested tables as part of an assignment statement.
- You can also apply set operators (CARDINALITY, MEMBER OF, IS A SET, IS EMPTY) to check certain conditions within a nested table or between two nested tables
?
DECLARE
TYPE nested_typ IS TABLE OF NUMBER;
nt1 nested_typ := nested_typ(1,2,3);
nt2 nested_typ := nested_typ(3,2,1);
nt3 nested_typ := nested_typ(2,3,1,3);
nt4 nested_typ := nested_typ(1,2,4);
answer BOOLEAN;
howmany NUMBER;
PROCEDURE testify(truth BOOLEAN DEFAULT NULL, quantity NUMBER DEFAULT NULL) IS
BEGIN
IF truth IS NOT NULL THEN
DBMS_OUTPUT.PUT_LINE(CASE truth WHEN TRUE THEN 'True' WHEN FALSE THEN 'False' END);
END IF;
IF quantity IS NOT NULL THEN
DBMS_OUTPUT.PUT_LINE(quantity);
END IF;
END;
BEGIN
answer := nt1 IN (nt2,nt3,nt4); -- true, nt1 matches nt2
testify(truth => answer);
answer := nt1 SUBMULTISET OF nt3; -- true, all elements match
testify(truth => answer);
answer := nt1 NOT SUBMULTISET OF nt4; -- also true
testify(truth => answer);
howmany := CARDINALITY(nt3); -- number of elements in nt3
testify(quantity => howmany);
howmany := CARDINALITY(SET(nt3)); -- number of distinct elements
testify(quantity => howmany);
answer := 4 MEMBER OF nt1; -- false, no element matches
testify(truth => answer);
answer := nt3 IS A SET; -- false, nt3 has duplicates
testify(truth => answer);
answer := nt3 IS NOT A SET; -- true, nt3 has duplicates
testify(truth => answer);
answer := nt1 IS EMPTY; -- false, nt1 has some members
testify(truth => answer);
END;
- 臨時表
--臨時表
create global temporary table T_TEMP
(
name VARCHAR2(200)
)
on commit delete rows; --數據只在實務內部有效 不加這句代碼 是在session內部有效
?
Oracle allocates segments for a temporary table when the first INSERT into that table is issued. (This can be an internal insert operation issued by CREATE TABLE AS SELECT.) The first INSERT into a temporary table allocates the segments for the table and its indexes, creates the root page for the indexes, and allocates any LOB segments.
Segments for a temporary table are allocated in a temporary tablespace of the user who created the temporary table.
Oracle drops segments for a transaction-specific temporary table at the end of the transaction and drops segments for a session-specific temporary table at the end of the session. If other transactions or sessions share the use of that temporary table, the segments containing their data remain in the table.
?
解釋:oracle為每個事物分配單獨的塊來存儲數據 在事物結束的時候 直接把塊清除 也就是說 刪除的速度很快 插入時候也不會跟其他事物沖突 兩個事物的同一個臨時表的操作 相當于兩張單獨的表
?
- 不能直接into到集合:
?
declare
type t_names is table of varchar2(200);
l_names t_names := t_names();
begin
--下面代碼報錯
--select o.organ_name into l_names from pub_organ o;
end;
20131212 周四
oracle pl/sql 程序設計 第13章其他數據類型
- Boolean :plsql支持,sql不支持。
?
?
20131213 周五
oracle pl/sql 程序設計 第13章DML和事務管理
- 隱式游標,可以在DML語句之后獲取相關信息。
sql%found
sql%notfound
sql%rowcount
- 從DML中獲取返回信息
declare
l_organid pub_organ.organ_id%type;
l_organname pub_organ.organ_name%type;
begin
update pub_organ o
set o.organ_name = 'new organ'
where o.organ_id = 'O50649765'
returning o.organ_id, o.organ_name into l_organid, l_organname; --返回修改后的信息
dbms_output.put_line(l_organid);
dbms_output.put_line(l_organname); --new organ
?
delete from pub_stru s where s.organ_id = 'O50649765';
?
delete from pub_organ o
where o.organ_id = 'O50649765'
returning o.organ_id, o.organ_name into l_organid, l_organname; --返回刪除前的信息
dbms_output.put_line(l_organid);
dbms_output.put_line(l_organname); --new organ
?
l_organid := 'newID';
l_organname := 'newName';
--沒有的情況下 將不執行into操作 也不報錯 這和select into 不一樣
update pub_organ o
set o.organ_name = 'new organ'
where o.organ_id = 'NEVER'
returning o.organ_id, o.organ_name into l_organid, l_organname;
dbms_output.put_line(l_organid); --newID
dbms_output.put_line(l_organname); --newName
dbms_output.put_line(sql%rowcount); --0
end;
- 事物中單條語句報錯與整個事務的影響:
?
java示例:
@Test
????public void test() throws SQLException
????{
????????DataSource ds = DataSourceGen.getDataSourceFromXML();
????????Connection conn = ds.getConnection();
????????try {
????????????//禁用自動提交
????????????conn.setAutoCommit(false);
????????????//這句代碼不報錯
????????????PreparedStatement ps = conn.prepareStatement("update pub_organ o set o.organ_name = '221112' where o.organ_id = 'O50649765'");
????????????ps.executeUpdate();
????????????//這句代碼報錯報錯后數據庫會自動回滾當前出錯的代碼但是上一條的代碼不會自動回滾等待commit或者rollback代碼來處理
????????????ps = conn.prepareStatement("update pub_organ o set o.in_use = '11' where o.organ_id = 'O50649765'");
????????????ps.executeUpdate();
????????????
????????????//這句代碼執行不到
????????????ps = conn.prepareStatement("update pub_organ o set o.organ_name = 'new name' where o.organ_id = 'O50649765'");
????????????ps.executeUpdate();
????????????
????????} catch (Exception e) {
????????????// TODO Auto-generated catch block
????????????e.printStackTrace();
????????}
????????finally{
????????????//close會觸發commit 所以這里第一條語句執行成功第二條執行失敗
????????????conn.close();
????????}
????????
????}
?
plsql示例:
?
如果下面代碼是一個存儲過程:
begin
update pub_organ o
set o.organ_name = '221112'
where o.organ_id = 'O50649765';
--下面代碼報錯
update pub_organ o set o.in_use = '11' where o.organ_id = 'O50649765';
update pub_organ o
set o.organ_name = 'new name'
where o.organ_id = 'O50649765';
end;
在java中調用這個存儲過程。在執行錯誤代碼的時候報錯,而存儲過程內部沒有捕捉到異常,異常會跑出到java層,java層可以commit或者rollback,如果沒有,那么將在conn的close的時候自動commit。
所以,如果是存儲過程,應該將異常在存儲過程內部處理掉,不要拋出到應用層,應該返回錯誤信息給應用層。
如:
declare
l_message varchar2(100);
begin
update pub_organ o
set o.organ_name = '221112'
where o.organ_id = 'O50649765';
update pub_organ o set o.in_use = '11' where o.organ_id = 'O50649765';
update pub_organ o
set o.organ_name = 'new name'
where o.organ_id = 'O50649765';
exception
when others then
rollback;
l_message := 'error';
end;
?
- 自治事務:如果對一個過程設置了自治事務,那么調用它的外層和它本身使用兩個事務,互不影響。
?
oracle pl/sql 程序設計 第15章數據提取
- 游標的基本概念:
游標并不是指向數據庫表的指針,而是指向虛擬表的,這張虛擬表只在查詢期間存在,為了滿足讀一致性。游標的結果集是游標打開那個時間點的有效數據。不是fetch時的實時數據。
declare
type cur_organ is ref cursor return pub_organ%rowtype;
l_cur cur_organ;
type organ is record(
organ_id VARCHAR2(30),
organ_code VARCHAR2(30),
organ_name VARCHAR2(80),
short_name VARCHAR2(40),
organ_type VARCHAR2(10),
workplace_id VARCHAR2(30),
begin_date CHAR(8),
end_date CHAR(8),
scn INTEGER,
in_use CHAR(1));
l_organ organ;
--也可以使用下面代碼 至今為止 所有的into操作不強制要求類型是%rowtype
--l_organ pub_organ%rowtype;
begin
open l_cur for
select * from pub_organ o where o.organ_id = 'O50649765';
update pub_organ o
set o.organ_name = 'newname'
where o.organ_id = 'O50649765';
fetch l_cur
into l_organ;
--輸出"江西省全南縣供電有限責任公司" 不是newname 符合讀一致性
dbms_output.put_line(l_organ.organ_name);
close l_cur;
end;
?
- SQL查詢的結果集是存放在SGA(系統全局區域)中的。數據庫針對這個結果集維護一個指針,實現游標的功能。
- 游標變量是一個真正的變量,這個記錄,集合是不一樣的。
declare
type cur_organ is ref cursor return pub_organ%rowtype;
l_cur1 cur_organ;
l_cur2 cur_organ;
l_organ pub_organ%rowtype;
begin
--O50649765 江西省全南縣供電有限責任公司
open l_cur1 for
select * from pub_organ o where o.organ_id = 'O50649765';
?
-- 此時 兩個游標變量指向同一片內存區域
l_cur2 := l_cur1;
?
--O50649773 江西省定南縣供電有限責任公司
open l_cur2 for
select * from pub_organ o where o.organ_id = 'O50649773';
--更改l_cur2的查詢 l_cur1的結果也被改變
fetch l_cur1
into l_organ;
--輸出"江西省定南縣供電有限責任公司"
dbms_output.put_line(l_organ.organ_name);
--只需關閉一個
close l_cur1;
end;
?
- 游標屬性
sql%found
sql%notfound
sql%rowcount
sql%isopen
sql%bulk_rowcount
sql%bulk_exceptions
如果是隱式游標,那么應該在查詢代碼之后即可執行游標的相關屬性,因為中間執行了其他查詢后,sql%的信息就是新查詢的了。
?
?
- select into是典型的隱式游標。它的缺點是會拋出異常no_data_found和too_many_rows。每次使用select into都要考慮到這兩個異常的處理情況,所以可以考慮養成顯示聲明游標的習慣。
- 一個顯示游標的例子:
?
create or replace package p_cursors is
cursor cur_organ is
select * from pub_organ o where rownum < 100;
end p_cursors;
?
declare
--聲明的類型應該是游標%rowtype
l_organ p_cursors.cur_organ%rowtype;
begin
open p_cursors.cur_organ;
loop
fetch p_cursors.cur_organ
into l_organ;
--游標在達到最后一行后 讓可以fetch
--只是fetch的還是最后一行的數據
--exit應該緊接著fetch 然后才是業務處理
exit when p_cursors.cur_organ%notfound;
dbms_output.put_line(l_organ.organ_name);
end loop;
--關閉
close p_cursors.cur_organ;
exception
when others then
--關閉
close p_cursors.cur_organ;
end;
?
思考:包中的游標是session級別的嗎,支持并發嗎?
?
- 游標的關閉
oracle會盡量的在游標作用域過期的時候自動關閉游標(但是不包括游標變量),游標變量必須被顯示關閉。但是oracle在嵌套的匿名塊中不能自動關閉游標。而且包中的游標也不會自動關閉,所以要養成顯示關閉游標。?
- 游標變量一個最大的用途是可以在不同的plsql程序間傳遞查詢的結果集。這種查詢傳遞的是引用,引用指向的查詢結果集放在SGA,但是如果你使用集合來傳遞,那么集合是放在PGA的,需要復制集合數據來實現值傳遞
- sys_refcursor是一個弱類型的游標變量,系統自帶聲明。
- 游標變量存在PGA,所指向的游標對象存在SGA。聲明游標變量的時候不會創建游標對象,只有在open for操作賦予游標變量查詢語句的時候才會創建游標對象。在已經open for的基礎上再次open for,上一個游標對象如果沒有關閉,仍然會處于打開狀態,雖然已經找不到了。
- 約束:
游標變量不能在包中定義。
不能使用比值運算。如兩個游標變量是否相等。
不能給一個游標變量賦null值。
?
20131216 周一
oracle pl/sql 程序設計 第14章動態SQL和動態PL/SQL
- 代碼示例:
declare
l_organ pub_organ%rowtype;
begin
--執行查詢
execute immediate 'select * from pub_organ o where o.organ_id = :organ_id'
into l_organ
using 'O50649765';
dbms_output.put_line(l_organ.organ_name);
--執行DML
execute immediate 'create table pub_organ_' || to_char(sysdate, 'DD');
--執行PLSQL
execute immediate 'begin proc_name_' || to_char(sysdate, 'DD') || ' end;';
end;
- execute immediate后的sql可以是除了多行查詢之外的任何sql語句。
- 多行查詢使用open for。
declare
l_organ pub_organ%rowtype;
l_cur sys_refcursor;
begin
open l_cur for 'select /* zjf_flag100 */ * from pub_organ o where o.organ_name like :oname'
using '國家%';
loop
fetch l_cur
into l_organ;
exit when l_cur%notfound;
dbms_output.put_line(l_organ.organ_name);
end loop;
close l_cur;
end;
- 可以綁定變量的內容不包含表明列名where關鍵字等,因為去除綁定內容后,oracle要保證這個sql可以正確解析。不可以綁定的部分應該使用字符串拼接。綁定是為了使用sql解析的緩存,防止重復解析。
- 動態sql的out參數:在使用returnling字句的時候,需要out參數。如:
declare
l_organ_name pub_organ.organ_name%type;
begin
execute immediate 'update pub_organ o set o.organ_name = :newname1 where o.organ_id = :organ_id returning o.organ_name into :newname'
using 'newname', 'O50649765', out l_organ_name;
dbms_output.put_line(l_organ_name);
end;
- 重復的特殊情況:如果是sql語句,保證重復的參數也要用不同的占位符名字,如上面那個例子newname1和newname1,如果是plsql那么重復的參數應該使用相同的占位符名字。
- null的特殊情況:不能直接using null。但是可以使用using to_number(null)或者賦值為null的變量。
?
?
- 包的變量的有效性局限在會話級別上:
?
create or replace package p_globle is
g_num number := 0;
end;
?
create or replace function f_package_test return number is
begin
return p_globle.g_num;
end;
?
create or replace procedure p_package_test is
?
begin
p_globle.g_num := 1;
end;
?
?
public static void main(String[] args) {
????????
????????DataSource ds = DataSourceGen.getDataSourceFromXML();
????????Connection conn = null;
????????try {
????????????conn = ds.getConnection();
????????????CallableStatement cs = conn.prepareCall("{call p_package_test()}");
????????????cs.execute();
????????????
????????????
????????????cs = conn.prepareCall("{? = call f_package_test()}");
????????????cs.registerOutParameter(1, Types.NUMERIC);
????????????cs.execute();
????????????System.out.println(cs.getDouble(1));//還是那個connection 輸出1
????????????conn.close();
????????????
????????????
????????????conn = ds.getConnection();//重新建立一個連接
????????????cs = conn.prepareCall("{? = call f_package_test()}");
????????????cs.registerOutParameter(1, Types.NUMERIC);
????????????cs.execute();
????????????System.out.println(cs.getDouble(1));//輸出0
????????} catch (SQLException e) {
????????????e.printStackTrace();
????????} finally {
????????????if (conn != null) {
????????????????try {
????????????????????conn.close();
????????????????} catch (SQLException e) {
????????????????????e.printStackTrace();
????????????????}
????????????}
????????}
轉載于:https://www.cnblogs.com/xiaolang8762400/p/7078687.html
總結
以上是生活随笔為你收集整理的oracle pl/sql 程序设计 历史笔记整理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: mongoDB学习笔记(一)
- 下一篇: ERROR 1366 (HY000):