1 一、匿名块 2 1、使用returning ... INTO 保存增删改表数据时的一些列的值 3 (01)增加数据时保存数据 4 DECLARE 5 v_ename emp.ename%TYPE; 6 v_sal emp.sal%TYPE; 7 BEGIN 8 INSERT INTO emp(empno,ename,JOB,sal,comm,deptno) VALUES(&no,'zhaoliu','deve',3000,500,10) RETURNING ename,sal INTO v_ename,v_sal; 9 dbms_output.put_line(v_ename || ','||v_sal); 10 end; 11 12 ---打开控制台输出开关 13 SET serveroutput ON; 14 15 (02)修改数据时保存数据 16 DECLARE 17 v_ename emp.ename%TYPE; 18 v_sal emp.sal%type; 19 BEGIN 20 UPDATE emp SET sal=sal+100 WHERE empno=&no RETURNING ename,sal INTO v_ename,v_sal; 21 dbms_output.put_line(v_ename || ','||v_sal); 22 END; 23 24 (03)删除数据时保存数据 25 DECLARE 26 v_ename emp.ename%TYPE; 27 v_sal emp.sal%TYPE; 28 BEGIN 29 DELETE FROM emp WHERE empno=&no RETURNING ename,sal INTO v_ename,v_sal; 30 dbms_output.put_line(v_ename || ','||v_sal); 31 END; 32 33 二、分支结构 34 1、if 35 IF 条件 THEN 结果; 36 END IF; 37 38 IF 条件 THEN 结果; 39 ELSE 结果; 40 END IF; 41 42 IF 条件 THEN 结果; 43 elsif 条件 THEN 结果; 44 .... 45 ELSE 结果; 46 END IF; 47 48 ---查询一个员工薪水,判断员工薪水如果大于3000,输出他是一个白领;如果工资小于等于3000大于1500,输出他是一个蓝领;其它情况输出是一个灰领; 49 DECLARE 50 v_sal emp.sal%TYPE; 51 BEGIN 52 SELECT sal INTO v_sal FROM emp WHERE empno=&no; 53 IF v_sal>3000 THEN 54 dbms_output.put_line('白领'); 55 elsif v_sal>1500 THEN 56 dbms_output.put_line('蓝领'); 57 ELSE dbms_output.put_line('灰领'); 58 END IF; 59 END; 60 61 2、case: 62 格式一: 63 CASE WHEN 条件 THEN 结果; 64 WHEN 条件 THEN 结果; 65 ... 66 ELSE 结果; 67 END CASE; 68 69 格式二: 70 CASE 条件表达式 WHEN 值1 THEN 结果1; 71 WHEN 值2 THEN 结果2; 72 .... 73 ELSE 结果m; 74 END CASE; 75 76 ---输入一个成绩,判断该成绩等级,如果在99~90之间,返回为A,如果在89~80之间,返回为B,如果在79~70之间,返回C,其它返回为D 77 DECLARE 78 v_score NUMBER(3); 79 v_flag VARCHAR2(1); 80 BEGIN 81 v_score:=&score; 82 CASE trunc(v_score/10) WHEN 9 THEN v_flag:='A'; 83 WHEN 8 THEN v_flag:='B'; 84 WHEN 7 THEN v_flag:='C'; 85 ELSE v_flag:='D'; 86 END CASE; 87 dbms_output.put_line(v_score || ',该 成绩的等级为:' || v_flag); 88 END; 89 90 三、循环结构 91 1、直到型循环 92 loop 93 执行循环体; 94 exit WHEN 条件; 95 END loop; 96 97 ---在控制台输出1~5 98 DECLARE 99 v_i NUMBER(1):=1; 100 BEGIN 101 loop 102 dbms_output.put_line(v_i); 103 v_i:=v_i+1; 104 exit WHEN v_i>5; 105 END loop; 106 end; 107 108 2、while: 109 while 条件 110 loop 111 执行循环体; 112 END loop; 113 114 ---在控制台输出1~5 115 DECLARE 116 v_i NUMBER(1):=1; 117 BEGIN 118 while v_i<=5 119 loop 120 dbms_output.put_line(v_i); 121 v_i:=v_i+1; 122 END loop; 123 end; 124 125 3、fOR: 126 FOR 循环变量 IN 最小值..最大值 127 loop 128 执行循环体; 129 END loop; 130 131 ---在控制台输出1~5 132 BEGIN 133 for v_i in 1..5 134 loop 135 dbms_output.put_line(v_i); 136 END loop; 137 end; 138 139 练习: 140 创建一张表,只包含一个字段id 141 先在表中插入一条记录5 142 然后将10—20之间的数插入两遍 143 144 四、存储过程 145 语法: 146 CREATE [OR REPLACE] PROCEDURE 存储过程名(形参1 IN|out 数据类型,形参2 IN|out 数据类型...) 147 IS | AS 148 定义部分; 149 begin 150 执行部分; 151 exception 152 异常部分; 153 END; 154 155 调用过程: 156 exec 存储过程名(实参...); 157 call 存储过程名(实参...); ---如果调用的是无参存储过程,过程名后面必须() 158 159 1、创建一个无参存储过程 160 CREATE PROCEDURE pro_demo1 161 IS 162 BEGIN 163 INSERT INTO dept VALUES(50,'deve','nanjing'); 164 end; 165 166 exec pro_demo1; 167 call pro_demo1(); 168 169 2、创建一个输入参数存储过程 170 ---根据输入的员工工号和新的月薪修改数据库中该 员工的薪水 171 CREATE OR REPLACE PROCEDURE pro_demo1(NO emp.empno%TYPE,newsal emp.sal%TYPE) 172 is 173 BEGIN 174 update emp set sal=newsal where empno=no; 175 end; 176 177 一个为雇员加薪的过程 178 CREATE OR REPLACE PROCEDURE pro_demo1(NO emp.empno%TYPE,addsal emp.sal%TYPE) 179 is 180 BEGIN 181 UPDATE emp SET sal=sal+addsal WHERE empno=NO; 182 end; 183 184 185 CALL pro_demo1(7788,10000); 186 187 SELECT * FROM emp; 188 189 3、创建一个即有输入参数也有输出参数存储过程 190 ---输出指定员工的姓名和工资 191 CREATE OR REPLACE PROCEDURE pro_demo2(NO emp.empno%TYPE,NAME out emp.ename%TYPE,salary out emp.sal%TYPE) 192 IS 193 BEGIN 194 SELECT ename,sal INTO NAME,salary FROM emp WHERE empno=NO; 195 END; 196 197 198 --使用匿名块调用存储过程 199 DECLARE 200 v_name VARCHAR2(30); 201 v_sal NUMBER(7); 202 BEGIN 203 pro_demo2(&no,v_name,v_sal); 204 dbms_output.put_line(v_name || ',' || v_sal); 205 end; 206 207 五、函数 208 CREATE OR REPLACE FUNCTION 函数名(形参 数据类型,...) RETURN 数据类型 209 IS 210 定义部分; 211 BEGIN 212 执行部分; 213 exception 214 异常部分; 215 END; 216 217 ---返回指定员工的年薪 218 CREATE OR REPLACE FUNCTION fun_demo1(NO emp.empno%TYPE) RETURN NUMBER 219 IS 220 v_salary NUMBER(10,2); 221 BEGIN 222 SELECT sal*12 INTO v_salary FROM emp WHERE empno=NO; 223 RETURN v_salary; 224 END; 225 226 SELECT fun_demo1(7788) FROM dual; 227 228 ---写一个函数,返回用户的姓名,入职日期,工资 229 CREATE OR REPLACE FUNCTION fun_demo2(NO emp.empno%TYPE) RETURN VARCHAR2 230 IS 231 v_ename emp.ename%TYPE; 232 v_hiredate varchar2(50); 233 v_sal emp.sal%TYPE; 234 v_info varchar2(200); 235 BEGIN 236 SELECT ename,to_char(hiredate,'yyyy-mm-dd'),sal INTO v_ename,v_hiredate,v_sal FROM emp WHERE empno=NO; 237 v_info:=v_ename ||','|| v_hiredate ||','|| v_sal; 238 RETURN v_info; 239 end; 240 241 SELECT fun_demo2(7788) FROM dual; 242 243 存储过程和函数的区别: 244 1.返回值的区别,函数返回值只有一个,而存储过程是通过输出参数来返回,可以有多个 245 2.调用的区别,函数可以在查询语句中直接调用,而存储过程必须单独调用 246 3.应用场合的区别,函数一般用来计算并返回计算的结果,而存储过程一般用来执行特定的操作,如创建表、创建用户等DDL语句 247 248 五、包,里面包含的内容:存储过程,函数 249 语法:包括两个部分 250 声明部分: 251 CREATE OR REPLACE PACKAGE 包名 252 IS 253 声明内容; 254 END; 255 256 包体的实体: 257 CREATE OR REPLACE PACKAGE BODY 包名 258 is 259 实现部分; 260 end; 261 262 CREATE OR REPLACE PACKAGE pac_demo1 263 IS 264 PROCEDURE pro_demo1(NO emp.empno%TYPE,salary emp.sal%TYPE); 265 PROCEDURE pro_demo2(NO emp.empno%TYPE); 266 FUNCTION fun_demo1(NO emp.empno%TYPE) RETURN NUMBER; 267 end; 268 269 create or replace package body pac_demo1 270 IS 271 PROCEDURE pro_demo1(NO emp.empno%TYPE,salary emp.sal%TYPE) 272 is 273 BEGIN 274 UPDATE emp SET sal=salary WHERE empno=NO; 275 END; 276 PROCEDURE pro_demo2(NO emp.empno%TYPE) 277 IS 278 BEGIN 279 DELETE FROM emp WHERE empno=NO; 280 END; 281 FUNCTION fun_demo1(NO emp.empno%TYPE) RETURN NUMBER 282 IS 283 v_salary number(10,2); 284 BEGIN 285 SELECT sal*12 INTO v_salary FROM emp WHERE empno=NO; 286 RETURN v_salary; 287 END; 288 end; 289 290 CALL pac_demo1.pro_demo1(7788,20000); 291 SELECT * FROM emp; 292 CALL pac_demo1.pro_demo2(7788); 293 select pac_demo1.fun_demo1(7369) from dual; 294 295 296 297 --1.创建一个包,包含一个为雇员加薪的过程,一个为雇员减薪的过程 298 CREATE OR REPLACE PACKAGE pac_test1 299 IS 300 PROCEDURE addsal(NO emp.empno%TYPE,addsal emp.sal%TYPE); 301 procedure minussal(no emp.empno%type,minussal emp.sal%type); 302 END; 303 304 CREATE OR REPLACE PACKAGE body pac_test1 305 IS 306 PROCEDURE addsal(NO emp.empno%TYPE,addsal emp.sal%TYPE) 307 is 308 BEGIN 309 UPDATE emp SET sal=sal+addsal WHERE empno=NO; 310 end; 311 PROCEDURE minussal(NO emp.empno%TYPE,minussal emp.sal%TYPE) 312 IS 313 BEGIN 314 UPDATE emp SET sal=sal-minussal WHERE empno=NO; 315 end; 316 END; 317 318 --2.编写一个过程,验证用户登陆。如果用户名、密码匹配,输出Y,否则输出N emp表中的ename为用户名 empno为密码 319 CREATE OR REPLACE PROCEDURE pro_test2(username VARCHAR2,PASSWORD NUMBER,flag out VARCHAR2) 320 IS 321 v_count number(2); 322 BEGIN 323 SELECT count(empno) INTO v_count FROM emp WHERE ename=username AND empno=PASSWORD; 324 IF v_count>0 THEN flag:='Y'; 325 ELSE flag:='N'; 326 END IF; 327 END; 328 329 --3.编写一个函数,根据雇员编号,计算调整后的工资 330 如果该雇员从事经理工作,且工资大于3000,则工资上涨20%,否则上涨35% 331 其他情况均上涨10%,输出雇员编号、姓名、上涨后的工资 332 333 CREATE OR REPLACE FUNCTION fun_test3(NO emp.empno%TYPE) RETURN VARCHAR2 334 IS 335 v_ename emp.ename%TYPE; 336 v_sal emp.sal%TYPE; 337 v_newsal emp.sal%TYPE; 338 v_job emp.job%type; 339 v_info varchar2(100); 340 BEGIN 341 SELECT ename,sal,JOB INTO v_ename,v_sal,v_job FROM emp WHERE empno=NO; 342 IF upper(v_job)='MANAGER' THEN 343 IF v_sal>3000 THEN 344 v_newsal:=v_sal*1.2; 345 ELSE v_newsal:=v_sal*1.35; 346 END IF; 347 ELSE v_newsal:=v_sal*1.1; 348 END IF; 349 v_info:= NO || ',' || v_ename || ',' || v_newsal; 350 RETURN v_info; 351 end; 352 353 --4.创建一个包 354 包中含有一个存储过程,实现涨工资的功能,输入雇员姓名,如果其工作年限超过20年,工资增加2000 355 工作年限在10-20年,工资增加1000,工作年限在5-10年,工资增加500,输出雇员姓名和增涨后的工资 356 包中还含有一个函数,实现输入工资,判断工资如果大于4500,返回1,否则返回0 357 358 CREATE OR REPLACE PACKAGE pac_test4 359 IS 360 PROCEDURE pro_addsal(NAME emp.ename%TYPE,info out VARCHAR2); 361 FUNCTION fun_sal(salary emp.sal%TYPE) RETURN NUMBER; 362 end; 363 364 CREATE OR REPLACE PACKAGE body pac_test4 365 IS 366 PROCEDURE pro_addsal(NAME emp.ename%TYPE,info out VARCHAR2) 367 IS 368 v_year NUMBER(5); 369 v_sal emp.sal%TYPE; 370 BEGIN 371 SELECT trunc(months_between(SYSDATE,hiredate)/12),sal INTO v_year,v_sal FROM emp WHERE upper(ename)=upper(NAME); 372 IF v_year>20 THEN v_sal:=v_sal+2000; 373 elsif v_year>=10 THEN v_sal:=v_sal+1000; 374 elsif v_year>=5 THEN v_sal:=v_sal+500; 375 END IF; 376 info:=NAME || ','||v_sal; 377 end; 378 FUNCTION fun_sal(salary emp.sal%TYPE) RETURN NUMBER 379 IS 380 v_flag NUMBER(1); 381 BEGIN 382 IF salary>4500 THEN 383 v_flag:=1; 384 ELSE v_flag:=0; 385 END IF; 386 RETURN v_flag; 387 end; 388 END; 389 390 六、游标: 391 1、显示游标(查)、隐式游标(增删改) 392 使用显示游标有四个步骤: 393 (01):声明游标:cursor 游标名 IS 查询语句; 394 (02):打开游标:open 游标名; 395 (03):获取数据:fetch 游标名 INTO 变量名; 396 (04):关闭游标:close 游标名; 397 398 游标属性: 399 %found:是否发现数据,如果发现,返回true,否则返回false 400 %notfound:是否没有数据,如果没有,返回true,否则返回false 401 %isopen:是否打开,如果打开,,返回true,否则返回false 402 %rowcount:返回涉及的行数 403 404 405 DECLARE 406 CURSOR cur IS SELECT * FROM dept; 407 v_dept dept%rowtype; 408 BEGIN 409 OPEN cur; 410 fetch cur INTO v_dept; 411 loop 412 dbms_output.put_line(v_dept.deptno || ','|| v_dept.dname || ','||v_dept.loc); 413 fetch cur INTO v_dept; 414 exit WHEN cur%notfound; 415 END loop; 416 CLOSE cur; 417 end; 418 419 ---使用for循环读取游标指向的数据,打开游标、获取游标数据、关闭游标都自动 420 DECLARE 421 CURSOR cur IS SELECT * FROM dept; 422 BEGIN 423 FOR v_dept IN cur 424 loop 425 dbms_output.put_line(v_dept.deptno || ','|| v_dept.dname || ','||v_dept.loc); 426 END loop; 427 end; 428 429 2、隐式游标:游标名为SQL,增删改 430 431 ---修改指定员工的工资,如果员工不存在,提示员工不存在,如果存在,输出更新数据的行数 432 BEGIN 433 UPDATE emp SET sal=sal*1.2 WHERE empno=&no; 434 IF SQL%found THEN 435 dbms_output.put_line('更新数据的行数为:' || SQL%rowcount); 436 ELSE 437 dbms_output.put_line('对不起,该用户不存在'); 438 END IF; 439 end; 440 441 六、异常: 442 1、预定义异常:有异常号也有异常名 443 DECLARE 444 v_emp emp%rowtype; 445 BEGIN 446 SELECT * INTO v_emp FROM emp WHERE empno=&no; 447 exception 448 WHEN no_data_found THEN 449 dbms_output.put_line('用户不存在'); 450 WHEN too_many_rows THEN 451 dbms_output.put_line('返回了多行数据'); 452 WHEN others THEN 453 dbms_output.put_line('程序出现了错误'); 454 END; 455 456 2、非预定义异常:有异常号没有异常名 457 3、自定义异常:没有异常号也没有异常名 458 459 七、触发器 460 1、DML触发器: 461 462 CREATE OR REPLACE TRIGGER 触发器名 463 AFTER|BEFORE INSERT|UPDATE|DELETE ON 表名 464 [FOR EACH ROW] ---行触 发器 465 BEGIN 466 执行体; 467 end; 468 469 ---如果有人删除emp表中的数据,给出警告信息 470 CREATE OR REPLACE trigger tri_demo1 471 AFTER DELETE ON emp 472 BEGIN 473 dbms_output.put_line('有人删除了emp表中的数据,请注意'); 474 end; 475 476 DELETE FROM emp WHERE empno=7566; 477 478 CREATE OR REPLACE TRIGGER tri_demo1 479 AFTER DELETE or update or insert ON emp 480 BEGIN 481 IF deleting THEN 482 dbms_output.put_line('有人在删除emp表的数据'); 483 elsif inserting THEN 484 dbms_output.put_line('有人新增了数据'); 485 elsif updating THEN 486 dbms_output.put_line('有人修改了数据'); 487 end if; 488 END; 489 490 DELETE INSERT UPDATE 491 :NEW 无 有效 有效 492 :old 有效 无 有效 493 494 CREATE OR REPLACE TRIGGER tri_demo3 495 before DELETE OR UPDATE OR INSERT ON emp 496 for each row 497 BEGIN 498 IF deleting THEN 499 dbms_output.put_line('有人在删除emp表的数据'); 500 dbms_output.put_line(:old.ename ||','||:old.sal); 501 elsif inserting THEN 502 dbms_output.put_line('有人新增了数据'); 503 dbms_output.put_line(:new.ename ||','||:new.sal); 504 elsif updating THEN 505 dbms_output.put_line('有人修改了数据'); 506 dbms_output.put_line('原数据为:'||:OLD.ename ||','||:OLD.sal); 507 dbms_output.put_line('修改后的数据为:'||:new.ename ||','||:new.sal); 508 END IF; 509 END; 510 511 512 语句触发器:无论增加、删除、修改多少行数据,触发器只触发一次 513 行触发器:增加、删除、修改对于每一行涉及的数据,触发器都会执行 514 515 INSERT INTO emp(empno,ename,sal) VALUES(1000,'bbb',6000); 516 commit; 517 518 DELETE FROM emp WHERE deptno=10; 519 commit;