----------------------------------------------------------------------------- -- (c) Copyright IBM Corp. 2008 All rights reserved. -- -- The following sample of source code ("Sample") is owned by International -- Business Machines Corporation or one of its subsidiaries ("IBM") and is -- copyrighted and licensed, not sold. You may use, copy, modify, and -- distribute the Sample in any form without payment to IBM, for the purpose of -- assisting you in the development of your applications. -- -- The Sample code is provided to you on an "AS IS" basis, without warranty of -- any kind. IBM HEREBY EXPRESSLY DISCLAIMS ALL WARRANTIES, EITHER EXPRESS OR -- IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -- MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. Some jurisdictions do -- not allow for the exclusion or limitation of implied warranties, so the above -- limitations or exclusions may not apply to you. IBM shall not be liable for -- any damages you suffer as a result of using, copying, modifying or -- distributing the Sample, even if IBM has been advised of the possibility of -- such damages. ----------------------------------------------------------------------------- -- -- SAMPLE FILE NAME: xmludfs.db2 -- -- PURPOSE: The purpose of this sample is to show extended support of XML for -- sourced UDF and SQL bodied UDF. -- -- USAGE SCENARIO: The scenario is for a Book Store that has two types of -- customers, retail customers and corporate customers. Corporate -- customers do bulk purchases of books for their company libraries. -- The Book Store also maintains list of �registered customers� -- who are frequent buyers from the store and have registered -- themselves with the store. The store has a DBA, sales clerk and a -- manager for maintaining the database and to run queries on different -- tables to view the book sales. -- -- The store manager frequently queries various tables to get -- information such as contact numbers of different departments, -- location details, location manager details, employee details -- in order to perform various business functions like promoting -- employees, analysing sales, giving awards and bonus to employees -- based on their sales. -- -- The manager is frustrated writing the same queries every time to -- get the information and observes performance degradation as well. -- So he decides to create a user-defined function and a stored -- procedure for each of his requirements. -- -- PREREQUISITES: None -- -- EXECUTION: db2 -td@ -vf xmludfs.db2 -- -- INPUTS: NONE -- -- OUTPUTS: Successfully execution of all UDFs and stored procedures. -- -- -- -- SQL STATEMENTS USED: -- CREATE TABLE -- INSERT -- UPDATE -- DELETE -- DROP -- -- SQL/XML FUNCTIONS USED: -- XMLPARSE -- XMLTABLE -- XMLQUERY -- XMLEXISTS -- ----------------------------------------------------------------------------- -- For more information about the command line processor (CLP) scripts, -- see the README file. -- -- For information on using SQL statements, see the SQL Reference. -- ----------------------------------------------------------------------------- -- SAMPLE DESCRIPTION -- ----------------------------------------------------------------------------- -- 1. UDF Scalar function which takes an XML variable as input parameter -- and returns XML value as output. -- -- 2. UDF Table function which takes an XML variable as input parameter -- and returns a table with XML values as output. -- -- 3. Sourced UDF which takes an XML variable as the input parameter -- and returns XML value as output. -- -- 4. SQL bodied UDF which takes an XML variable as the input parameter -- and returns a table with XML values as output. This UDF -- internally calls a stored procedure which takes an XML variable -- as the input parameter and returns an XML value as output. -- ----------------------------------------------------------------------------- -- SETUP ----------------------------------------------------------------------------- -- Connect to sample database CONNECT TO sample@ ----------------------------------------------------------------------------- -- Setting up tables for the sample ----------------------------------------------------------------------------- -- Create table 'sales_department' CREATE TABLE sales_department(dept_id CHAR(10), dept_info XML)@ -- Create table 'sales_employee' CREATE TABLE sales_employee (emp_id INTEGER, total_sales INTEGER, emp_details XML)@ -- Create table 'performance_bonus_employees' CREATE TABLE performance_bonus_employees(bonus_info XML)@ -- Insert values into 'sales_employee' table INSERT INTO sales_employee VALUES (5001, 40000, XMLPARSE(document '<employee id="5001"> <name>Lethar Kessy</name> <address> <street>555 M G Road</street> <city>Bangalore</city> <state>Karnataka</state> <country>India</country> <zipcode>411004</zipcode> </address> <birthdate>27-01-1954 </birthdate> <gender>Male</gender> <phone> <cell>9435344354</cell> </phone> <email>lethar@company.com</email> <dept>DS02</dept> <skill_level>7</skill_level> <sales>40000</sales> <salary currency="INR">25500</salary> <designation>Sr. Manager</designation> <employee_type>regular</employee_type> <manager>Harry</manager> </employee> '))@ INSERT INTO sales_employee VALUES (5002, 50000, XMLPARSE(document '<employee id="5002"> <name>Mathias Jessy</name> <address><street>Indra Nagar Road No. 5</street> <city>Bangalore</city> <state>Karnataka</state> <country>India</country> <zipcode>411004</zipcode> </address> <birthdate>17-01-1974 </birthdate> <gender>Female</gender> <phone> <cell>9438884354</cell> </phone> <email>jessy@company.com</email> <dept>DS02</dept> <skill_level>6</skill_level> <sales>50000</sales> <salary currency="INR">22500</salary> <designation>Manager</designation> <employee_type>regular</employee_type> <manager>Harry</manager> </employee> '))@ INSERT INTO sales_employee VALUES (5003, 40000, XMLPARSE(document '<employee id="5003"> <name>Mohan Kumar</name> <address> <street>Vijay Nagar Road No. 5</street> <city>Bangalore</city> <state>Karnataka</state> <country>India</country> <zipcode>411004</zipcode> </address> <birthdate>21-11-1974 </birthdate> <gender>Male</gender> <phone> <cell>9438881234</cell> </phone> <email>Mohan@company.com</email> <dept>DS02</dept> <skill_level>5</skill_level> <sales>40000</sales> <salary currency="INR">15500</salary> <designation>Associate Manager</designation> <employee_type>regular</employee_type> <manager>Harry</manager> </employee> '))@ -- Insert values into 'sales_department' table INSERT INTO sales_department VALUES ('DS02', XMLPARSE(document '<department id="DS02"> <name>sales</name> <manager id="M2001"> <name>Harry Thomas</name> <phone> <cell>9732432423</cell> </phone> </manager> <address> <street>Bannerghatta</street> <city>Bangalore</city> <state>Karnataka</state> <country>India</country> <zipcode>560012</zipcode> </address> <phone> <office>080-23464879</office> <office>080-56890728</office> <fax>080-45282976</fax> </phone> </department>'))@ ----------------------------------------------------------------------------- -- 1. UDF Scalar function which takes an XML variable as input parameter -- and returns an XML value as output. ---------------------------------------------------------------------------- -- Create a scalar function 'getDeptContactNumbers' which returns a list -- of department phone numbers CREATE FUNCTION getDeptContactNumbers(dept_info_p XML) RETURNS XML LANGUAGE SQL SPECIFIC contactNumbers NO EXTERNAL ACTION BEGIN ATOMIC -- Return a list of department phone numbers RETURN XMLQuery('document {<phone_list>{$dep/department/phone}</phone_list>}' PASSING dept_info_p as "dep"); END@ -- Call scalar UDF 'getDeptContactNumbers' to get contact numbers of -- the department "DS02" SELECT getDeptContactNumbers(sales_department.dept_info) FROM sales_department WHERE dept_id = 'DS02'@ ---------------------------------------------------------------------------- -- 2. UDF Table function which takes an XML variable as input parameter -- and returns a table with XML values as output. ---------------------------------------------------------------------------- -- The store opens new branches in different parts of the city. -- The book store manager wants to promote senior managers and associate -- managers and designate them to manage these new branches. He wants to -- update the skill level and salaries of all the promoted managers in the -- sales_employee table. He asks the DBA to create a table function for -- this requirement. The DBA creates the 'updatePromotedEmployeesInfo' -- table function. This function updates the skill level and salaries of -- the promoted managers in sales_employee table and returns details of -- all the managers who got promoted. CREATE FUNCTION updatePromotedEmployeesInfo(emp_id_p INTEGER) RETURNS TABLE (name VARCHAR(50), emp_id integer, skill_level integer, salary double, address XML) LANGUAGE SQL MODIFIES SQL DATA SPECIFIC func1 BEGIN ATOMIC -- Update the skill_level and salary for the Sr. manager promoted to -- Area sales manager. UPDATE sales_employee SET emp_details = XMLQuery('transform copy $emp_info := $emp modify if ($emp_info/employee[skill_level = 7 and designation = "Sr. Manager"]) then ( do replace value of $emp_info/employee/skill_level with 8, do replace value of $emp_info/employee/salary with $emp_info/employee/salary * 9.5 ) else if ($emp_info/employee[skill_level = 6 and designation = "Manager"]) then ( do replace value of $emp_info/employee/skill_level with 7, do replace value of $emp_info/employee/salary with $emp_info/employee/salary * 7.5 ) else if ($emp_info/employee[skill_level = 5 and designation = "Associate Manager"]) then ( do replace value of $emp_info/employee/skill_level with 6, do replace value of $emp_info/employee/salary with $emp_info/employee/salary * 5.5 ) else () return $emp_info' PASSING emp_details as "emp") WHERE emp_id = emp_id_p; -- To return the updated details of promoted employees, create a -- relational view of employee_details XML document using XMLTABLE -- function. RETURN SELECT X.* FROM sales_employee, XMLTABLE('$e_info/employee' PASSING emp_details as "e_info" COLUMNS name VARCHAR(50) PATH 'name', emp_id integer PATH '@id', skill_level integer path 'skill_level', salary double path 'salary', addr XML path 'address') AS X WHERE sales_employee.emp_id = emp_id_p; END@ -- Call the 'updatePromotedEmployeesInfo' table function to update the details -- of promoted employees in 'sales_employee' table SELECT A.* FROM sales_employee AS E, table(updatePromotedEmployeesInfo(E.emp_id)) AS A@ ---------------------------------------------------------------------------- -- 3. Sourced UDF which takes an XML variable as the input parameter -- and returns an XML value as output. ---------------------------------------------------------------------------- -- The store manager would like to get a particular dept manager name and -- his contact numbers. The DBA then creates a 'getManagerDetails' UDF to get -- a particular department manager name and manager contact details. CREATE FUNCTION getManagerDetails(dept_info_p XML, dept_p VARCHAR(5)) RETURNS XML LANGUAGE SQL SPECIFIC getManagerDetails BEGIN ATOMIC DECLARE tmp XML; -- Return manager name and manager contact details of 'dept_p' -- department RETURN XMLQuery('$info/department[name=$dept_name]/manager' PASSING dept_info_p as "info", dept_p as "dept_name"); END@ -- Create a sourced UDF 'getManagerInfo' based on 'getManagerDetails' -- user defined function CREATE FUNCTION getManagerInfo(XML, CHAR(10)) RETURNS XML SOURCE getManagerDetails(XML, VARCHAR(5))@ -- Call the sourced UDF 'getManagerInfo' to get 'sales' department -- manager details SELECT getManagerInfo(sales_department.dept_info, 'sales') FROM sales_department WHERE dept_id='DS02'@ ------------------------------------------------------------------------- -- 4. SQL bodied UDF which takes an XML variable as the input parameter -- and returns a table with XML values as output. This UDF -- calls a stored procedure which takes an XML variable -- as the input parameter and returns an XML value as output. -------------------------------------------------------------------------- -- Create a function which calculates an employee gift cheque amount and -- adds this value as a new element into the employee information document CREATE PROCEDURE calculateGiftChequeAmount(INOUT emp_info_p XML, IN emp_name_p VARCHAR(20)) LANGUAGE SQL MODIFIES SQL DATA SPECIFIC customer_award BEGIN DECLARE emp_bonus_info_v XML; IF XMLEXISTS('$e_info/employee[name = $emp1]' PASSING emp_info_p as "e_info", emp_name_p as "emp1") THEN -- Calculate employee gift cheque amount and add a new element -- 'customer_gift_cheque' to employee info document SET emp_bonus_info_v = XMLQuery('copy $bonus := $info modify do insert <customer_gift_cheque>{$bonus/employee/salary * 0.50 + 25000} </customer_gift_cheque> into $bonus/employee return $bonus' PASSING emp_info_p as "info"); END IF; -- Set output parameter value 'emp_info_p' with newly calculated -- bonus information SET emp_info_p = emp_bonus_info_v; END@ -- Some employees who got customer appreciation awards and whose -- total sales are greater than expected sales were given gift -- cheques by the store. The DBA creates 'calculatePerformanceBonus' -- function to calculate employee performance bonus along with -- customer gift cheque amount and update the employee information -- in sales_employee table. CREATE FUNCTION calculatePerformanceBonus(sales_info_p XML) RETURNS table(info XML) LANGUAGE SQL SPECIFIC awardedemployees MODIFIES SQL DATA BEGIN ATOMIC DECLARE awarded_emp_info_v XML; DECLARE emp_name VARCHAR(20); DECLARE min_sales_v INTEGER; DECLARE avg_sales_v INTEGER; -- Extract minimum and average sales from input XML document SET min_sales_v = XMLCAST(XMLQuery('$info/sales_per_annum/min_sales' PASSING sales_info_p as "info") AS INTEGER); SET avg_sales_v = XMLCAST(XMLQuery('$info/sales_per_annum/avg_sales' PASSING sales_info_p as "info") AS INTEGER); -- Loop through the employee records and select all the employees -- whose total sales value is between target_sales and min_sales. FOR_LOOP: FOR EACH_ROW AS SELECT XMLCAST(XMLQuery('$info/employee/name' PASSING awarded_emp_info_v as "info") AS VARCHAR(20)) as name, XMLQuery('copy $e_info := $inf modify do insert <performance_bonus>{$e_info/employee/salary * 0.25 + 5000} </performance_bonus> into $e_info/employee return $e_info' PASSING emp_details as "inf") as info FROM sales_employee WHERE total_sales between min_sales_v and avg_sales_v DO -- For the selected employee, calculate performance bonus and add a new -- element 'performance_bonus' to employee info document SET awarded_emp_info_v = EACH_ROW.info; -- Get the employee name SET emp_name = EACH_ROW.name; -- Call the stored procedure 'calculateGiftChequeAmount' to calculate -- gift cheque amount for the above selected employee CALL calculateGiftChequeAmount(awarded_emp_info_v, emp_name); -- Insert records of employees who got performance bonus and -- gift cheques into 'performance_bonus_employees' table INSERT INTO performance_bonus_employees VALUES (EACH_ROW.info); END FOR; -- Return updated employees information RETURN SELECT * FROM performance_bonus_employees; END@ -- Call the table function 'calculatePerformanceBonus' to get the -- information of all the employees who got gift cheques -- and performance bonus. SELECT * FROM table(calculatePerformanceBonus(XMLPARSE(document '<sales_per_annum> <target_sales>80000</target_sales> <avg_sales>70000</avg_sales> <min_sales>35000</min_sales> </sales_per_annum>')))@ ------------------------------------------------------------------------- -- CLEANUP ------------------------------------------------------------------------- DROP FUNCTION getDeptContactNumbers@ DROP FUNCTION updatePromotedEmployeesInfo@ DROP FUNCTION getManagerInfo@ DROP FUNCTION calculatePerformanceBonus@ DROP PROCEDURE calculateGiftChequeAmount@ DROP TABLE sales_employee@ DROP TABLE sales_department@ DROP TABLE performance_bonus_employees@ DROP FUNCTION getManagerDetails@