Chapter 8 Advanced SQL Chapter 8 Advanced SQL NOTE Several points are worth emphasizing: We have provided the SQL scripts for the chapters. These scripts are intended to facilitate the flow of the material presented to the class. However, given the comments made by our students, the scripts should not replace the manual typing of the SQL commands by students. Some students learn SQL better when they have a chance to type their own commands and get the feedback provided by their errors. We recommend that the students use their lab time to practice the commands manually. In this chapter, most of the queries are executed in the Oracle RDBMS. This chapter uses features that are restricted to client/server DBMSes. Such features are not commonly available in a desktop DBMS such as Microsoft Access. Answers to Review Questions 1. The relational set operators UNION, INTERSECT, and MINUS work properly only if the relations are union-compatible. What does union-compatible mean, and how would you check for this condition? Union compatible means that the relations yield attributes with identical names and compatible data types. That is, the relation A(c1,c2,c3) and the relation B(c1,c2,c3) have union compatibility if the columns have the same names, are in the same order, and the columns have “compatible” data types. Compatible data types do not require that the attributes be exactly identical -- only that they are comparable. For example, VARCHAR(15) and CHAR(15) are comparable, as are NUMBER (3,0) and INTEGER, and so on. 2. What is the difference between UNION and UNION ALL? Write the syntax for each. UNION yields unique rows. In other words, UNION eliminates duplicates rows. On the other hand, a UNION ALL operator will yield all rows of both relations, including duplicates. Notice that for two rows to be duplicated, they must have the same values in all columns. To illustrate the difference between UNION and UNION ALL, let’s assume two relations: A (ID, Name) with rows (1, Lake, 2, River, and 3, Ocean) and B (ID, Name) with rows (1, River, 2, Lake, and 3, Ocean). Given this description, 271 Chapter 8 Advanced SQL SELECT * FROM A UNION SELECT * FROM B will yield: ID 1 2 3 1 2 Name Lake River Ocean River Lake while SELECT * FROM A UNION ALL SELECT * FROM B will yield: ID 1 2 3 1 2 3 Name Lake River Ocean River Lake Ocean 3. Suppose that you have two tables, EMPLOYEE and EMPLOYEE_1. The EMPLOYEE table contains the records for three employees: Alice Cordoza, John Cretchakov, and Anne McDonald. The EMPLOYEE_1 table contains the records for employees John Cretchakov and Mary Chen. Given that information, what is the query output for the UNION query? (List the query output.) The query output will be: Alice Cordoza John Cretchakov Anne McDonald Mary Chen 272 Chapter 8 Advanced SQL 4. Given the employee information in Question 3, what is the query output for the UNION ALL query? (List the query output.) The query output will be: Alice Cordoza John Cretchakov Anne McDonald John Cretchakov Mary Chen 5. Given the employee information in Question 3, what is the query output for the INTERSECT query? (List the query output.) The query output will be: John Cretchakov 6. Given the employee information in Question 3, what is the query output for the MINUS query? (List the query output.) This question can yield two different answers. If you use SELECT * FROM EMPLOYEE MINUS SELECT * FROM EMPLOYEE_1 the answer is Alice Cordoza Ann McDonald If you use SELECT * FROM EMPLOYEE_1 MINUS SELECT * FROM EMPLOYEE the answer is Mary Chen 7. What is a CROSS JOIN? Give an example of its syntax. A CROSS JOIN is identical to the PRODUCT relational operator. The CROSS JOIN is also known as the Cartesian product of two tables. For example, if you have two tables, AGENT, with 10 rows 273 Chapter 8 Advanced SQL and CUSTOMER, with 21 rows, the CROSS JOIN resulting set will have 210 rows and will include all of the columns from both tables. Syntax examples are: SELECT * FROM CUSTOMER CROSS JOIN AGENT; or SELECT * FROM CUSTOMER, AGENT If you do not specify a join condition when joining tables, the result will be a CROSS Join or PRODUCT operation. 8. What three join types are included in the OUTER JOIN classification? An OUTER JOIN is a type of JOIN operation that yields all rows with matching values in the join columns as well as all unmatched rows. (Unmatched rows are those without matching values in the join columns). The SQL standard prescribes three different types of join operations: LEFT [OUTER] JOIN RIGHT [OUTER] JOIN FULL [OUTER] JOIN. The LEFT [OUTER] JOIN will yield all rows with matching values in the join columns, plus all of the unmatched rows from the left table. (The left table is the first table named in the FROM clause.) The RIGHT [OUTER] JOIN will yield all rows with matching values in the join columns, plus all of the unmatched rows from the right table. (The right table is the second table named in the FROM clause.) The FULL [OUTER] JOIN will yield all rows with matching values in the join columns, plus all the unmatched rows from both tables named in the FROM clause. 9. Using tables named T1 and T2, write a query example for each of the three join types you described in Question 8. Assume that T1 and T2 share a common column named C1. LEFT OUTER JOIN example: SELECT * FROM T1 LEFT OUTER JOIN T2 ON T1.C1 = T2.C1; RIGHT OUTER JOIN example: SELECT * FROM T1 RIGHT OUTER JOIN T2 ON T1.C1 = T2.C1; FULL OUTER JOIN example: SELECT * FROM T1 FULL OUTER JOIN T2 ON T1.C1 = T2.C1; 274 Chapter 8 Advanced SQL 10. What is a subquery, and what are its basic characteristics? A subquery is a query (expressed as a SELECT statement) that is located inside another query. The first SQL statement is known as the outer query, the second is known as the inner query or subquery. The inner query or subquery is normally executed first. The output of the inner query is used as the input for the outer query. A subquery is normally expressed inside parenthesis and can return zero, one, or more rows and each row can have one or more columns. A subquery can appear in many places in a SQL statement: as part of a FROM clause, to the right of a WHERE conditional expression, to the right of the IN clause, in a EXISTS operator, to the right of a HAVING clause conditional operator, in the attribute list of a SELECT clause. Examples of subqueries are: INSERT INTO PRODUCT SELECT * FROM P; DELETE FROM PRODUCT WHERE V_CODE IN (SELECT V_CODE FROM VENDOR WHERE V_AREACODE = ‘615’); SELECT FROM WHERE V_CODE, V_NAME VENDOR V_CODE NOT IN (SELECT V_CODE FROM PRODUCT); 11. What is a correlated subquery? Give an example. A correlated subquery is subquery that executes once for each row in the outer query. This process is similar to the typical nested loop in a programming language. Contrast this type of subquery to the typical subquery that will execute the innermost subquery first, and then the next outer query … until the last outer query is executed. That is, the typical subquery will execute in serial order, one after another, starting with the innermost subquery. In contrast, a correlated subquery will run the outer query first, and then it will run the inner subquery once for each row returned in the outer subquery. For example, the following subquery will list all the product line sales in which the “units sold” value is greater than the “average units sold” value for that product (as opposed to the average for all products.) 275 Chapter 8 Advanced SQL SELECT FROM WHERE INV_NUMBER, P_CODE, LINE_UNITS LINE LS LS.LINE_UNITS > (SELECT AVG(LINE_UNITS) FROM LINE LA WHERE LA.P_CODE = LS.P_CODE); The previous nested query will execute the inner subquery once to compute the average sold units for each product code returned by the outer query. 12. What MS Access/SQL Server function should you use to calculate the number of days between the current date and January 25, 1999? SELECT DATE()-#25-JAN-1999# NOTE: In MS Access you do not need to specify a FROM clause for this type of query. 13. What Oracle function should you use to calculate the number of days between the current date and January 25, 1999? SELECT FROM SYSDATE – TO_DATE(’25-JAN-1999’, ‘DD-MON-YYYY’) DUAL; Note that in Oracle, the SQL statement requires the use of the FROM clause. In this case, you must use a DUAL table. (A DUAL table is a dummy “virtual” table provided by Oracle for this type of query. 14. Suppose that a PRODUCT table contains two attributes, PROD_CODE and VEND_CODE. Those two attributes have values of ABC, 125, DEF, 124, GHI, 124, and JKL, 123, respectively. The VENDOR table contains a single attribute, VEND_CODE, with values 123, 124, 125, and 126, respectively. (The VEND_CODE attribute in the PRODUCT table is a foreign key to the VEND_CODE in the VENDOR table.) Given that information, what would be the query output for: Because the common attribute is V_CODE, the output will only show the V_CODE values generated by the each query. a. A UNION query based on these two tables? 125,124,123,126 b. A UNION ALL query based on these two tables? 125,124,124,123,123,124,125,126 c. An INTERSECT query based on these two tables? 123,124,125 276 Chapter 8 Advanced SQL d. A MINUS query based on these two tables? If you use PRODUCT MINUS VENDOR, the output will be NULL If you use VENDOR MINUS PRODUCT, the output will be 126 15. What Oracle string function should you use to list the first three characters of a company’s EMP_LNAME values? Give an example, using a table named EMPLOYEE. You must use the SUBSTR function as illustrated next: SELECT SUBSTR(EMP_LNAME,1,3) FROM EMPLOYEE; 16. What is an Oracle sequence? Write its syntax. An Oracle sequence is a special type of object that generates unique numeric values in ascending or descending order. You can use a sequence to assign values to a primary key field in a table. A sequence provides functionality similar to the Autonumber data type in MS Access. For example, both, sequences and Autonumber data types provide unique ascending or descending values. However, there are some subtle differences between the two: In MS Access an Autonumber is a data type; in Oracle a sequence is a completely independent object, rather than a data type. In MS Access, you can only have one Autonumber per table; in Oracle you can have as many sequences as you want and they are not tied to any particular table. In MS Access, the Autonumber data type is tied to a field in a table; in Oracle, the sequencegenerated value is not tied to any field in any table and can, therefore, be used on any attribute in any table. The syntax used to create a sequence is: CREATE SEQUENCE CUS_NUM_SQ START WITH 100 INCREMENT BY 10 NOCACHE; 17. What is a trigger, and what is its purpose? Give an example. A trigger is a block of PL/SQL code that is automatically invoked by the DBMS upon the occurrence of a data manipulation event (INSERT, UPDATE or DELETE.) Triggers are always associated with a table and are invoked before or after a data row is inserted, updated, or deleted. Any table can have one or more triggers. Triggers provide a method of enforcing business rules such as: A customer making a credit purchase must have an active account. A student taking a class with a prerequisite must have completed that prerequisite with a B grade. To be scheduled for a flight, a pilot must have a valid medical certificate and a valid training completion record. 277 Chapter 8 Advanced SQL Triggers are also excellent for enforcing data constraints that cannot be directly enforced by the data model. For example, suppose that you must enforce the following business rule: If the quantity on hand of a product falls below the minimum quantity, the P_REORDER attribute must the automatically set to 1. To enforce this business rule, you can create the following TRG_PRODUCT_REORDER trigger: CREATE OR REPLACE TRIGGER TRG_PRODUCT_REORDER BEFORE INSERT OR UPDATE OF P_ONHAND, P_MIN ON PRODUCT FOR EACH ROW BEGIN IF :NEW.P_ONHAND <= :NEW.PROD_MIN THEN NEW.P_REORDER := 1; ELSE :NEW.P_REORDER := 0; END IF; END; 18. What is a stored procedure, and why is it particularly useful? Give an example. A stored procedure is a named block of PL/SQL and SQL statements. One of the major advantages of stored procedures is that they can be used to encapsulate and represent business transactions. For example, you can create a stored procedure to represent a product sale, a credit update, or the addition of a new customer. You can encapsulate SQL statements within a single stored procedure and execute them as a single transaction. There are two clear advantages to the use of stored procedures: 1. Stored procedures substantially reduce network traffic and increase performance. Because the stored procedure is stored at the server, there is no transmission of individual SQL statements over the network. 2. Stored procedures help reduce code duplication through code isolation and code sharing (creating unique PL/SQL modules that are called by application programs), thereby minimizing the chance of errors and the cost of application development and maintenance. For example, the following PRC_LINE_ADD stored procedure will add a new invoice line to the LINE table and it will automatically retrieve the correct price from the PRODUCT table. 278 Chapter 8 Advanced SQL 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); DBMS_OUTPUT.PUT_LINE('Invoice line ' || W_LN || ' added'); END; 19. What is embedded SQL, and how is it used? Embedded SQL is a term used to refer to SQL statements that are contained within an application programming language such as COBOL, C++, ASP, Java, or ColdFusion. The program may be a standard binary executable in Windows or Linux, or it may be a Web application designed to run over the Internet. No matter what language you use, if it contains embedded SQL statements it is called the host language. Embedded SQL is still the most common approach to maintaining procedural capabilities in DBMS-based applications. For example, the following embedded SQL code will delete the employee 109, George Smith, from the EMPLOYEE table: EXEC SQL DELETE FROM EMPLOYEE WHERE EMP_NUM = 109; END-EXEC. Remember that the preceding embedded SQL statement is compiled to generate an executable statement. Therefore, the statement is fixed permanently; that is, it cannot change -- unless, of course, the programmer changes it. Each time the program runs, it deletes the same row. In short, the preceding code is good only for the first run; all subsequent runs will more than likely give an error. (Employee 109 is deleted the first time the embedded statement is executed. If you run the code again, it tries to delete an employee who has already been deleted.) Clearly, this code would be more useful if you are able to specify a variable to indicate which employee number is to be deleted. 279 Chapter 8 Advanced SQL 20. What is dynamic SQL, and how does it differ from static SQL? Dynamic SQL is a term used to describe an environment in which the SQL statement is not known in advance; instead, the SQL statement is generated at run time. In a dynamic SQL environment, a program can generate the SQL statements (at run time) that are required to respond to ad-hoc queries. In such an environment, neither the programmers nor end users are likely to know precisely what kind of queries are to be generated or how those queries are to be structured. For example, a dynamic SQL equivalent of the example shown in question 19 might be: SELECT :W_ATTRIBUTE_LIST FROM :W_TABLE WHERE :W_CONDITION; Note that the attribute list and the condition are not known until the end user specifies them. W_TABLE, W_ATRIBUTE_LIST and W_CONDITION are text variables that contain the end-user input values used in the query generation. Because the program uses the end user input to build the text variables, the end user can run the same program multiple times to generate different outputs. For example, in one case the end user may one to know what products have a price less than $100; in another case, the end user may want to know how many units of a given product are available for sale at any given moment. Although dynamic SQL is clearly flexible, such flexibility carries a price. Dynamic SQL tends to be much slower that static SQL and it requires more computer resources (overhead). In addition, you are more likely to find different levels of support and incompatibilities between DBMS vendors. 280 Chapter 8 Advanced SQL Problem Solutions Use the database tables in Figure P8.1 as the basis for problems 1-18. ONLINE CONTENT The Ch08_SimpleCo database is located in the Online Student Companion. This database is stored in Microsoft Access format. The Student Online Companion also includes SQL script files (Oracle and SQLServer) for all of the data sets used throughout the book. Figure P8.1 Ch08_SimpleCo Database Tables 1. Create the tables. (Use the MS Access example shown in Figure P8.1 to see what table names and attributes to use.) CREATE TABLE CUSTOMER ( CUST_NUM NUMBER PRIMARY KEY, CUST_LNAME VARCHAR(20), CUST_FNAME VARCHAR(20), CUST_BALANCE NUMBER); CREATE TABLE CUSTOMER_2 ( CUST_NUM NUMBER PRIMARY KEY, CUST_LNAME VARCHAR(20), CUST_FNAME VARCHAR(20)); 281 Chapter 8 Advanced SQL CREATE TABLE INVOICE ( INV_NUM NUMBER PRIMARY KEY, CUST_NUM NUMBER, INV_DATE DATE, INV_AMOUNT NUMBER); 2. Insert the data into the tables you created in Problem 1. INSERT INTO CUSTOMER VALUES(1000 ,'Smith' ,'Jeanne' ,1050.11); INSERT INTO CUSTOMER VALUES(1001 ,'Ortega' ,'Juan' ,840.92); INSERT INTO CUSTOMER_2 VALUES(2000 ,'McPherson' ,'Anne'); INSERT INTO CUSTOMER_2 VALUES(2001 ,'Ortega' ,'Juan'); INSERT INTO CUSTOMER_2 VALUES(2002 ,'Kowalski' ,'Jan'); INSERT INTO CUSTOMER_2 VALUES(2003 ,'Chen' ,'George'); INSERT INTO INVOICE VALUES(8000 ,1000 ,'23-APR-2008' ,235.89); INSERT INTO INVOICE VALUES(8001 ,1001 ,'23-MAR-2008' ,312.82); INSERT INTO INVOICE VALUES(8002 ,1001 ,'30-MAR-2008' ,528.1); INSERT INTO INVOICE VALUES(8003 ,1000 ,'12-APR-2008' ,194.78); INSERT INTO INVOICE VALUES(8004 ,1000 ,'23-APR-2008' ,619.44); 3. Write the query that will generate a combined list of customers (from tables CUSTOMER and CUSTOMER_2) that do not include the duplicate customer records. (Note that only the customer named Juan Ortega shows up in both customer tables.) SELECT CUST_LNAME, CUST_FNAME FROM CUSTOMER UNION SELECT CUST_LNAME, CUST_FNAME FROM CUSTOMER_2; 4. Write the query that will generate a combined list of customers to include the duplicate customer records. SELECT CUST_LNAME, CUST_FNAME FROM CUSTOMER UNION ALL SELECT CUST_LNAME, CUST_FNAME FROM CUSTOMER_2; 5. Write the query that will show only the duplicate customer records. We have shown both Oracle and MS Access query formats: In Oracle: SELECT CUST_LNAME, CUST_FNAME FROM CUSTOMER INTERSECT SELECT CUST_LNAME, CUST_FNAME FROM CUSTOMER_2; 282 Chapter 8 Advanced SQL In MS Access: SELECT C.CUST_LNAME, C.CUST_FNAME FROM CUSTOMER AS C, CUSTOMER_2 AS C2 WHERE C.CUST_LNAME=C2.CUST_LNAME AND C.CUST_FNAME=C2.CUST_FNAME; Because Access doesn’t support the INTERSECT SQL operator, you need to list only the rows in which all the attributes match. 6. Write the query that will generate only the records that are unique to the CUSTOMER_2 table. We have shown both Oracle and MS Access query formats: In Oracle: SELECT CUST_LNAME, CUST_FNAME FROM CUSTOMER_2 MINUS SELECT CUST_LNAME, CUST_FNAME FROM CUSTOMER; In MS Access: SELECT FROM WHERE C2.CUST_LNAME, C2.CUST_FNAME CUSTOMER_2 AS C2 C2.CUST_LNAME + C2.CUST_FNAME NOT IN (SELECT C1.CUST_LNAME + C1.CUST_FNAME FROM CUSTOMER C1); Because Access doesn’t support the MINUS SQL operator, you need to list only the rows that are in CUSTOMER_2 that do not have a matching row in CUSTOMER. 7. Write the query to show the invoice number, the customer number, the customer name, the invoice date, and the invoice amount for all customers with a customer balance of $1,000 or more. This command will run in Oracle and in MS Access: SELECT INV_NUM, CUSTOMER.CUST_NUM, CUST_LNAME, CUST_FNAME, INV_DATE, INV_AMOUNT FROM INVOICE INNER JOIN CUSTOMER ON INVOICE.CUST_NUM=CUSTOMER.CUST_NUM WHERE CUST_BALANCE>=1000; 283 Chapter 8 Advanced SQL 8. Write the query that will show the invoice number, the invoice amount, the average invoice amount, and the difference between the average invoice amount and the actual invoice amount. There are at least two ways to do this query. SELECT FROM GROUP BY INV_NUM, AVG_INV, (INV_AMOUNT - AVG_INV) AS DIFF INVOICE, (SELECT AVG(INV_AMOUNT) AS AVG_INV FROM INVOICE) INV_NUM, AVG_INV, INV_AMOUNT- AVG_INV Another way to write this query is: SELECT FROM GROUP BY INV_NUM, INV_AMOUNT, (SELECT AVG(INV_AMOUNT) FROM INVOICE) AS AVG_INV, (INV_AMOUNT-(SELECT AVG(INV_AMOUNT) FROM INVOICE)) AS DIFF INVOICE INV_NUM, INV_AMOUNT; The preceding code examples will run in both Oracle and MS Access. 9. Write the query that will write Oracle sequences to produce automatic customer number and invoice number values. Start the customer numbers at 1000 and the invoice numbers at 5000. The following code will only run in Oracle: CREATE SEQUENCE CUST_NUM_SQ START WITH 1000 NOCACHE; CREATE SEQUENCE INV_NUM_SQ START WITH 5000 NOCACHE; 10. Modify the CUSTOMER table to included two new attributes: CUST_DOB and CUST_AGE. Customer 1000 was born on March 15, 1979, and customer 1001 was born on December 22, 1988. In Oracle: ALTER TABLE CUSTOMER ADD (CUST_DOB DATE) ADD (CUST_AGE NUMBER); The SQL code required to enter the date values is: UPDATE CUSTOMER SET CUST_DOB = ’15-MAR-1979’ WHERE CUST_NUM = 1000; UPDATE CUSTOMER SET CUST_DOB = ‘2-DEC-1988’ WHERE CUST_NUM = 1001; 284 Chapter 8 Advanced SQL 11. Assuming you completed problem 10, write the query that will list the names and ages of your customers. In Oracle: SELECT CUST_LNAME, CUST_FNAME, ROUND((SYSDATE-CUST_DOB)/365,0) AS AGE FROM CUSTOMER; In MS Access: SELECT CUST_LNAME, CUST_FNAME, ROUND((DATE()-CUST_DOB)/365,0) AS AGE FROM CUSTOMER; NOTE The correct age computation may be computed by INT((DATE()-CUST_DOB)/365) However, students have not (yet) seen the INT function at this point -- which is why we used ROUND() function. 12. Assuming the CUSTOMER table contains a CUST_AGE attribute, write the query to update the values in that attribute. Hint: Use the results of the previous query. In Oracle: UPDATE CUSTOMER SET CUST_AGE = ROUND((SYSDATE-CUST_DOB)/365,0); In MS Access: UPDATE CUSTOMER SET CUST_AGE = ROUND((DATE()-CUST_DOB)/365,0); 13. Write the query that will list the average age of your customers. (Assume that the CUSTOMER table has been modified to include the CUST_DOB and the derived CUST_AGE attribute.) SELECT AVG(CUST_AGE) FROM CUSTOMER; 285 Chapter 8 Advanced SQL 14. Write the trigger to update the CUST_BALANCE in the CUSTOMER table when a new invoice record is entered. (Assume that the sale is a credit sale.) Test the trigger using the following new INVOICE record: 8005, 1001, ’27-APR-08’, 225.40 Name the trigger trg_updatecustbalance. CREATE OR REPLACE TRIGGER TRG_UPDATECUSTBALANCE AFTER INSERT ON INVOICE FOR EACH ROW BEGIN UPDATE CUSTOMER SET CUST_BALANCE = CUST_BALANCE + :NEW.INV_AMOUNT WHERE CUST_NUM = :NEW.CUST_NUM; END; To test the trigger you do the following: SELECT * FROM CUSTOMER; INSERT INTO INVOICE VALUES (8005,1001,’27-APR-08’,225.40); SELECT * FROM CUSTOMER; 15. Write a procedure to add a new customer to the CUSTOMER table. Use the following values in the new record: 1002, ‘Rauthor’, ‘Peter’, 0.00 Name the procedure prc_cust_add. Run a query to see if the record has been added. CREATE OR REPLACE PROCEDURE PRC_CUST_ADD (W_CN IN NUMBER, W_CLN IN VARCHAR, W_CFN IN VARCHAR, W_CBAL IN NUMBER) AS BEGIN INSERT INTO CUSTOMER (CUST_NUM, CUST_LNAME, CUST_FNAME, CUST_BALANCE) VALUES (W_CN, W_CLN, W_CFN, W_CBAL); END; To test the procedure: EXEC PRC_CUST_ADD(1002,’Rauthor’,’Peter’,0.00); SELECT * FROM CUSTOMER; 286 Chapter 8 Advanced SQL 16. Write a procedure to add a new invoice record to the INVOICE table. Use the following values in the new record: 8006, 1000, ’30-APR-08’, 301.72 Name the procedure prc_invoice_add. Run a query to see if the record has been added. CREATE OR REPLACE PROCEDURE PRC_INVOICE_ADD (W_IN IN NUMBER, W_CN IN NUMBER, W_ID IN DATE, W_IA IN NUMBER) AS BEGIN INSERT INTO INVOICE VALUES (W_IN, W_CN, W_ID, W_IA); END; To test the procedure: SELECT * FROM CUSTOMER; SELECT * FROM INVOICE; EXEC PRC_INVOICE_ADD(8006,1000,’30-APR-08’,301.72); SELECT * FROM INVOICE; SELECT * FROM CUSTOMER; 17. Write a trigger to update the customer balance when an invoice is deleted. Name the trigger trg_updatecustbalance2. CREATE OR REPLACE TRIGGER TRG_UPDATECUSTBALANCE2 AFTER DELETE ON INVOICE FOR EACH ROW BEGIN UPDATE CUSTOMER SET CUST_BALANCE = CUST_BALANCE - :OLD.INV_AMOUNT WHERE CUST_NUM = :OLD.CUST_NUM; END; 287 Chapter 8 Advanced SQL 18. Write a procedure to delete an invoice given the invoice number as a parameter. Name the procedure prc_inv_delete. Test the procedure by deleting invoices 8005 and 8006. CREATE OR REPLACE PROCEDURE PRC_INV_DELETE (W_IN IN NUMBER) AS BEGIN IF W_IN IS NOT NULL THEN DELETE FROM INVOICE WHERE INV_NUM = W_IN; END IF; END; To test the procedure: SELECT * FROM CUSTOMER; SELECT * FROM INVOICE; EXEC PRC_INV_DELETE(8005); EXEC PRC_INV_DELETE(8006); SELECT * FROM INVOICE; SELECT * FROM CUSTOMER; 288 Chapter 8 Advanced SQL NOTE The following problem sets can serve as the basis for a class project or case. Figure P8.19 Ch08_SaleCo2 Database Tables Use the Ch08_SaleCo2 database to work Problems 19-22. ONLINE CONTENT The Ch08_SaleCo2 database used in Problems 19-22 is located in the Online Student Companion. This database is stored in Microsoft Access format. The Student Online Companion also includes SQL script files (Oracle and SQLServer) for all of the data sets used throughout the book. 289 Chapter 8 Advanced SQL 19. Create a trigger named trg_line_total to write the LINE_TOTAL value in the LINE table every time you add a new LINE row. (The LINE_TOTAL value is the product of the LINE_UNITS and the LINE_PRICE values.) CREATE OR REPLACE TRIGGER TRG_LINE_TOTAL BEFORE INSERT ON LINE FOR EACH ROW BEGIN :NEW.LINE_TOTAL := :NEW.LINE_UNITS * :NEW.LINE_PRICE; END; 20. Create a trigger named trg_line_prod that will automatically update the product quantity on hand for each product sold after a new LINE row is added. 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; 21. Create a stored procedure named prc_inv_amounts to update the INV_SUBTOTAL, INV_TAX, and INV_TOTAL. The procedure takes the invoice number as a parameter. The INV_SUBTOTAL is the sum of the LINE_TOTAL amounts for the invoice, the INV_TAX is the product of the INV_SUBTOTAL and the tax rate (8%), and the INV_TOTAL is the sum of the INV_SUBTOTAL and the INV_TAX. CREATE OR REPLACE PROCEDURE PRC_INV_AMOUNTS (W_IN IN NUMBER) AS W_CHK NUMBER := 0; W_SUBT NUMBER := 0; W_TAX NUMBER := 0; BEGIN -- VALIDATE INVOICE NUMBER SELECT COUNT(*) INTO W_CHK FROM INVOICE WHERE INV_NUMBER = W_IN; IF W_CHK = 1 THEN -- COMPUTE THE W_SUBT SELECT SUM(LINE_TOTAL) INTO W_SUBT FROM LINE WHERE LINE.INV_NUMBER = W_IN; -- COMPUTE W_TAX W_TAX := W_SUBT * 0.08; -- UPDATE INVOICE UPDATE INVOICE 290 Chapter 8 Advanced SQL SET INV_SUBTOTAL = W_SUBT, INV_TAX = W_TAX, INV_TOTAL = W_SUBT + W_TAX WHERE INV_NUMBER = W_IN; END IF; END; 22. Create a procedure named prc_cus_balance_update that will take the invoice number as a parameter and update the customer balance. (Hint: You can use the DECLARE section to define a TOTINV numeric variable that holds the computed invoice total.) NOTE Actually, the TOTINV is not really needed – because the INVOICE table already contains the INV_TOTAL attribute. The procedure we have shown next uses the INV_TOTAL attribute. CREATE OR REPLACE PROCEDURE PRC_CUS_BALANCE_UPDATE (W_IN IN NUMBER) AS W_CUS NUMBER := 0; W_TOT NUMBER := 0; BEGIN -- GET THE CUS_CODE SELECT CUS_CODE INTO W_CUS FROM INVOICE WHERE INVOICE.INV_NUMBER = W_IN; -- UPDATES CUSTOMER IF W_CUS > 0 IF W_CUS > 0 THEN UPDATE CUSTOMER SET CUS_BALANCE = CUS_BALANCE + (SELECT INV_TOTAL FROM INVOICE WHERE INV_NUMBER = W_IN) WHERE CUS_CODE = W_CUS; END IF; END; 291 Chapter 8 Advanced SQL Use the Ch08_AviaCo database to work Problems 23-34. ONLINE CONTENT The Ch08_AviaCo database used for Problems 23−34 is located in the Online Student Companion. This database is stored in Microsoft Access format. The Student Online Companion also includes SQL script files (Oracle and SQLServer) for all of the data sets used throughout the book. Figure P8.23 Ch08_AviaCo Database Tables 292 Chapter 8 Advanced SQL 23. Modify the MODEL table to add the following attribute and insert the values shown in Table P8.23. Table P8.23 The New Attribute for the MODEL Table Attribute name MOD_WAIT_CHG Attribute Description Attribute type Waiting charge per Numeric hour for each model: Attribute Values $100 for C-90A $50 for PA23-250 $75 for PA31-350 ALTER TABLE MODEL ADD MOD_WAIT_CHG NUMBER; UPDATE MODEL SET MOD_WAIT_CHG = 100 WHERE MOD_CODE = ‘C-90A’; UPDATE MODEL SET MOD_WAIT_CHG = 50 WHERE MOD_CODE = ‘PA23-250’; UPDATE MODEL SET MOD_WAIT_CHG = 75 WHERE MOD_CODE = ‘PA31-350’; 24. Write the queries to update the MOD_WAIT_CHG attribute values based on problem 23. UPDATE MODEL SET MOD_WAIT_CHG = 100 UPDATE MODEL SET MOD_WAIT_CHG = 50 UPDATE MODEL SET MOD_WAIT_CHG = 75 WHERE MOD_CODE = 'C-90A'; WHERE MOD_CODE = 'PA23-250'; WHERE MOD_CODE = 'PA31-350'; 25. Modify the CHARTER table to add the attributes shown in Table P8.25. Table P8.25 The New Attributes for the CHARTER Table Attribute name CHAR_WAIT_CHG Attribute Description Attribute type Waiting charge for each model (copied from the Numeric MODEL table.) CHAR_FLT_CHG_HR Flight charge per mile for each model (copied Numeric from the MODEL table using the MOD_CHG_MILE attribute.) CHAR_FLT_CHG Flight charge (calculated by Numeric CHAR_HOURS_FLOWN x CHAR_FLT_CHG_HR) CHAR_TAX_CHG CHAR_TOT_CHG CHAR_PYMT CHAR_BALANCE CHAR_FLT_CHG x tax rate (8%) CHAR_FLT_CHG + CHAR_TAX_CHG Amount paid by customer Balance remaining after payment 293 Numeric Numeric Numeric Numeric Chapter 8 Advanced SQL ALTER TABLE CHARTER ADD CHAR_WAIT_CHG NUMBER ADD CHAR_FLT_CHG_HR NUMBER ADD CHAR_FLT_CHG NUMBER ADD CHAR_TAX_CHG NUMBER ADD CHAR_TOT_CHG NUMBER ADD CHAR_PYMT NUMBER ADD CHAR_BALANCE NUMBER; 26. Write the sequence of commands required to update the CHAR_WAIT_CHG attribute values in the CHARTER table. Hint: Use either an updatable view or a stored procedure. NOTE This query can be done with an UPDATE statement and a SELECT subquery. UPDATE CHARTER SET CHAR_WAIT_CHG = ( SELECT MOD_WAIT_CHG FROM MODEL, AIRCRAFT WHERE MODEL.MOD_CODE = AIRCRAFT.MOD_CODE AND AIRCRAFT.AC_NUMBER = CHARTER.AC_NUMBER); 27. Write the sequence of commands required to update the CHAR_FLT_CHG_HR attribute values in the CHARTER table. Hint: Use either an updatable view or a stored procedure. NOTE This query can be done with an UPDATE statement and a SELECT subquery. UPDATE CHARTER SET CHAR_FLT_CHG_HR = ( SELECT MOD_CHG_MILE FROM MODEL, AIRCRAFT WHERE MODEL.MOD_CODE = AIRCRAFT.MOD_CODE AND AIRCRAFT.AC_NUMBER = CHARTER.AC_NUMBER); 28. Write the command required to update the CHAR_FLT_CHG attribute values in the CHARTER table. UPDATE CHARTER SET CHAR_FLT_CHG = CHAR_HOURS_FLOWN * CHAR_FLT_CHG_HR; 29. Write the command required to update the CHAR_TAX_CHG attribute values in the CHARTER table. UPDATE CHARTER SET CHAR_TAX_CHG = CHAR_FLT_CHG * 0.08; 294 Chapter 8 Advanced SQL 30. Write the command required to update the CHAR_TOT_CHG attribute values in the CHARTER table. UPDATE CHARTER SET CHAR_TOT_CHG = CHAR_FLT_CHG + CHAR_TAX_CHG; 31. Modify the PILOT table to add the attribute shown in Table P8.21. Table P8.31 The New Attribute for the PILOT Table Attribute name PIL_PIC_HRS Attribute Description Attribute type Pilot in command (PIC) hours. Updated by adding the Numeric CHARTER table’s CHAR_HOURS_FLOWN to the PIL_PIC_HRS when the CREW table shows the CREW_JOB to be pilot. ALTER TABLE PILOT ADD PIL_PIC_HRS NUMBER; 32. Create a trigger named trg_char_hours that will automatically update the AIRCRAFT table when a new CHARTER row is added. Use the CHARTER table’s CHAR_HOURS_FLOWN to update the AIRCRAFT table’s AC_TTAF, AC_TTEL, and AC_TTER values. CREATE OR REPLACE TRIGGER TRG_CHAR_HOURS AFTER INSERT ON CHARTER FOR EACH ROW BEGIN UPDATE AIRCRAFT SET AC_TTAF = AC_TTAF + :NEW.CHAR_HOURS_FLOWN, AC_TTEL = AC_TTEL + :NEW.CHAR_HOURS_FLOWN, AC_TTER = AC_TTER + :NEW.CHAR_HOURS_FLOWN WHERE AIRCRAFT.AC_NUMBER = :NEW.AC_NUMBER; END; 295 Chapter 8 Advanced SQL 33. Create a trigger named trg_pic_hours that will automatically update the PILOT table when a new CREW row is added and the CREW table uses a ‘pilot’ CREW_JOB entry. Use the CHARTER table’s CHAR_HOURS_FLOWN to update the PILOT table’s PIL_PIC_HRS only when the CREW table uses a ‘pilot’ CREW_JOB entry. CREATE OR REPLACE TRIGGER TRG_PIC_HOURS AFTER INSERT ON CREW FOR EACH ROW BEGIN IF :NEW.CREW_JOB = 'Pilot' THEN UPDATE PILOT SET PIL_PIC_HRS = PIL_PIC_HRS + (SELECT CHAR_HOURS_FLOWN FROM CHARTER WHERE CHAR_TRIP = :NEW.CHAR_TRIP) WHERE EMP_NUM = :NEW.EMP_NUM; END IF; END; 34. Create a trigger named trg_cust_balance that will automatically update the CUSTOMER table’s CUST_BALANCE when a new CHARTER row is added. Use the CHARTER table’s CHAR_TOT_CHG as the update source (Assume that all charter charges are charged to the customer balance.) CREATE OR REPLACE TRIGGER TRG_CUST_BALANCE AFTER INSERT ON CHARTER FOR EACH ROW BEGIN UPDATE CUSTOMER SET CUS_BALANCE = CUS_BALANCE + :NEW.CHAR_TOT_CHG WHERE CUSTOMER.CUS_CODE = :NEW.CUS_CODE; END; 296