1. Trang chủ
  2. » Công Nghệ Thông Tin

Oracle PL/SQL for dummies phần 10 potx

39 356 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 39
Dung lượng 1,48 MB

Nội dung

Unless you’re in the debugging or development mode, never use an exception handler like this, especially in production instances of a system. All exception handlers that have WHEN OTHERS without additional activity (you might need to have that exception) should look like this: function f_assignManager_tx (i_empNo NUMBER, i_mgr NUMBER) return VARCHAR2 is v_job_tx VARCHAR2(10); begin Update employee update emp set mgr=i_mgr where empNo=i_empNo returning job into v_job_tx; If person is managing analysis - there will be no commissions. Give 5% raise per person to the manager if v_job_tx = ‘ANALYST’ then update emp set sal=sal*1.05 where empNo=i_mgr; end if; return ‘OK’; exception when others then p_log(‘f_assignManager_tx(‘||i_empNo||’,’||i_mgr|| ‘) ERROR:’||sqlerrm); return ‘ERROR’; end; Here, you aren’t raising an exception if something goes wrong, but instead, returning ERROR rather than OK and logging a real error (see the P_LOG procedure in Chapter 15). You can use this logic if, because of front-end restrictions, you can’t throw Oracle exceptions (for example, in a Web-based environment). This technique is a cleaner way of notifying the front end that something has gone wrong without destroying performance, and it also pro- vides useful debugging information. This exception handler includes a call to the logging routine, to which you are passing the current function name, its parameters, and the SQL error message. This is the minimum information that should be logged, but you could add the current user, the client’s IP address, global parameter settings, and other data. Don’t hesitate to add a lot of information to debugging messages. When you’re trying to identify and solve a problem, you never know what data you might need. These debugging statements shouldn’t be executed at all, but even if they are executed, the performance impact is negligible. 378 Part VI: The Part of Tens 25_599577 ch16.qxp 5/1/06 12:18 PM Page 378 Forgetting to Handle NULL Values Operating on variables or columns that might contain NULL values without explicitly handling these NULL values can cause problems and produce strange results. That’s because NULL is handled differently from other values. As mentioned in Chapter 3, you should keep the following rules in mind: 1. All logical operations (including NOT) that involve NULL values always return FALSE. 2. All operations (built-in functions, arithmetic) with NULL return NULL, with the following exceptions: • Concatenations of strings ignore NULL. • DECODE can compare values with NULL. • REPLACE can take NULL as a third parameter. As an example, if you need to create a trigger to enforce a number of rules related to the salaries and commissions of employees, you might write: create or replace trigger emp_biu before insert or update on emp referencing new as new old as old for each row begin if :new.sal+:new.comm >= 10000 then raise_application_error (-20999,’Salary with commissions should be less than 10000’); end if; end; Now when you try to run a basic update, you get the following result: SQL> update emp 2 set sal=15000 3 where eName=’KING’; 1 row updated. SQL> The trigger didn’t work, and it might take you hours to debug. The real prob- lem is that this trigger is correct only when neither SAL nor COMM have NULL values. Because the commission value is NULL for KING and SAL+COMM is NULL (Rule #2 from earlier), you’re trying to compare NULL with 10000. But any comparison of NULL always returns NULL (Rule #1). Therefore, the IF statements don’t catch the problem. 379 Chapter 16: Ten Common Mistakes to Avoid in PL/SQL 25_599577 ch16.qxp 5/1/06 12:18 PM Page 379 The trigger should look like this: create or replace trigger emp_biu before insert or update on emp referencing new as new old as old for each row begin if nvl(:new.sal,0)+nvl(:new.comm,0) >= 10000 then raise_application_error (-20999,’Salary with commissions should be less than 10000’); end if; end; Using this code, all cases are covered. By applying NVL to the columns, you can be certain that an operation won’t result in a NULL value. In grouping functions (SUM, AVG, COUNT), you also need to watch out for NULL values. The rule is that these functions process only not-NULL values; but if all values are NULL, the result is also NULL, as shown here: SQL> select deptNo, sum(comm), sum(sal), sum(comm)+sum(sal) 2 from emp 3 group by deptNo; DEPTNO SUM(COMM) SUM(SAL) SUM(COMM)+SUM(SAL) 10 12750 20 10875 30 2200 9400 11600 SQL> Even employees from department 30 have some NULL values in the COMM column, SUM(COMM), because department 30 is not NULL (Oracle adds up all not-NULL values). But in departments 10 and 20, there are no employees with not-NULL commissions. That’s why SUM(COMM) is NULL for these depart- ments, and consequently, SUM(COMM)+SUM(SAL) is also NULL. Creating Unintended Boolean Expressions Be careful when building complex logical conditions. You need to group logi- cal conditions appropriately so that others can maintain your code in the future. Using the trigger from the previous example, add more complex rules: Salary + commissions may not be greater than $10,000 if you work in department 20, or if you are a clerk. 380 Part VI: The Part of Tens 25_599577 ch16.qxp 5/1/06 12:18 PM Page 380 With complex conditions like this, you need to define each element: 1. Is the total of salary + commissions > $10,000? 2. Does the employee work in department 20? 3. Is the employee’s job title CLERK? Now you need to group the rules. In this case, you have two groups for the error condition: check salary (Rule #1 should be true) and check extra condi- tions (either Rule #2 or Rule #3 should be true). The last step is to convert a group into logical operations. Inside the second group, you have an OR condition. Between groups, you have AND conditions, as shown in Listing 16-1. Listing 16-1: Grouping Conditions create or replace trigger emp_biu before insert or update on emp referencing old as old new as new for each row begin if nvl(:new.sal,0)+nvl(:new.comm,0) >= 10000 and (:new.deptNo=20 ➞ 7 or :new.job=’CLERK’) ➞ 8 then raise_application_error (-20999,’Salary with commissions should be less than 10000’); end if; end; ➞ 7–8 Note the parentheses around the two conditions connected with OR. Because the first group contains only one condition, no extra parentheses are necessary. This is the only correct way of coding. Each group of conditions should be enclosed in parentheses. But if you forgot the parentheses and wrote the code like this: if nvl(:new.sal,0)+nvl(:new.comm,0) >= 10000 and :new.deptNo=20 or :new.job=’CLERK’ you will have an error each time you try to update the salary or commissions of any employee with the job CLERK because the logical operator AND has a higher precedence than OR (like multiplying rather than adding). As a result, the last condition can be translated as: “The update will fail if salary + commissions for a person working in department 20 are more than $10,000. The update will also fail if the job title is ‘CLERK’.” This is definitely not what you wanted. 381 Chapter 16: Ten Common Mistakes to Avoid in PL/SQL 25_599577 ch16.qxp 5/1/06 12:18 PM Page 381 You should use the same syntax rule of enclosing condition groups in paren- theses, not only in PL/SQL but in SQL, too. Remembering this could save you hours of debugging afterward. The following is an example of good syntax for a situation when you need to retrieve all records from the EMP table with a number of different rules: select * from emp where ( (deptNo=30 and sal>1500) or (deptNo=20 and sal>1000) ) and job!=’ANALYST’ Note how we applied parentheses to group each condition so that Oracle knows exactly how those conditions should be interpreted. Forgetting to Close an Explicit Cursor Each time you use an explicit cursor, don’t forget to close it. Using explicit cursors (which we introduce in Chapter 6) is good coding practice. Remember that the database parameter OPEN_CURSORS defines the maximum number of cursors that can be open at the same time. The value of the variable might change from one environment to another, but the point is that the number of cursors is limited. Forgotten cursors that are left open can bring a system to a halt. Listing 16-2 shows a correctly written routine. Listing 16-2: Correctly Written Explicit Cursors create or replace function f_getList_tx (i_source_tx VARCHAR2, i_column_tx VARCHAR2, i_filter_tx VARCHAR2, i_separator_tx VARCHAR2) return VARCHAR2 is v_string_tx VARCHAR2(4000); v_temp_tx VARCHAR2(4000); v_out_tx VARCHAR2(4000); v_weak_ref sys_refcursor; begin v_string_tx:=’select ‘||i_column_tx|| ‘ from ‘||i_source_tx|| ‘ where ‘||i_filter_tx; 382 Part VI: The Part of Tens 25_599577 ch16.qxp 5/1/06 12:18 PM Page 382 open v_weak_ref for v_string_tx; loop fetch v_weak_ref into v_temp_tx; exit when v_weak_ref%NOTFOUND; if v_out_tx is null then v_out_tx:=v_temp_tx; else v_out_tx:=v_out_tx||i_separator_tx||v_temp_tx; end if; end loop; close v_weak_ref; return v_out_tx; exception when others then if v_weak_ref%isOpen then close v_weak_ref; raise; end if; end; The problem was to generate a list of any columns from any table with a specified condition and separator. As we discuss in Chapter 13, if you have an undefined data source, you can always use dynamic SQL. But with dynamic SQL, you have to use explicit cursors. If you stick to the following rules, you should be able to use explicit cursors successfully: ߜ When you start typing a routine, immediately include both the OPEN and CLOSE cursor statements. ߜ Never add a RETURN clause before closing the cursor. ߜ In the exception-handling block, always check to see whether explicit cursors are open, and if so, close them. If you’re using recursive calls to the same routine, be very careful about using explicit cursors. In a structure with 20 levels of hierarchy, at some point, you’re likely to have 20 cursors open simultaneously. If you have a large number of users, this could cause your system to reach or exceed the maximum number of cursors. Oracle is fairly smart about closing cursors if you forget to do so. When a pro- gram unit terminates, all cursors that it opened are automatically closed. But relying on this capability is dangerous and can ultimately result in having too many cursors open at once, so remember to close your cursors explicitly. 383 Chapter 16: Ten Common Mistakes to Avoid in PL/SQL 25_599577 ch16.qxp 5/1/06 12:18 PM Page 383 Starting Endless Loops Endless loops can cause endless problems. Common among those problems is freezing your system. So each time you create a loop, you must think about how the code will exit from the loop. Listing 16-3 illustrates how easy it is to create loop-related problems, if you’re not careful. This code creates a function that checks whether, in a given department, the number of employees with an income less than the defined amount is in fact limited to the number specified. Listing 16-3: Endless Loop Example function f_limit_yn(i_deptNo NUMBER, i_limit_nr NUMBER, i_income_nr NUMBER) return VARCHAR2 is cursor c_emp is select nvl(sal,0)+nvl(comm,0) income_nr from emp where deptNo=i_deptNo; v_income_nr NUMBER; v_counter_nr NUMBER:=0; v_error_yn VARCHAR2(1):=’N’; begin open c_emp; loop fetch c_emp into v_income_nr; if v_income_nr < i_income_nr then v_counter_nr:=v_counter_nr+1; end if; if v_counter_nr=i_limit_nr then v_error_yn:=’Y’; exit; end if; end loop; close c_emp; return v_error_yn; end; You could write this function, test it a few times, and deploy it to production. But if you select department 40 in the user interface, you’ll be stuck in a dead loop. This is because you can exit from the loop only if the major condition is satisfied. But what about the case when it isn’t satisfied, as is the case with department 40, which has no employees? Listing 16-4 shows the correct way. 384 Part VI: The Part of Tens 25_599577 ch16.qxp 5/1/06 12:18 PM Page 384 Listing 16-4: Correct Code to Exit a Loop function f_limit_yn(i_deptNo NUMBER, i_limit_nr NUMBER, i_income_nr NUMBER) return VARCHAR2 is cursor c_emp is select nvl(sal,0)+nvl(comm,0) income_nr from emp where deptNo=i_deptNo; v_income_nr NUMBER; v_counter_nr NUMBER:=0; v_error_yn VARCHAR2(1):=’N’; begin open c_emp; loop fetch c_emp into v_income_nr; exit when c_emp%NOTFOUND; ➞ 17 if v_income_nr < i_income_nr then v_counter_nr:=v_counter_nr+1; end if; if v_counter_nr=i_limit_nr then v_error_yn:=’Y’; exit; ➞ 23 end if; end loop; close c_emp; return v_error_yn; end; ➞ 17 Provides the exit from the loop if the department has no employees. Usually, developers focus on the major condition and forget that other scenarios could cause problems. The best way to avoid endless loops is to use CURSOR FOR loops or FOR loops whenever possible. If you don’t need to interrupt processing, always use a FOR loop. It’s much safer. In some cases, you can replace regular loops with SQL. Listing 16-4 could be rewritten, as shown in Listing 16-5. Listing 16-5: A SQL Replacement for Regular Loops function f_checkDeptLimit_yn (i_deptNo NUMBER, i_limit_nr NUMBER, i_income_nr NUMBER) return VARCHAR2 (continued) 385 Chapter 16: Ten Common Mistakes to Avoid in PL/SQL 25_599577 ch16.qxp 5/1/06 12:18 PM Page 385 Listing 16-5 (continued) is v_counter_nr NUMBER:=0; ➞5 v_error_yn VARCHAR2(1):=’N’; begin select count(*) into v_counter_nr from emp where deptNo = i_deptNo and nvl(sal,0)+nvl(comm,0)<i_income_nr and rownum < i_limit_nr+1 ; if v_counter_nr=i_limit_nr then ➞ 15 v_error_yn:=’Y’; end if; return v_error_yn; end; ➞ 5, 15 Limits number of counted rows with the passed limit. As a result, the value of v_counter_nr could be less than or equal to the limit. This solution, although elegant, is significantly less clear. Even though you’re getting rid of loops, you’re increasing the complexity of the code. You need to use your judgment about whether the added complexity is warranted. Reinventing the Wheel Don’t try to create code structures that have already been developed for you by Oracle. Before you start coding, it is a good idea to review an Oracle manual with the list of built-in functions. This tip is especially true when working with strings. For example, if you need to create a routine to check Social Security num- bers, the specifications would be: ߜ A correct string is 11-characters long. ߜ A string should contain 9 numbers and 2 dashes. Your first reaction might be to just start coding. You could write something like Listing 16-6 in 20 minutes. 386 Part VI: The Part of Tens 25_599577 ch16.qxp 5/1/06 12:18 PM Page 386 Listing 16-6: A Routine to Check Social Security Numbers function f_validSSN_yn (i_ssn_tx VARCHAR2) return VARCHAR2 is v_ctr_nr NUMBER := 0; v_ssnNr_tx VARCHAR2(256); v_out_yn VARCHAR2(1); v_error_yn VARCHAR2(1); begin if i_ssn_tx is null then v_out_yn:=’Y’; else v_ssnNr_tx:=replace(i_ssn_tx,’-’,’’); if length(v_ssnNr_tx)!=9 then v_error_yn:=’Y’; else v_ctr_nr:=1; loop if instr (‘0123456789’, substr (v_ssnNr_tx, v_ctr_nr, 1))= 0 then v_error_yn:=’Y’; end if; exit when v_ctr_nr=9 or v_error_yn=’Y’; v_ctr_nr:=v_ctr_nr+1; end loop; end if; end if; if v_error_yn=’Y’ then v_out_yn:=’N’; else v_out_yn:=’Y’; end if; return v_out_yn; end; Listing 16-6 works exactly as you specified. But is it the best way to code? Definitely not. You could code exactly the same functionality in a different way, as shown in Listing 16-7. Listing 16-7: A Better Routine to Check Social Security Numbers function f_validSSN_yn (i_ssn_tx VARCHAR2) return VARCHAR2 is v_out_tx VARCHAR2(1); (continued) 387 Chapter 16: Ten Common Mistakes to Avoid in PL/SQL 25_599577 ch16.qxp 5/1/06 12:18 PM Page 387 [...]... understanding big picture, 336–337 coding standards for capitalization, 211 for comments, 207– 210 for constant value, 202–205 for data conversion for dates, 213 for data element, 206 for datatype declarations, 211–213 for global variables, 210 for indentation, 210 211 for line length, 213 overview of, 201–202 for program units, 205–206 for SQL, 214–217 for synonyms, 213–214 collection associative arrays,... 307–308 commands to support, 106 description of, 105 handling, 122–126 naming standards for, 196 predefined, 111–112 propagation of, 118–126 types of, 110 111 unhandled, 108 user-defined, 105 , 113–118, 196 WHEN OTHERS exception, 370–372 exception code, 110 exception handler adding, 107 – 110 avoiding exception raised in, 124–126 CURSOR FOR loop and, 152–153, 154–155 description of, 106 implicit cursor and,... updating record using, 148–150 Loney, Kevin, Oracle Database 10g: The Complete Reference, 19, 140 loop CURSOR FOR loop, 150–155, 385 description of, 85 endless, 81, 384–386 FOR loop, 102 104 , 385 nested, 99 100 , 133–134 simple, 97–99 types of, 97 WHILE loop, 100 102 LOOP command, 150–151 looping through multiple records, 132–133 Lowe, Doug, Networking For Dummies, 7th Edition, 25 LPAD function, 249–250... application development tools influence on, 189 for collections, 197–198 enforcing, 199–200 for exceptions, 196 for files, 198–199 Java influences on, 188 for objects, 190–191 Oracle database influences on, 188 overview of, 187–188 for parameters, 194–196 for program units, 193–194 setting for common code elements, 189 for user-defined datatypes, 196–197 for variables, 191–193 Native Dynamic SQL bind... 110 Oracle See also Oracle environment Application Development Framework Business Components, 169, 182 Application Server, 17 Applications, 17 Database 10g, 27 DBMS, 17 Developer, programming for, 21–22 OpenWorld conference, 353 SQL Developer, 30, 166, 189 staying up-to-date on, 352–354 Oracle Database 10g PL/SQL Programming (Urman), 353 Oracle Database 10g: The Complete Reference (Loney), 19, 140 Oracle. .. external large object, 254–255 EXTRACT function, 237–238 •F• Feuerstein, Steven (PL/SQL expert), 5, 211, 353 file, naming standards for, 198–199 A First Course in Database Systems (Ullman and Widom), 16 First Normal Form (1NF), 14 FOR loop, 102 104 , 385 foreign key, 11, 12 formal parameter, 63–67 format mask, 231–234 forward slash (/), 35 %FOUND variable, 144, 145, 146, 148, 156 full rollback, 294–295... Database 10g: The Complete Reference (Loney), 19, 140 Oracle Developer: Advanced Forms & Reports (Koletzke and Dorsey), 22 Oracle Development Tools User Group, 5, 353 Oracle environment hardware and software requirements for, 25–26 installing, 23 overview of, 17 setting up, 23–25 SQL in, 18 Oracle 9i For Dummies (McCulloughDieter), 2 Oracle Technology Network (OTN), 5, 26, 27 ORDER BY statement, and parameters,... 168–169 table alias, guidelines for, 215 table descriptions for Scott/Tiger schema, 37–38 table-level trigger, 19 Taylor, Allan G Database Development For Dummies, 337 SQL For Dummies, 5th Edition, 3, 18 team approach, 349–350 termination point of recursion, 81 Index terminology for database design, 12–13 test case, 340 test-first approach, 351 testing code architecture for, 345 “good enough is best”... Listing 16 -10 formats an address 389 390 Part VI: The Part of Tens Listing 16 -10: Incorrect Code to Format an Address function f_formatAddress_tx (i_street_tx VARCHAR2, i_city_tx VARCHAR2, i_state_tx VARCHAR2, i_zip_nr NUMBER) return VARCHAR2 is v_out_tx VARCHAR2(2000); begin v_out_tx:=i_street_tx||chr (10) ||i_city_tx|| ‘, ‘||i_state_tx||’ ‘||i_zip_nr; return v_out_tx; end; ➞3 SQL> select f_formatAddress_tx(‘701... set of elements ELSIF statement, 88–89 Embarcadero, RapidSQL, 31 embedding code in database view, 21 END statement, 58, 208, 210 endless loop, 81, 384–386 enforcing naming standards, 199–200 Enterprise Edition, 27 entity, 13 401 402 Oracle PL/SQL For Dummies error code prefix, 110 error message compilation and, 342–343 including in user-defined exception, 116–117 listing example, 58 parsing and, 56–57 . 16 -10 formats an address. 389 Chapter 16: Ten Common Mistakes to Avoid in PL/SQL 25_599577 ch16.qxp 5/1/06 12:18 PM Page 389 Listing 16 -10: Incorrect Code to Format an Address function f_formatAddress_tx. developed for you by Oracle. Before you start coding, it is a good idea to review an Oracle manual with the list of built-in functions. This tip is especially true when working with strings. For example,. commission value is NULL for KING and SAL+COMM is NULL (Rule #2 from earlier), you’re trying to compare NULL with 100 00. But any comparison of NULL always returns NULL (Rule #1). Therefore, the IF statements

Ngày đăng: 08/08/2014, 20:21

TỪ KHÓA LIÊN QUAN