PL/SQL Chapter 8
Outline Procedures Cursors Triggers
Procedural SQL SQL does not support the conditional execution of procedures as well as looping operations. To overcome the lack of procedural functionality in SQL and to provide standardization, SQL-99 standard defines the use of persistent stored modules (PSM).
What is PSM? A PSM is a block of code containing standard SQL statements and procedural extensions that is stored and executed at the DBMS server. The PSM represents business logic that can be encapsulated, stored and shared among multiple database users. Oracle implements PSMs through its procedural SQL language.
What is PL/SQL and where can PL/SQL be used? Procedural SQL (PL/SQL) is a language that makes it possible to use and store procedural code and SQL statements within the database and merge SQL and traditional programming constructs. The procedural code is executed as a unit by the DBMS when it is invoked b the end user. PL/SQL is used to create Anonymous PL/SQL blocks (without name) Stored Procedures PL/SQL functions Triggers
PL/SQL block The PL/SQL block starts with the DECLARE section in which the variables, the data types or the initial values are declared. The PL/SQL block ends with the word END The PL/SQL block can be executed by typing / and press enter key. Each statement inside the PL/SQL code must end with a semicolon “;”. To display the output use the command dbms_output.put_line(‘any string’);. Conditional statement or Looping statements can be used after declare statements; The SELECT statement uses the INTO keyword inside the PL/SQL block of code to assign the output of the query to a PL/SQL variable. If the SELECT statement returns more than one value, you will get an error.
PL/SQL Program Blocks
Commands included in PL/SQL Comments: PL/SQL program includes commands such as Comments statements that are lines of text that explain or document a program step or series of steps. Not executed by interpreter To create a block of comments enclosed between /* and */ To create one line comment type two hyphens -- at the beginning of the line. PL/SQL programs have Commands to perform arithmetic operations Assignment statements that assign values to variables Conditional structures to perform decisions Looping structures to repeat instructions multiple times SQL commands that retrieve data.
Procedural Statements Sequences Statement 1; Statement 2;… Control Statements IF <condition> THEN statement1; ELSE statement2; ENDID; Looping Structures DO WHILE LOOP…. ENDLOOP
Assignment Statements Assigns a value to a variable An assignment statement has the following syntax variable_name := value; Value can be a literal: current_s_first_name := 'John'; Value can be another variable: current_s_first_name := s_first_name;
Displaying PL/SQL Program Output in SQL*Plus PL/SQL output buffer Memory area on database server Stores program’s output values before they are displayed to user Should increase the size of the buffer as SET SERVEROUTPUT ON SIZE buffer_size Default buffer size 2000 bytes To display more than just a few lines of output, it is better to make the output buffer larger than the default size. Maximum is 1000000 bytes
Displaying PL/SQL Program Output in SQL*Plus (continued) Display program output DBMS_OUTPUT.PUT_LINE('display_text'); The display_text value can contain literal character strings such as ‘ My name is Roohi’ or variable names such as current_s_first. Display maximum of 255 characters of text data
Executing a PL/SQL Program in SQL*Plus Create program in text editor Copy and paste program commands into SQL*Plus window Press Enter after last program command Type front slash ( / ) Then press Enter again
Executing a PL/SQL Program in SQL*Plus Write a PL/SQL program to display the current date as shown below Today’s date is 27-Feb-2012 DECLARE todays_date DATE; X number; BEGIN todays_date := SYSDATE; X:= 1; DBMS_OUTPUT.PUT_LINE('Today''s date is '); DBMS_OUTPUT.PUT_LINE(todays_date); DBMS_OUTPUT.PUT_LINE(x); END;
PL/SQL Data Conversion Functions Sometimes the PL/SQL interpreter is unable to implicitly convert value and an error occurs. Other times, PL/SQL interpreter performs an implicit conversion and produce unexpected/invalid data Explicit data conversions Convert variables to different data types using data conversion functions that are built into PL/SQL. A Guide to Oracle10g
Display today’s date using concatenated character string To concatenate two strings in PL/SQL, you use the double bar (||) operator: new_string := string1 || string2; SQL> DECLARE 2 todays_date DATE; 3 BEGIN 4 todays_date :=SYSDATE; 5 DBMS_OUTPUT.PUT_LINE('Today''s date is ' || TO_CHAR(todays_date)); 6 END; 7 / PL/SQL procedure successfully completed.
PL/SQL Decision Control Structures Use IF/THEN/ELSE to execute code if condition is true or FALSE IF condition THEN commands that execute if condition is TRUE; ELSE commands that execute if condition is FALSE; END IF; Evaluates ELSE commands if condition is FALSE
PL/SQL Decision Control Structures Use IF/ELSIF decision control structure to test for many different conditions.: IF condition1 THEN commands that execute if condition1 is TRUE; ELSIF condition2 THEN commands that execute if condition2 is TRUE; ELSIF condition3 THEN commands that execute if condition3 is TRUE; ... ELSE commands that execute if none of the conditions are TRUE; END IF;
Loops PL/SQL has 5 loop structures LOOP…EXIT WHEN WHILE …..LOOP Numeric FOR loops
The LOOP...EXIT WHEN Loop LOOP END LOOP; The LOOP…EXITWHEN loop can also be either a pretest or a post test loop. LOOP program statements EXIT WHEN condition; END LOOP;
The WHILE...LOOP WHILE condition LOOP END LOOP; The WHILE….LOOP is a pretest loop that evaluates the exit condition before it executes any program statement. WHILE condition LOOP program statements END LOOP;
The Numeric FOR Loop Does not require explicit counter increment The loop counter variable and it start and end values are declared in the loop’s FOR statement. Automatically increments counter variable until it reaches the end value. FOR counter_variable IN start_value .. end_value LOOP program statements END LOOP;
Using SQL Queries in PL/SQL Programs Action queries can be used as in SQL*Plus May use variables in action queries DDL commands may not be used in PL/SQL
Using SQL Commands in PL/SQL Programs
PL/SQL block to insert a new row in the VENDOR table SET SERVEROUTPUT ON BEGIN INSERT INTO VENDOR VALUES (25772,’Clue Store’, ‘Issac Hayes’, ‘456’, ‘323-2009’, ‘VA’, ‘N’); DBMS.OUTPUT.PUT_LINE(‘New Vendor Added’); END; / -- to execute An anonymous PL/SQL Program
PL/SQL block to count the number of products having different price ranges DECLARE W_P1 NUMBER(3) := 0; W_p2 number(3) := 10; W_P1 NUMBER(2) := 0; BEGIN WHILE W_P2 <300 LOOP SELECT COUNT(P_CODE) INTO W_NUM FROM PRODUCT WHERE P_PRICE BETWEEN W_P1 AND W_P2; DBMS_OUTPUT.PUT_LINE (‘The number of products between’ ||W_P1||’and’ || W_P2 || ‘is ’ || W_NUM); W_P1 := W_P2+1; W_P2 := W_P2+ 50 ; END LOOP; END; /
Refer to the handouts for more explanation on IN/OUT arguments STORED PROCEDURES A stored procedures is a named collection of procedural and SQL statements. Stored procedures are stored in the database. They can be used to encapsulate and represent business transactions. Syntax: CREATE OR REPLACE PROCEDURE procedure_name [(argument [IN/OUT] data-type,….. )] [IS/AS] [variable_name data_type[:=initial_value] ] BEGIN PL/SQL or SQL statements …. END; Refer to the handouts for more explanation on IN/OUT arguments
STORED PROCEDURES Advantages: Stored procedures substantially reduce traffic as there is no transmission of individual SQL statements over the network. Stored procedures increase performance because all the transactions are executed locally on the RDBMS.
CREATE OR REPLACE PROCEDURE PRC_PROD_DISCOUNT AS BEGIN UPDATE PRODUCT Create a procedure to assign an additional 5percent discount for all products when the quantity on Hand is more than or equal to twice the minimum quantity. CREATE OR REPLACE PROCEDURE PRC_PROD_DISCOUNT AS BEGIN UPDATE PRODUCT SET P_DISCOUNT = P_DISCOUNT + .05 WHERE P_QOH >= P_MIN*2; DBMS_OUTPUT.PUT_LINE ('* * Update finished * *'); END;
STORED PROCEDURES To execute the stored procedure, use the followijg syntax: EXEC procedure_name[(paramenter_list)]
Create a procedure to assign an additional percent discount (an input variable) for all products when the quantity on Hand is more than or equal to twice the minimum quantity. CREATE OR REPLACE PROCEDURE PRC_PROD_DISCOUNT(WPI IN NUMBER) AS BEGIN IF ((WPI <= 0) OR (WPI >= 1)) THEN -- validate WPI parameter DBMS_OUTPUT.PUT_LINE('Error: Value must be greater than 0 and less than 1'); ELSE -- if value is greater than 0 and less than 1 UPDATE PRODUCT SET P_DISCOUNT = P_DISCOUNT + WPI WHERE P_QOH >= P_MIN*2; DBMS_OUTPUT.PUT_LINE ('* * Update finished * *'); END IF; END;
Run the Procedure EXEC PRC_PROD_DISCOUNT (1.5); What is the outcome? EXEC PRC_PROD_DISCOUNT (.05);
Oracle Sequences MS Access AutoNumber data type fills a column with unique numeric values Oracle sequences Independent object in the database Named, used anywhere a value expected Not tied to a table or column Generate numeric values that can be assigned to any column in any table Created and deleted at any time
Create a procedure which adds a new invoice. CREATE OR REPLACE PROCEDURE PRC_INV_ADD (W_CUS_CODE IN VARCHAR2, W_DATE IN DATE) AS BEGIN INSERT INTO INVOICE VALUES(INV_NUMBER_SEQ.NEXTVAL, W_CUS_CODE, W_DATE); DBMS_OUTPUT.PUT_LINE('Invoice added'); END;
Create a procedure to add a new customer CREATE OR REPLACE PROCEDURE PRC_CUS_ADD (W_LN IN VARCHAR, W_FN IN VARCHAR, W_INIT IN VARCHAR, W_AC IN VARCHAR, W_PH IN VARCHAR) AS BEGIN -- note that the procedure uses the CUS_CODE_SEQ sequence created earlier. Attribute names are required when not giving values for all table attributes INSERT INTO CUSTOMER(CUS_CODE,CUS_LNAME, CUS_FNAME, CUS_INITIAL, CUS_AREACODE, CUS_PHONE) VALUES (CUS_CODE_SEQ.NEXTVAL, W_LN, W_FN, W_INIT, W_AC, W_PH); DBMS_OUTPUT.PUT_LINE ('Customer ' || W_LN || ', ' || W_FN || ' added.'); END; /
EXEC PRC_CUS_ADD(‘Walker’, ‘James’,NULL, ‘615’, ‘84-HORSE’); The result is Customer Walker, James added. PL/SQL procedure successfully completed.
Create a procedure which adds a new product line row for a given invoice. CREATE OR REPLACE PROCEDURE PRC_LINE_ADD (W_LN IN NUMBER, W_P_CODE IN VARCHAR2, W_LU NUMBER) AS W_LP NUMBER := 0.00; BEGIN -- GET THE PRODUCT PRICE SELECT P_PRICE INTO W_LP FROM PRODUCT WHERE P_CODE = W_P_CODE; -- ADDS THE NEW LINE ROW INSERT INTO LINE VALUES(INV_NUMBER_SEQ.CURRVAL, W_LN, W_P_CODE, W_LU, W_LP,null); DBMS_OUTPUT.PUT_LINE('Invoice line ' || W_LN || ' added'); END; In order to insert a new record in the LINE table, we need to know the Invoice Number, Product Code, Line Units (the values are obtained from the input parameters), Line Price ( the value is obtained from the Product Price i.e. p_price) in a PRODUCT Table
Cursor A cursor is a special construct in procedural SQL to hold the data rows returned by an SQL query. A cursor is a reserved area of memory in which the output of the query is stored like an array holding columns and rows. Cursors are held in a reserved memory area in the DBMS server, not in the client computer.
Cursors A cursor is a pointer to a memory location on database server that the DBMS uses to process a SQL query Cursors are used to retrieve and manipulate database data in PL/SQL programs Types: Implicit Explicit
Implicit Cursor
Implicit Cursors Context area Active set Implicit cursor A memory location on the database server Contains information about query e.g. no. of rows that the query processes, and a machine language representation of the query etc. Created by INSERT, UPDATE, DELETE, or SELECT Active set For SELECT queries, the context area also stores the active set, which is the set of data rows that query retrieves Implicit cursor is a pointer to the context area For Implicit cursor, there is no need to write commands explicitly to create it or retrieve its value.
Implicit Cursors Executing a SELECT query creates an implicit cursor i.e. Implicit cursor can be used to assign the output of a SELECT query to PL/SQL program variables when we are sure that the query will return one and only one record. If the query returns more than one record, or does not return any record, an error occurs.
Using an Implicit Cursor To retrieve data values from a query’s implicit cursor into PL/SQL program variables add INTO clause : SELECT field1, field2, ... INTO variable1, variable2, ... FROM table1, table2, ... WHERE join_ conditions AND search_condition_to_retrieve_1_record; The variables must have been declared in the program’s declaration section The variables must have the same data types as the associated database fields.
Reference Variables Reference variables directly reference specific database field or record and assume data type of the associated field or record %TYPE: is a reference data type which assumes the data type of a database field data declaration syntax: variable_name tablename.fieldname%TYPE; e.g. current_c_last Customer.Cus_Lname%TYPE; The current_c_last variable would assume a data type of VARCHAR2(15), because this is the data type of Cus_Lname field in the CUSTOMER table
Implicit Cursors (continued) Useful to use %TYPE reference data type to declare variables used with implicit cursors variable_name tablename.fieldname%TYPE e.g. current_c_last customer.cus_lname%TYPE Variable name Table name Field name in the table
Reference Variables %ROWTYPE: is a reference data type which assumes the data type of a database record data declaration syntax: row_variable_name tablename%ROWTYPE; e.g. customer_row Customer%ROWTYPE; The CUSTOMER_ROW references all of the fields in the CUSTOMER TABLE, and each field has the same data type as its associated database fields.
Reference Variables customer_row.cus_fname customer_row.cus_balance How do you refer to individual data field within a %ROWTYPE variable? Use the syntax row_variable_name.fieldname If u have to refer to f_first name, you can write customer_row.cus_fname or the balance, you can write customer_row.cus_balance
Reference Data Type used with Cursors %TYPE: is a reference data type which assumes the data type of a database field data declaration syntax: variable_name Cursor_Name.fieldname%TYPE; e.g. current_c_last cus_cursor.cus_fname%TYPE; %ROWTYPE: is a reference data type which assumes the data type of a database record variable_name Cursor_Name%ROWTYPE; e.g. customer_row cus_cursor%ROWTYPE;
PL/SQL program that displays last name and first name from the CUSTROMER table where cus_code =10010 using an implicit cursor SQL> DECLARE 2 current_c_last customer.cus_lname%TYPE; 3 current_c_first customer.cus_fname%TYPE; 4 BEGIN 5 SELECT cus_lname, cus_fname 6 INTO current_C_last, current_C_first 7 FROM customer 8 WHERE cus_code = 10010; 9 DBMS_OUTPUT.PUT_LINE(' The customer ''s name is ' || current_f_first || ' ' || current_f_last); 10 END; 11 / The customer's name is Ramas Alfred PL/SQL procedure successfully completed.
Implicit cursor query tried to retrieve multiple records Error “ORA-01422: exact fetch returns more than requested number of rows” SQL> DECLARE 2 current_c_last customer.cus_lname%TYPE; 3 current_c_first customer.cus_fname%TYPE; 4 BEGIN 5 SELECT cus_lname, cus_fname 6 INTO current_f_last, current_f_first 7 FROM customer 8 WHERE cus_lname LIKE ‘S%’; 9 DBMS_OUTPUT.PUT_LINE(' The customer ''s name is ' || current_f_first || ' ' || current_f_last); 10 END; 11 / DECLARE * ERROR at line 1: ORA-01422: exact fetch returns more than requested number of rows ORA-06512: at line 8
When implicit cursor query returns no records SQL> DECLARE 2 current_c_last customer.cus_lname%TYPE; 3 current_c_first customer.cus_fname%TYPE; 4 BEGIN 5 SELECT cus_lname, cus_fname 6 INTO current_f_last, current_f_first 7 FROM customer 8 WHERE cus_code= 12345; 9 DBMS_OUTPUT.PUT_LINE(' The customer ''s name is ' || current_f_first || ' ' || current_f_last); 10 END; 11 / DECLARE * ERROR at line 1: ORA-01403: no data found ORA-06512: at line 5
Explicit Cursors Explicit cursors are created to retrieve and display data in PL/SQL programs for query that might Retrieve multiple records Return no records at all Explicit cursors must be explicitly declared in the program’s declaration section Explicit commands are written to process the cursor. Steps for creating and using explicit cursor Declare cursor Open cursor Fetch data rows Close cursor
Using an Explicit Cursor Declaring an Explicit Cursor By declaring an EC, a memory location is created on the database server that processes query and stores the records that the query retrieves CURSOR cursor_name IS select_query; Cursor_name can be any valid PL/SQL variable name. Select_query is the query that retrieves the desired data value ( simple, complex , nested query etc.) The query’s search condition can contain PL/SQL variables as long as the variables are declared before the cursor is declared and are assigned values before the program opens and processes the cursor.
Using an Explicit Cursor Open the cursor OPEN cursor_name; When the open command is executed, the PL/SQL interpreter examines or parses the cursor’s SQL query, confirms that the query contains no syntax errors and translates the query into a machine language format. The system stores the parsed query in the cursor’s context area and creates the memory structure that will store the active set. The cursor does not retrieve the data values at this stage.
Using an Explicit Cursor Fetching the data rows LOOP FETCH cursor_name INTO variable_name(s); EXIT WHEN cursor_name%NOTFOUND; It retrieves the query data from the database into the active set, one row at a time. The FETCH command associates each field value with a program variable. The FETCH command is executed within a loop in order for the query to return several records. The cursor variable or variables are declared using either the %TYPE or %ROWTYPE reference data type.
Using an Explicit Cursor Active set pointer It is a pointer that indicates the memory location of next record that is retrieved from database When the FETCH command executes past the last record of the query, the active set pointer points an empty record. The exit condition cursor_name%NOTFOUND determines whether the last cursor record has been fetched by checking if the active pointer is pointing to the last record and then the loop exits. Closing the cursor Close the cursor after it processes all of the records in the active set so that its memory area and resources are available to the system for other tasks. CLOSE cursor_name; The system automatically closes the cursor when the program that processes the cursor ends.
Explicit Cursor with %TYPE It is used when a cursor retrieves one data field.
CREATE OR REPLACE PROCEDURE PRC_CURSOR_EXAMPLE IS Using cursors create a procedure that lists all products that have a quantity on hand greater than the average quantity on hand for all products. CREATE OR REPLACE PROCEDURE PRC_CURSOR_EXAMPLE IS W_P_CODE PRODUCT.P_CODE%TYPE; W_P_DESCRIPT PRODUCT.P_DESCRIPT%TYPE; --W_TOT NUMBER(3); CURSOR PROD_CURSOR IS SELECT P_CODE, P_DESCRIPT FROM PRODUCT WHERE P_QOH > (SELECT AVG(P_QOH) FROM PRODUCT); BEGIN DBMS_OUTPUT.PUT_LINE('PRODUCTS WITH P_QOH > AVG(P_QOH)');
DBMS_OUTPUT.PUT_LINE('============================ =========='); OPEN PROD_CURSOR; LOOP FETCH PROD_CURSOR INTO W_P_CODE, W_P_DESCRIPT; EXIT WHEN PROD_CURSOR%NOTFOUND; DBMS_OUTPUT.PUT_LINE(W_P_CODE ||' -> ' || W_P_DESCRIPT ); END LOOP; DBMS_OUTPUT.PUT_LINE('TOTAL PRODUCT PROCESSED ' || PROD_CURSOR%ROWCOUNT); DBMS_OUTPUT.PUT_LINE('--- END OF REPORT ----'); CLOSE PROD_CURSOR; END; /
current_cus_code NUMBER(4); CURSOR invoice_cursor IS SELECT inv_number Write a program that uses an Explicit Cursor to retrieve and display the invoice number value (using %TYPE variable) for every record in the INVOICE table for the customer code 10014. DECLARE current_cus_code NUMBER(4); CURSOR invoice_cursor IS SELECT inv_number FROM invoice WHERE inv_number = current_cus_code; current_inv invoice.inv_number%TYPE; BEGIN OPEN invoice_cursor; current_inv_num := ‘1001'; DBMS_OUTPUT.PUT_LINE('The current invoice number for ' || current_cus_code || are as follows’); LOOP FETCH invoice_cursor INTO current_inv; EXIT WHEN invoice_cursor%NOTFOUND; DBMS_OUTPUT.PUT_LINE(current_ínv); END LOOP; CLOSE invoice_cursor; END; /
Explicit Cursor with %ROWTYPE It is used when a cursor retrieves multiple data fields. Use a single variable that has %ROWTYPE reference data type Assume the same data type as the row that the cursor retrieves. How to declare a cursor %ROWTYPE variable row_variable_name cursor_name%ROWTYPE e.g. customer_row location_cursor%ROWTYPE To reference individual data fields within a %ROWTYPE variable, use row_variable_name.fieldname e.g. customer_row.room to reference the ROOM field in the location_row variable.
current_inv_num NUMBER(4); CURSOR invoice_cursor IS SELECT p_code Write a program that uses an Explicit Cursor to retrieve and display the invoice number value (using %TYPE variable) for every record in the INVOICE table in which the customer code is 10014. DECLARE current_inv_num NUMBER(4); CURSOR invoice_cursor IS SELECT p_code FROM invoice WHERE inv_number = current_inv_num; current_p_code invoice_cursor%ROWTYPE; BEGIN OPEN invoice_cursor; current_inv_num := ‘1001'; LOOP FETCH invoice_cursor INTO invoice_cursor; EXIT WHEN invoice_cursor%NOTFOUND; DBMS_OUTPUT.PUT_LINE('The current product code in ' || current_inv_num || ' ' || invoice_p_code.p_code); END LOOP; CLOSE invoice_cursor; END; /
Triggers A Trigger is procedural SQL code that is automatically invoked by the RDBMS upon the occurrence of a given data manipulation event. A trigger is invoked before or after a data row is actually inserted, updated or deleted. A trigger is associated within a database table. Each database table may have one or more triggers. A trigger is executed as part of the transaction that triggered it.
Uses of Triggers Triggers can be used to enforce constraints that cannot be enforced at the DBMS design and implementation levels. Triggers add functionality by automating critical actions and providing appropriate warnings and suggestions for remedial action. Triggers can be used to update table values, insert records in tables and call other stored procedures.
Syntax to create a Trigger in Oracle CREATE OR REPLACE TRIGGER trigger_name [BEFORE/AFTER] [DELETE/ INSERT/ UPDATE OF column_name] ON table_name [FOR EACH ROW] [DECLARE] [varaible_namedata type[:=initial_value] ] BEGIN PL/SQL instructions; …………. END;
Syntax to create a Trigger in Oracle The Triggering timing: BEFORE or AFTER. This timing indicates when the trigger’s PL/SQL code executes; in this case, before or after the triggering statement is completed. (BEFORE means before the changes are permanently saved to disk but after the changes are made in memory.) The triggering event: the statement that causes the trigger to execute (INSERT, UPDATE or DELETE). The triggering level: A statement-level trigger is assumed if we omit the FOR EACH ROW keywords. This executes only once before or after the triggering statement is completed. A row-level trigger requires use of the FOR EACH ROW keywords. This is executed once for reach row affected by the triggering statement. The Triggering action: The PL/SQL code enclosed between the BEGIN and END keywords. Each statement inside the PL/SQL code must end with a semicolon.
Example Create a trigger which should be activated when a product’s quantity on hand is updated when the product is sold, the system should automatically check whether the quantity on hand falls below its minimum allowable quantity. CREATE OR REPLACE TRIGGER TRG_PRODUCT_REORDER AFTER INSERT OR UPDATE OF P_QOH ON PRODUCT BEGIN UPDATE PRODUCT SET P_REORDER = 1 WHER P_QOH <= P_MIN; END; /
Verifying The Trigger SELECT * FROM PRODUCT WHERE P_CODE =‘11QER/31’; -------- UPDATE PRODUCT SET P_QOH = 4 WHERE P_CODE =‘ 11QER/31’;
SECOND version of the TRG_PRODUCT_REORDER TRIGGER What happens if we reduce the minimum quantity of product ‘2232/QWE’? When we update the P_MIN column, the trigger is never executed. TRG_PRODUCT_REORDER executes only after an update of the P_QOH column. To avoid that inconsistency we need to modify the trigger event. CREATE OR REPLACE TRIGGER TRG_PRODUCT_REORDER AFTER INSERT OR UPDATE OF P_QOH, P_MIN ON PRODUCT BEGIN UPDATE PRODUCT SET P_REORDER = 1 WHER P_QOH <= P_MIN; END; /
What happens if we change the P_QOH value for product ‘11QER/31 from 4 to 29 when the P_MIN is set to 5 and P_REORDER is 1’ The P_REORDER flag is still set to 1 because the trigger sets the P_REORDER value only to1;it does not reset the value to 0, even if such an action is clearly required when the inventory level is back to a value greater than the minimum value. Why did not the trigger change the reorder flag to 0?
Third version of the TRG_PRODUCT_REORDER TRIGGER To avoid that inconsistency we need to modify the trigger event. CREATE OR REPLACE TRIGGER TRG_PRODUCT_REORDER BEFORE INSERT OR UPDATE OF P_QOH, P_MIN ON PRODUCT FOR EACH ROW BEGIN IF :NEW.P_QOH <=:NEW.P_MIN THEN :NEW.P_REORDER :=1; ELSE :NEW.P_REORDER :=0; ENDIF; END; /
Third version of the TRG_PRODUCT_REORDER TRIGGER The computer cannot change anything directly in permanent storage (disk).It must read the data from permanent storage to primary memory; then it makes the change in primary memory and finally, it writes the changed data back to permanent memory (disk). All changes are done first in primary memory, then are transferred to permanent memory.
Third version of the TRG_PRODUCT_REORDER TRIGGER To ensure data integrity, DBMS makes two copies of every row being changed by a DML statement. The first copy contains the original(old) value of the attributes before the changes. The second copy contains the changed(new) values of the attributes that will be permanently saved to the database.
Create a Trigger to update an attribute in a table other than the one being modified Example: Create a trigger that automatically reduces the quantity on hand of a product with every sale. CREATE OR REPLACE TRIGGER TRG_LINE_PROD AFTER INSERT ON LINE FOR EACH ROW BEGIN UPDATE PRODUCT SET P_QOH =P_QOH - :NEW.LINE_UNITS WHERE PRODUCT.P_CODE = :NEW.P_CODE; END; /
Use of variables within a trigger Example: Create a trigger to update the customer balance(CUS_BALANCE) in the CUSTOMER table after inserting every new LINE row. The trigger code is CREATE OR REPLACE TRIGGER TRG_LINE_CUS AFTER INSERT ON LINE FOR EACH ROW DECLARE W_CUS CHAR(5); W_TOT NUMBER:= 0; -- to compute total cost BEGIN -- this trigger fires up after an INSERT of a LINE -- it will update the CUS_BALANCE in CUSTOMER
Use of variables within a trigger -- 1) get the CUS_CODE SELECT CUS_CODE INTO W_CUS FROM INVOICE WHERE INVOICE.INV_NUMBER = :NEW.INV_NUMBER; -- 2) compute the total of the current line W_TOT := :NEW.LINE_PRICE * :NEW.LINE_UNITS; -- 3) Update the CUS_BALANCE in CUSTOMER UPDATE CUSTOMER SET CUS_BALANCE = CUS_BALANCE + W_TOT WHERE CUS_CODE = W_CUS; END; DBMS_OUTPUT.PUT_LINE(' * * * Balance updated for customer: ' || W_CUS); /
Deleting a Trigger Although triggers are independent objects, they are associated with database tables. When you delete a table, all its trigger objects are deleted with it. To delete a trigger DROP TRIGGER trigger_name;
Trigger Action Based On Conditional DML Predicates Triggers are created depending on the type of DML statements such as INSERT or UPDATE or DELETE that fires the trigger. But how would we know which one of the three statements caused the trigger to execute? In those cases, we could use the following syntax: IF UPDATTING THEN ….ENDIF; IF INSERTING THEN… ENDIF; IF DELETING THEN … ENDIF;
PL/SQL Stored Functions We can create our own stored functions. Stored procedures and functions are very similar. A stored function is basically a named group of procedural and SQL statements that returns a value indicated by a RETURN in its program code. We can use function in the right side of the assignment statement.
PL/SQL Stored Functions Syntax: CREATE FUNCTION function_name(argument IN data-type,….) RETURN data-type [IS] BEGIN PL/SQL statements; …… RETURN (value or expression); END;