990 likes | 1.33k Views
第14章 PL/SQL 语言基础. 一、例子 DECLARE N CHAR(8) ; SAL NUMBER(5) ; I NUMBER(2) ; BEGIN SELECT SALARY INTO SAL FROM EMPLOYEES WHERE EMPLOYEE_ID='201' ; IF SAL <10000 THEN DBMS_OUTPUT.PUT_LINE(TO_CHAR(SAL)||' 太少') ; ELSE
E N D
第14章 PL/SQL语言基础 一、例子 DECLARE N CHAR(8); SAL NUMBER(5); I NUMBER(2); BEGIN SELECT SALARY INTO SAL FROM EMPLOYEES WHERE EMPLOYEE_ID='201'; IF SAL <10000 THEN DBMS_OUTPUT.PUT_LINE(TO_CHAR(SAL)||' 太少'); ELSE DBMS_OUTPUT.PUT_LINE(TO_CHAR(SAL)||' 还可以'); END IF; END;
二、PL/SQL简述 1. PL/SQL块及程序 PL/SQL是一种过程化编程语言。用PL/SQL定 义块可将多个SQL编制成程序(程序过程、函数 触发器等)。程序是由若干PL/SQL块组成。 2. PL/SQL块结构与用途 PL/SQL块由三部分组成: 定义部分:定义所处理的变量、常量、游标等。 可执行部分:SQL语句及控制结构的PL/SQL语句 异常处理部分:对执行过程中的错误进行处理。 块可以嵌套。
块结构: [DECLARE 说明部分 ] BEGIN 语句(SQL或PL/SQL语句) [EXCEPTION 错误处理程序 ] END 3. PL/SQL程序分类 由基本PL/SQL块组成PL/SQL程序。程序有: 无名块:它嵌入在某个应用中的PL/SQL块。 存储过程或函数:命名了的PL/SQL块,可以带
参数,并重复调用,是数据库对象。 包:是命名PL/SQL块,由一组相关的过程、函 数和标识符组成。是数据库对象。 触发器:与表相关联的存储过程。一表最多12个。 4. PL/SQL程序运行环境 可以有多个:SQL Plus Worksheet,SQL PLUS 等。 在SQL PLUS环境中,第一句是DECLARE或 BEGIN就识别为是PL/SQL程序。它只能以“/”表 示程序结束。
三、PL/SQL基础 • 标识符 标识符是用户定义的符号串,用来命名变量、常 量、过程等。 标识符以字母开头,后跟数字(0-9)或特殊字符$、 #和_。长度不超过30,不能是PL/SQL的保留字, 不能有空格。 例1:合法标识符:A34、BB#,D_123DF 不合法标识符:5A$、#ABC、END。 2. 变量及变量说明 变量是表示要处理数据项的名称。变量名用标识 符来表示。 变量在使用之前必须用DECLARE进行说明。
变量说明: 变量名 [CONSTANT] 类型 [NOT NULL][:=值][DEFAULT SQL表达式]; 例1: DECLARE aa char(5) not null:=‘TEST’; bb number(3) default 5; cc char(4):='THIS'; dd constant char(4) :=‘SWJ’; --不能赋值 begin DBMS_OUTPUT.PUT_LINE(aa||' '||to_char(bb)||' '||cc||' '||DD); end; 说明:非空时要有初始值,NOT NULL在前,缺省 值在后;每个变量说明占一行,且以分号“;”结 束。变量说明要在DECLARE以后,BEGIN之前。
变量或常量类型 数值型:NUMBER(p,s),INTEGER,FLOAT,DEC 字符型:CHAR(n),VARCHAR2(n),RAW(n) 日期型:DATE 布尔型:BOOLEAN(TRUE或FALSE) 大数据类型:BFILE,BLOB,CLOB,NCLOB 4. 数据类型转换 显示转换: TO_CHAR(),TO_NUMBER(), RAWTOHEX(), ,HEXTORAW(),ROWIDTOCHAR(),TO_DATE()
6. 表达式 表达式是由变量、常量、列名、函数和运算符 结合的有意义式子。 数值表达式:数值运算符、变量、常量、函数等 数值运算符:+、-、*、/、**、() 例2: DECLARE A INT :=4; B FLOAT:=4.324; X FLOAT; BEGIN X:= SIN(3)*10+2**8-A*10*(10-B**2); DBMS_OUTPUT.PUT_LINE(TO_CHAR(X)); END;
字符表达式: 字符运算符: ||(合并) 关系表达式:关系表达式的结果是TRUE和FALSE 关系运算符: <> != < > <= >= = LIKE IN BETWEEN…AND… <表达式 > <关系运算符> <表达式> 例3: ABC > ‘123’ 123<>345 ‘THIS’ LIKE ‘T%’ X BETWEEN 10 AND 20 ‘A’ IN (‘SS’,’DD’,’AD’) FALSE SIN(X)+10 > A**2+B
逻辑表达式 逻辑运算符:NOT AND OR <关系表达式> <逻辑表达式> <关系表达式> 例4: NOT (A >10 AND B<10) A+10>100 AND CCC LIKE ‘C%’ 例5: DECLARE A INT :=4; B FLOAT:=4.324; X FLOAT; C BOOLEAN; BEGIN X:= SIN(3)*10+2**8-A*10*(10-B**2); C:='A' IN ('SS','DD','AD'); IF C OR A<B THEN DBMS_OUTPUT.PUT_LINE(TO_CHAR(X)); END IF; END;
7. 变量赋值 变量名:=与变量同类型的表达式; 例6: DECLARE C1 CHAR(34) ; --赋给C1的字符串不能大于34 N1 NUMBER(3); N2 NUMBER(2); B1 BOOLEAN; BEGIN N1:=10; N2:=SIN(N1)**2+20.30; C1:=TO_CHAR(N2)||’ TEST’; B1:= C1 LIKE ‘%T’; END;
8. 注释行 单行注释:在一行的任何位置以“--字符串” 多行注释:/* 注释内容 */ 例7:DECLARE C1 CHAR(34); --字符变量,长度不超过34 N1 NUMBER(3); --数字型变量 BEGIN /* 赋值语句范例: 变量类型要与表达式类型一致 */ N1:=10; C1:=TO_CHAR(N1)||' TEST'; END;
9. PL/SQL中的语句 PL/SQL块中每一行一条语句,并且必须以分号 “;”结束。 变量或常量说明语句、 变量赋值语句、CASE语 句和IF语句、 各种循环语句、数据处理语句SIDU、 事务处理语句、 DBMS_OUTPUT.PUT_LINE(字 符串)、 游标语句等。 COMMIT、ROLLBACK EXECUTE IMMEDIATE 动态串 SELECT…INTO 变量名表|记录名FROM… 每个块由若干语句组成。若干块组成程序。
四、PL/SQL控制结构 三种程序结构:顺序、选择和循环 • 顺序结构 顺序结构是指执行过程按所写程序的顺序执行。 例1: DECLARE VV CHAR(20); SAL NUMBER(4); BEGIN SELECT LAST_NAME INTO VV FROM EMPLOYEES WHERE EMPLOYEE_ID=201; DBMS_OUTPUT.PUT_LINE(VV); SAL:=10; END;
例2:DECLARE JID VARCHAR2(10):='AD_VP'; JTIT CHAR(30); BEGIN SELECT JOB_TITLE INTO JTIT FROM JOBS WHERE JOB_ID=JID; DBMS_OUTPUT.PUT_LINE(JTIT); END; 说明:JID的类型与JOB_ID的类型要完全一致, 且长度要一样。改为JID CHAR(10)将出错。 可改为:JID JOBS.JOB_ID%TYPE:=‘AD_VP’;
条件 语句1 语句2 IF 条件 THEN 语句序列; ENDIF; “条件”为逻辑表达式或关系表达式,“条件”为TRUE时执行“语句序列”,为FALSE时执行END IF后面的语句。 2. IF选择结构 IF 条件 THEN 语句序列1 ELSE 语句序列2 END IF;
IF 条件1 THEN 语句序列1 ELSIF 条件2 THEN 语句序列2 ELSE 语句序列3 END IF; 例1: DECLARE N1 NUMBER:=11; N2 NUMBER; BEGIN IF N1>10 THEN N2:=N1+10; DBMS_OUTPUT.PUT_LINE(TO_CHAR(N2)); END IF; END;
例2:嵌套选择结构 DECLARE N1 NUMBER:=11; N2 NUMBER DEFAULT 10; BEGIN IF N1>10 THEN IF N2<N1 THEN N2:=N1+10; DBMS_OUTPUT.PUT_LINE(TO_CHAR(N2)); END IF; END IF; END; 说明:嵌套必须是完全嵌套,可以是任何选择结构 IF_END IF、IF_ELSE_END IF 等。
例3:IF…THEN…ELSE结构 DECLARE N1 NUMBER:=9; N2 NUMBER:=19; BEGIN IF N1>10 THEN N2:=N2+10; ELSE N2:=N1*N1; END IF; DBMS_OUTPUT.PUT_LINE(TO_CHAR(n2)); END;
例4: IF…THEN…ELSIF…END IF结构 DECLARE N1 NUMBER:=10; N2 NUMBER:=20; CC CHAR(20); C2 DATE:=TO_DATE('1999-1-1','YYYY-MM-DD'); BEGIN DBMS_OUTPUT.PUT_LINE(TO_CHAR(C2)); IF N1>10 THEN N2:=N1+10; ELSIF N1 BETWEEN 7 AND 12 THEN N2:=SQRT(N1)*2; ELSIF C2>=DATE'1998-1-1' THEN CC:=TO_CHAR(SYSDATE); ELSE CC:='NO RESULT'; END IF; DBMS_OUTPUT.PUT_LINE(CC||' '||TO_CHAR(N2)); END;
说明: 1. 当“变量”的值与某个表达式值相同时,将执行相应的语句序列 2. 当变量与所有表达式不等时执行序列N+1 3.语句序列1到语句序列N+1中只能执行一个语句序列。 3. CASE选择结构 CASE 变量 WHEN 表达式1 THEN 语句序列1; WHEN 表达式2 THEN 语句序列2; …… WHEN 表达式N THEN 语句序列N; [ELSE 语句序列N+1;] END CASE
例1:DECLARE C1 CHAR(1):='F'; N1 NUMBER:=2; N2 NUMBER:=35; NC NUMBER; BEGIN NC:=ASCII(C1); CASE NC WHEN N2*N2 THEN N1:=N2*N2; DBMS_OUTPUT.PUT_LINE('N1='||TO_CHAR(N1)); WHEN N2+N2 THEN N1:=N2+N2; DBMS_OUTPUT.PUT_LINE('N1='||TO_CHAR(N1)); ELSE DBMS_OUTPUT.PUT_LINE('N1='||TO_CHAR(N1)); DBMS_OUTPUT.PUT_LINE('NC='||TO_CHAR(NC)); END CASE; END;
条件 语句序列 4. 循环结构 循环结构是指按照指定的逻辑条件循环执行一组 命令。有三种循环:LOOP-EXIT-END;LOOP- EXIT-WHEN-END;WHILE-LOOP-END和FOR- IN-LOOP-END。 当条件为真时,执行语句序列,直到条件为假。 条件是任何合法的逻辑表达式或关系表达式。
例1:计算1+2+3…+100 DECLARE I INT :=1; S INT:=0; BEGIN LOOP S:=S+I; IF I=100 THEN EXIT; END IF; I:=I+1; END LOOP; DBMS_OUTPUT. PUT_LINE(TO_CHAR(S)); END; 4.1 LOOP-EXIT-END循环 LOOP 语句序列 [EXIT] END LOOP 说明:执行EXIT时从循环 中退出,在条件语句中执 行EXIT。该循环中,必须 有EXIT,否则就会成为“死 循环”。EXIT只能在循环。
4.2 LOOP-EXIT WHEN-END循环 LOOP 语句序列 EXIT WITH 条件 --等价IF 条件 THEN EXIT,可用在其它循环 END LOOP 例2:计算10! DECLARE I INT :=1; S INT:=1; BEGIN LOOP S:=S*I; EXIT WHEN I=10; I:=I+1; END LOOP; DBMS_OUTPUT.PUT_LINE(TO_CHAR(S)); END;
4. 3 WHILE-LOOP-END循环WHILE 条件 LOOP 每次循环前计算条 语句序列; 件,为TRUE,执 END LOOP; 行语句,否则不。 例3:计算s=1*2+2*3+…+N*(N+1),当N=50的值。 DECLARE I INT :=1; S INT:=0; BEGIN WHILEI<=50LOOP S:=S+I*(I+1); I:=I+1; END LOOP; DBMS_OUTPUT.PUT_LINE(TO_CHAR(S)); END;
例4:在WHILE循环中使用EXIT或EXIT WHEN 计算S=1*2*3+2*3*4+…+N*(N+1)*(N+2);当N=40 DECLARE I INT :=1; S INT:=0; BEGIN WHILE TRUE LOOP S:=S+I*(I+1)*(I+2); EXIT WHEN I=40; I:=I+1; END LOOP; DBMS_OUTPUT.PUT_LINE(TO_CHAR(S)); END; 说明:EXIT和EXIT WHEN可用在任何循环内, 且只能在循环内。
4.4 FOR-IN-LOOP-END循环 FOR 循环变量 IN [REVERSE]下界..上界 LOOP 语句序列; END LOOP; 说明: • 循环变量被隐式说明为BINARY-INTEGER,也可显式说明。 • 步长1或-1(REVERSE),循环次数:上界-下界+1; • EXIT或EXIT WHEN可用在FOR循环中; • 已知循环次数时,可用FOR循环,也可用其它 • 末知循环次数不能用FOR循环 • 上界或下界可以为表达式。
例5:显示20到50的平方根的值及它们的和。 DECLARE S FLOAT :=0; BEGIN FOR I IN 20..50 LOOP DBMS_OUTPUT.PUT_LINE(TO_CHAR(SQRT(I),'9.9999')); S:=S+SQRT(I); END LOOP; DBMS_OUTPUT.PUT_LINE('S= '||TO_CHAR(S)); END; 说明:循环变量I不用说明。
例6: 步长-1。加REVERSE。 说明:加REVERSE,DECLARE S FLOAT :=0; BEGIN FOR I IN REVERSE 20..50 LOOP DBMS_OUTPUT.PUT_LINE(TO_CHAR(SQRT(I),'9.9999')); S:=S+SQRT(I); END LOOP; DBMS_OUTPUT.PUT_LINE('S= '||TO_CHAR(S)); END; 上界仍要大于下界。
例7:上下界为表达式的FOR循环 DECLARE S FLOAT :=0; N INT :=10; BEGIN FOR I IN REVERSE2*N-1..N*N LOOP DBMS_OUTPUT.PUT_LINE(TO_CHAR(SQRT(I),'9.9999')); S:=S+SQRT(I); END LOOP; DBMS_OUTPUT.PUT_LINE('S= '||TO_CHAR(S)); END;
例8: 生成字符串SWJ001-SWJ120 declare n number(1); sn varchar2(8); sn1 char(3); begin for n in 1..120 loop if n<10 then sn1:=ltrim(to_char(n,'9')); sn:=rtrim('swj00'||sn1); else if n<100 then sn1:=ltrim(to_char(n,'99')); sn:=rtrim('swj0'||sn1); else sn1:=ltrim(to_char(n,'999')); sn:=rtrim('swj'||sn1); end if ; end if; DBMS_OUTPUT.PUT_LINE(SN); end loop; End;
4. 5 循环嵌套 上面的任何循环都可以互相嵌套,且可以多重嵌 套,但必须是完全嵌套。如: LOOP … FOR … LOOP … --可有其它循环(多重循环) END LOOP; … WHILE … LOOP … --可有其它循环(多重循环) END LOOP; END LOOP;
例9: 两重循环,计算S=1!+2!+…+10! DECLARE S FLOAT :=0; N INT :=10; K INT ; BEGIN FOR I IN 1..N LOOP -- 计算阶乘和 K:=1; FOR J IN 1..I LOOP --计算K! K:=K*J; END LOOP; S:=S+K; DBMS_OUTPUT.PUT_LINE(TO_CHAR(K)); END LOOP; DBMS_OUTPUT.PUT_LINE('S= '||TO_CHAR(S)); END;
4.6 循环标签 循环标签是以用来标示循环,放在循环前。其格式: <<标签名>> DECLARE S FLOAT :=0; N INT :=10; K INT ; BEGIN <<FOR_LOOP1>> FOR I IN 1..N LOOP -- 计算阶乘和 K:=1; <<FOR_LOOP2>> FOR J IN 1..I LOOP --计算K! K:=K*J; END LOOP FOR_LOOP2; S:=S+K; DBMS_OUTPUT.PUT_LINE(TO_CHAR(K)); END LOOP FOR_LOOP1; DBMS_OUTPUT.PUT_LINE('S= '||TO_CHAR(S)); END;
循环标签一是提高程序的可读性,二是可以从内循环标签一是提高程序的可读性,二是可以从内 循环跳到外层。 <<outer>> LOOP ... LOOP ... EXIT outer WHEN ... – 退出两重循环。 END LOOP; ... END LOOP outer; 注意:只能从内循环跳到外循环,反之不行。
五、%TYPE类型 PL/SQL变量用来存储表中的数据,但二者要有相 同类型。要保证变量与列名类型总是一致,就要 用%TYPE类型。 变量名 表名.列名%TYPE; 例1:DECLARE J_T JOBS.JOB_TITLE%TYPE; BEGIN SELECT JOB_TITLE INTO J_T FROM JOBS WHERE JOB_ID='AD_VP'; DBMS_OUTPUT.PUT_LINE(J_T); END;
六、%ROWTYPE类型 用%ROWTYPE说明变量为一个记录类型。 变量名 表名%ROWTYPE; 引用变量时:变量名.列名 例1:DECLARE EMP EMPLOYEES %ROWTYPE; BEGIN SELECT * INTO EMP FROM EMPLOYEES WHERE EMPLOYEE_ID='201'; DBMS_OUTPUT.PUT_LINE (EMP.LAST_NAME||' '||EMP.FIRST_NAME); END;
七、游标(CURSOR) 游标是将从表中选择的一组记录,放置在内存的 临时表中。游标是数据类型,要先说明后使用。 • 说明游标 CURSOR 游标名 IS SELECT语句; 例1: DECLARE MIN_S JOBS.MIN_SALARY%TYPE; CURSOR MS IS SELECT * FROM JOBS WHERE MIN_SALARY>=MIN_S; BEGIN NULL; --空语句 END;
2. 打开游标 OPEN 游标名; 注意:打开之前,游标中的变量必须有值;打开 游标后,查询记录放在内存,指针指向第一个记录。 例2:DECLARE MIN_S JOBS.MIN_SALARY%TYPE; CURSOR MS IS SELECT * FROM JOBS WHERE MIN_SALARY>=MIN_S; BEGIN MIN_S:=1000; --先给变量赋值 OPEN MS; -- 才能打开游标 END;
3. 提取游标数据 FETCH 游标名 INTO 变量1,变量2,…; 注意: 变量要与说明游标时SELECT后的列名在类 型和个数上完成一样。不能只提取部分。 FETCH MS INTO J1,J2,M1,M2; 4. 关闭游标 CLOSE 游标; CLOSE MS; 关闭后的游标不能再使用。 使用游标的过程:说明游标、变量赋值、打开游 标、处理、关闭游标。
例3:提出数据 DECLARE J1 JOBS.JOB_ID%TYPE; --变量名与列名类型保持一致 J2 JOBS.JOB_TITLE%TYPE; M1 JOBS.MIN_SALARY%TYPE; M2 JOBS.MAX_SALARY%TYPE; MIN_S JOBS.MIN_SALARY%TYPE; CURSOR MS IS SELECT * FROM JOBS --说明游标 WHERE MIN_SALARY>=MIN_S; BEGIN MIN_S:=1000; --先给变量赋值 OPEN MS; -- 才能打开游标 FETCH MS INTO J1,J2,M1,M2; --第一条记录 DBMS_OUTPUT.PUT_LINE(J1||' '||J2||' '||TO_CHAR(m1+m2)); FETCH MS INTO J1,J2,M1,M2; --第二条记录 DBMS_OUTPUT.PUT_LINE(J1||' '||J2||' '||TO_CHAR(m1+m2)); FETCH MS INTO J1,J2,M1,M2; --第三条记录 DBMS_OUTPUT.PUT_LINE(J1||' '||J2||' '||TO_CHAR(m1+m2)); END;
例4: 复杂游标(分组) DECLARE DEP EMPLOYEES.DEPARTMENT_ID%TYPE; COU INT; CURSOR DEPT IS SELECT DEPARTMENT_ID,COUNT(*) FROM EMPLOYEES GROUP BY DEPARTMENT_ID; BEGIN OPEN DEPT; -- 打开游标 FETCH DEPT INTO DEP,COU; --第一条记录 DBMS_OUTPUT.PUT_LINE(DEP||'的人数是: '||TO_CHAR(COU)); FETCH DEPT INTO DEP,COU; --第二条记录 DBMS_OUTPUT.PUT_LINE(DEP||'的人数是: '||TO_CHAR(COU)); CLOSE DEPT; --关闭游标 END;
5. 游标属性 通过游标属性来知道游标的状态。 A: %FOUND 如果前一个FETCH返回一行,%FOUND返回 TRUE;否则返回FALSE。在未打开游标之前设 置%FOUND,返回错误ORA_1001。用法: 游标名%FOUND • %NOTFOUND 与%FOUND相反,常用于退出循环提出。 C. %ISOPEN 判定游标是否打开。打开为TRUE,否则为FALSE.
%ROWCOUNT 返回从游标中已提取的行数。
例5:declare emp employees %rowtype; cursor sal is select * into emp from employees where salary>=10000; --说明游标 begin open sal; -- 打开游标 loop fetch sal into emp; if sal%found then -- 提出成功 dbms_output.put_line (emp.last_name||' 工资:'||to_char(emp.salary)); else -- 不成功 exit; end if; end loop; end;
例6:测试游标属性 declare emp employees %rowtype; cursor sal is select * into emp from employees where department_id=100; begin if not sal%isopen then --游标未打开 open sal; end if; dbms_output.put_line('提取行数:'||to_char(sal%rowcount)); loop fetch sal into emp; dbms_output.put_line(emp.last_name||'行:'||to_char(sal%rowcount)); exit when sal%notfound or sal%rowcount>=5; end loop; end;
例7: 有表DATA_FIEL(n1,n2,n3,exnum)和表TEMP DECLARE num1 data_table.n1%TYPE; num2 data_table.n2%TYPE; num3 data_table.n3%TYPE; result temp.col1%TYPE; CURSOR c1 IS SELECT n1, n2, n3 FROM data_table WHERE exnum = 1; BEGIN OPEN c1; LOOP FETCH c1 INTO num1, num2, num3; EXIT WHEN c1%NOTFOUND; result := num2/(num1 + num3); INSERT INTO temp VALUES (result, NULL, NULL); END LOOP; CLOSE c1; COMMIT; END;
八、参数化游标和隐含游标 • 参数化游标 CRUSOR 游标名(变量1 类型1,变量2 类型2,…) IS SELECT语句 说明:SELECT语句的条件中使用变量,变量类型 可以为%TYPE。 打开参数化游标: OPEN 游标名(实参1,实参2,…); 说明:在打开时所有实参要有具体值,且在数量 和类型上与游标说明时一致。 实参可以是表达式。