DB2 9.5 SQL Procedure Developer exam 735 prep, Part 5: Advanced SQL Features

In this tutorial, learn about IBM® DB2® temporary tables, ADMIN_CMD procedure, savepoints and other advanced SQL features. This is the fifth in a series of six tutorials you can use to help prepare for the DB2 9.5 SQL Procedure Developer exam 735.

Share:

Marina Greenstein (greenstm@us.ibm.com), Executive IT Specialist, IBM

Marina Greenstein photoMarina Greenstein is an Executive IT Software Specialist with the IBM Database Migration Team. She is an IBM Certified Solutions Expert who joined IBM in 1995 with experience in database application architecture and development. During the 13 years Marina has been with the DB2 Migration Team, she has assisted customers in their migrations from Microsoft SQL Server™, Sybase, or Oracle databases to DB2. She has presented migration methodology at numerous DB2 technical conferences and at SHARE. She is also the author of multiple articles, white papers and IBM Redbooks about DB2 migration.



James Merry (jmerry@us.ibm.com), Enablement Consultant, IBM

James MerryJames Merry is an enablement consultant working with IBM Business Partners to help them enable their applications to DB2. He most recently was a member of the IBM Content Management eMail Compliance team. He has been working in the relational database industry for over 20 years developing database applications and database administration.



23 October 2008

Before you start

About this series

Thinking about seeking certification on DB2 SQL Procedure Developer (Exam 735)? If so, you've landed in the right spot. This series of six DB2 certification preparation tutorials covers all the basics -- the topics you'll need to understand before you read the first exam question. Even if you are not planning to seek certification right away, this set of tutorials is a great place to start learning all about database development for DB2 V9.5.

About this tutorial

This tutorial introduces you to advanced SQL features you need to understand to fully utilize DB2 9.5. Get an introduction to

This is the fifth in a series of six tutorials you can use to help prepare for the DB2 9.5 SQL Procedure Developer exam 735. The material in this tutorial primarily covers the objectives in Section 5 of the test, which is entitled "Advanced SQL Features." See Resources.

Objectives

  • Learn how you can use global temporary tables
  • Understand how to use hierarchical queries and the MERGE statement
  • See how you can use ADMIN_CMD routine to perform administration task within stored procedures
  • Discover how the GET_DIAGNOSTICS statement can help you with error handling
  • Understand SAVEPOINTS and the use of XML technology in stored procedures

Prerequisites

This tutorial is written for Linux®, UNIX®, or Windows® database developers or administrators, whose skills and experience are at a beginning to intermediate level. You should have a general familiarity with using a UNIX or Windows command-line shell and a working knowledge of DB2 and SQL commands.

System requirements

The examples in this tutorial are specific to DB2 9.5 running on a Windows operating system. The concepts and information provided are relevant to DB2 running on any distributed platform.

You do not need a copy of DB2 9.5 to complete this tutorial. However, you will get more out of the tutorial if you download the free trial version of IBM DB2 9.5 to work along with this tutorial.


Global declared temporary tables

Temporary tables are most often used to store temporary and intermediate results. They can increase performance because they do not require logging and do not appear in the system catalog. Also, they do not require locking because the temporary table only allows single connection access.

A declared global temporary table (DGTT) is accessible only by the connection in which it was declared (created). It is dropped when this connection to the database is ended.

To create DGTT, you need to issue the DECLARE GLOBAL TEMPORARY TABLE statement. The following is the syntax diagram for this statement:

Listing 1. Syntax for global temporary table declaration

Click to see code listing

Listing 1. Syntax for global temporary table declaration

        .-,---------------------.                                            V                       |                                    
>--+-(----| column-definition |-+--)-----------------------------+-->
   +-LIKE--+-table-name1-+--+------------------+-----------------+      |       '-view-name---'  '-| copy-options |-'                 |      '-AS--(--fullselect--)--DEFINITION ONLY--+------------------+-'                                               '-| copy-options |-'           .-ON COMMIT DELETE ROWS---.      
>--?--+-------------------------+--?---------------------------->
      '-ON COMMIT PRESERVE ROWS-'      

>--+-------------------------------------------+---------------->
   |             .-ON ROLLBACK DELETE ROWS---. |      '-NOT LOGGED--+---------------------------+-'                    '-ON ROLLBACK PRESERVE ROWS-'     

>--?--+--------------+--?--+---------------------+-------------->
      '-WITH REPLACE-'     '-IN--tablespace-name-'   

>--?--+------------------------------------------------------------+--?-><
      |                      .-,-----------.                       |            |                      V             |     .-USING HASHING-. |            '-PARTITIONING KEY--(----column-name-+--)--+---------------+-'      

column-definition

|--column-name--| data-type |--+--------------------+-----------|
                               '-| column-options |-'   

Please note that when the WITH REPLACE clause is specified, an existing DGTT with the same name is dropped and replaced with a new table definition.

Each session that defines a declared global temporary table of the same name has its own unique description of the temporary table. When the session terminates, the rows of the table are deleted, and the description of the temporary table is dropped.

Following is an explanation of some of these options:

  • ON COMMIT DELETE ROWS: When a COMMIT operation is performed, all rows of the table are deleted if no WITH HOLD cursor is open on the table. This is the default.
  • ON COMMIT PRESERVE ROWS : When a COMMIT operation is performed, all rows of the table are preserved.
  • ON ROLLBACK DELETE ROWS : When a ROLLBACK (or ROLLBACK to SAVEPOINT) operation is performed if the table data has been changed, all rows of this table are deleted. This is default.
  • ON ROLLBACK PRESERVE ROWS : When a ROLLBACK (or ROLLBACK to SAVEPOINT) operation is performed, all rows of the table are preserved.

Note that BLOB, CLOB, DBCLOB, LONG VARCHAR, LONG VARGRAPHIC, XML, reference, and structured types cannot be used as data type for the columns of declared global temporary tables.

To use DB2 GDTT you need to perform the following steps:

  • Step 1. Ensure that a user temporary table space exists. If a user temporary table space does not exist, issue a CREATE USER TEMPORARY TABLESPACE statement using the following syntax:
    CREATE USER TEMPORARY TABLESPACE usr_tbsp MANAGED BY SYSTEM USING 
                     ('c:\temp\usertempspace') ;
  • Step 2. Issue a DECLARE GLOBAL TEMPORARY TABLE statement in your application using syntax provided above. Here is an example:
    Listing 2. Example of DGTT declaration
    DECLARE GLOBAL TEMPORARY TABLE temp_proj
             (projno CHAR(6), projname VARCHAR(24), projsdate DATE, projedate DATE,)
        WITH REPLACE
        ON COMMIT PRESERVE ROWS	
        NOT LOGGED	
        IN usr_tbsp ;

    The schema for a declared temporary tables is always SESSION.
  • Step 3. When you reference this table in your procedure, you need to qualify the temporary table with schema SESSION. The following example demonstrates the use of temporary table:
    Listing 3. Use of a temporary table
    CREATE PROCEDURE DB2ADMIN.temp_table (  )
    P1: BEGIN
       DECLARE GLOBAL TEMPORARY TABLE temp1  AS
             ( SELECT deptnumb as dnum, 
                      deptname as name,
                       manager as mgr
                 FROM  org ) 
       DEFINITION ONLY ON COMMIT PRESERVE ROWS;
       BEGIN
         DECLARE c1 CURSOR WITH RETURN FOR
         SELECT dnum, mgr  FROM SESSION.temp1;
    
         INSERT INTO SESSION.temp1 (dnum, name, mgr)
              (SELECT deptnumb, deptname, manager 
                FROM org);
         OPEN c1;
        END; 
    END

Hierarchical queries

Expressing hierarchical relationships in a relational database

When working with hierarchical data in a relational database, it becomes a challenge to retrieve and display the data. A recursive SQL statement provides a means of working with these complex logical structures.

A recursive SQL statement is one where an SQL statement repeatedly used the resulting set to determine further results. This kind of SQL statement is built using a common table expression that makes references to itself (that is, it used its own definition). These type of queries “with (…) as tabname “ are also known as common table expressions (CTE).

An example of this type of data structure would be a bill of materials. The below table represents a hierarchical structure of a list of materials for building a computer. In this example, the computer has sub-assemblies that are made up of a hard drive, monitor, keyboard, mouse, and motherboard. The sub-assemblies themselves can be decomposed into sub-assembles or components, that is, a motherboard is made up of processors and RAM.

Table 1. Sample date from the bill_of_materials table
ASSEMBLY_IDSUB_ASSEMBLY_IDASSEMBLY_NM
1000Computer
10001100Hard Drive
10001200Monitor
10001300Keyboard
10001400Mouse
11001110Hard drive Cables
13001310Keyboard Cables
14001410Mouse
10001500Motherboard
15001510Processors
15001550RAM

The following statement performs a recursive query. The WITH statement defines a temporary table called ASSEMBLY. The upper part of the UNION ALL is only invoked once. It populates the assembly table with five rows that have the assembly ID of 1000.

The lower part of the UNION ALL executes recursively until there are no more matches to the join. That is, the recursive query iterates through the bill_of_materials table row by row creating a final result set and also input to the next iteration of the recursive query.

The SELECT statement at the end simply returns rows from temporary ASSEMBLY table that was just created using CTE.

Listing 4. WITH statement
WITH assembly
   (sub_assembly_id, assembly_nm, assembly_id) AS
(SELECT sub_assembly_id, assembly_nm, assembly_id
FROM bill_of_materials
WHERE assembly_id=1000
UNION ALL
SELECT child.sub_assembly_id,
       child.assembly_nm,
       child.assembly_id
FROM  bill_of_materials child, assembly p
WHERE child.assembly_id = p.sub_assembly_id)
SELECT assembly_id, sub_assembly_id, assembly_nm from assembly;

The final result set returned from the WITH statement:

Listing 5. Final result sets returned from WITH statement
ASSEMBLY_ID SUB_ASSEMBLY_ID ASSEMBLY_NM
     1000          1100            Hard Drive
     1000          1200            Monitor
     1000          1300            Keyboard
     1000          1400            Mouse
     1000          1500            Motherboard
     1100          1110            Hard drive Cables
     1300          1310            Keyboard Cables
     1400          1410            Mouse Cables
     1500          1510            Processors
     1500          1550            RAM

  10 record(s) selected.

Figure 1 gives you a graphical view of how these results would appear.

Figure 1. Example of the hierarchical query
Example of the hierarchical query

Merge statement

Using the MERGE statement to combine conditional update, insert, or delete operations

The MERGE statement updates a target table or updatable view using data from a source table. During a single operation, rows in the target that match the source can be updated or deleted, and rows that do not exist in the target can be inserted.

For example, consider the EMPLOYEE table to be the target table that contains up-to-date information about the employees of a large company. Branch offices handle updates to local employee records by maintaining their own version of the EMPLOYEE table called MY_EMP. The MERGE statement can be used to update the EMPLOYEE table with information that is contained in a MY_EMP table, which is the source table for the merge operation.

The following statement inserts a row for new employee number 000015 into the MY_EMP table.

Listing 6. Inserting a row for a new employee
INSERT INTO my_emp (empno, firstnme, midinit, lastname, workdept,
phoneno, hiredate, job, edlevel, sex, birthdate, salary)
VALUES ('000015', 'MARIO', 'M', 'MALFA', 'A00',
'6669', '05/05/2000', 'ANALYST', 15, 'M', '04/02/1973', 59000.00)

And the following statement inserts updated salary data for existing employee number 000010 into the MY_EMP table.

INSERT INTO my_emp (empno, firstnme, midinit, lastname, edlevel, salary)
VALUES ('000010', 'CHRISTINE', 'I', 'HAAS', 18, 66600.00)

At this point, the inserted data exists only in the MY_EMP table because it has not yet been merged with the EMPLOYEE table. Listing 7 shows the MERGE statement that takes the contents of the MY_EMP table and integrates them with the EMPLOYEE table.

Listing 7. MERGE statement
MERGE INTO employee AS e
USING (SELECT
empno, firstnme, midinit, lastname, workdept, phoneno,
hiredate, job, edlevel, sex, birthdate, salary
FROM my_emp) AS m
ON e.empno = m.empno
WHEN MATCHED THEN
UPDATE SET (salary) = (m.salary)
WHEN NOT MATCHED THEN
INSERT (empno, firstnme, midinit, lastname, workdept, phoneno,
hiredate, job, edlevel, sex, birthdate, salary)
VALUES (m.empno, m.firstnme, m.midinit, m.lastname,
m.workdept, m.phoneno, m.hiredate, m.job, m.edlevel,
m.sex, m.birthdate, m.salary)

Correlation names have been assigned to both the source and the target table to avoid ambiguous table references in the search condition. The statement identifies the columns in the MY_EMP table that are to be considered. The statement also specifies the actions that are to be taken when a row in the MY_EMP table is found to have a match in the EMPLOYEE table, or when a row does not have a match.

The following query executed against the EMPLOYEE table now returns a record for employee 000015:

SELECT * FROM employee WHERE empno = '000015'

And the following query returns the record for employee 000010 with an updated value for the SALARY column:

SELECT * FROM employee WHERE empno = '000010'

System procedure ADMIN_CMD

DB2 provides ADMIN_CMD procedure in SYSPROC schema to run administrative commands directly from application or from another procedure by using a CALL statement. This procedure can not be called from a UDF or trigger.

ADMIN_CMD allows one input parameter of type CLOB(2M) that can contain a single administrative command to be executed. The syntax for that statement is: CALL ADMIN_CMD(command_string);

The procedure currently supports numerous DB2 v9.5 administrative commands. Here are the most commonly used administrative commands:

  • DESCRIBE
  • EXPORT
  • FORCE APPLICATION
  • IMPORT
  • LOAD
  • REORG INDEXES/TABLE
  • RUNSTATS
  • UPDATE DATABASE CONFIGURATION

The full list of supported command can be found in the IBM DB2 9.5 Information Center .

The following example demonstrates the use of this procedure:

Listing 8. Sample ADMIN_CMD procedure
CREATE PROCEDURE test_admin_cmd (  )
P1: BEGIN
    DECLARE sql_string VARCHAR(200);
    SET sql_string ='LOAD FROM C:\DB9.5_test\org_exp.txt OF DEL
                     METHOD P (1, 2, 3, 4, 5) INSERT INTO DB2ADMIN.ORG1
                     (DEPTNUMB, DEPTNAME, MANAGER, DIVISION, LOCATION) 
                      COPY NO INDEXING MODE AUTOSELECT';
	CALL SYSPROC.ADMIN_CMD(sql_string);
END P1

GET DIAGNOSTIC statement

SQL PL provides a GET DIAGNOSTICS statement to obtain information about a previously executed SQL statement. For example, if you need to determine how many rows are affected as a result of an INSERT, DELETE or UPDATE statement, the GET DIAGNOSTICS statement with the ROW_COUNT option could be used to provide this information.

Listing 9 is the syntax diagram for GET DIAGNOSTICS statement:

Listing 9. Syntax for GET DIAGNOSTICS statement

Click to see code listing

Listing 9. Syntax for GET DIAGNOSTICS statement

>>-GET DIAGNOSTICS---------------------------------------------->

>--+-SQL-variable-name--=--+-ROW_COUNT---------+-+-------------><
   |                       '-DB2_RETURN_STATUS-' |      '-| condition-information |-------------------'   

condition-information

|--EXCEPTION--1------------------------------------------------->

   .-,------------------------------------------.      V                                            |   
>----SQL-variable-name--=--+-MESSAGE_TEXT-----+-+---------------|
                           '-DB2_TOKEN_STRING-'     

The GET DIAGNOSTICS statement can be use to obtain:

  1. The number of rows processed by a previously executed SQL statement
  2. The status value returned from the procedure associated with a previous CALL statement
  3. The native DB2 error or warning message text returned from the previously executed SQL statement

SQL-variable declaration depends on the information you are trying to obtain. It needs to be declared as INTEGER to store ROW_COUNT or DB2_RETURN_STATUS, but it should be declared as varchar(70) to store and error and warning messages.

Please note that the GET DIAGNOSTICS statement does not change the contents of the SQLSTATE or SQLCODE special variable.

The following examples demonstrate the use of GET DIAGNOSTICS statement with different options:

Listing 10. Get DIAGNOSTICS statement to retrieve ROW_COUNT
CREATE PROCEDURE UPDATE_RCOUNT(sales_corr INT, qtr int, OUT row_updated INT)
P1: BEGIN    
    UPDATE SALESBYQUARTER
    SET sales = sales+sales_corr
    WHERE y < year(current date)
      and q = qtr;
    GET DIAGNOSTICS row_updated = ROW_COUNT; 	
    END P1
Listing 11. GET DIAGNOSTICS statement to retrieve message text
CREATE PROCEDURE mess_text1 (num int, new_status varchar(10),
               OUT p_err_mess varchar(70))               
P1: BEGIN
    DECLARE SQLCODE INTEGER default 0;
    DECLARE SQLSTATE CHAR(5) default '';
    DECLARE v_trunc int default 0;
    
    DECLARE CONTINUE HANDLER FOR SQLEXCEPTION 
       BEGIN
            GET DIAGNOSTICS EXCEPTION 1 p_err_mess = MESSAGE_TEXT;
            SET v_trunc = 2;        
       END;                                                           
   INSERT INTO tab1 VALUES (num, new_status);   
   RETURN v_trunc;
END P1

The DB2_RETURN_STATUS returns the status value of the invoked SQL procedure. If the called procedure is successful, it returns a value of zero or a positive value indicating a failure.

Listing 12. Example of GET DIAGNOSTICS statement to retrieve return status
CREATE PROCEDURE myproc1 ()
   A1:BEGIN
	DECLARE RETVAL INTEGER DEFAULT 0;
	…
CALL MYPROC2;
	GET DIAGNOSTICS RETVAL = DB2_RETURN_STATUS;
	IF RETVAL <> 0 THEN
	   …
	   LEAVE A1;
	ELSE
	   …
	END IF;
   END A1

Savepoints

Savepoints provide a means of implementing subtransactions within a unit of work (transaction). This is accomplished by creating multiple reference points (savepoints) within a transaction that later can be referenced. For example, within an application, you can set multiple savepoints; at any point after the savepoint is created, you can roll back any work up to the savepoint without impacting any work performed prior to the savepoint.

The following syntax shows you how to set up savepoints:

SAVEPOINT  savepoint-name ON ROLLBACK RETAIN CURSORS

Let’s take a look at an example of performing a rollback operation for nested savepoints. First you create a table named T1. You then insert a row into new table T1. After the insertion, you create the first savepoint(SAVEPOINT1). After creating the savepoint, you insert another row into table T1 and create another savepoint(SAVEPOINT2). You insert another row (third row) into table T1. Then you create your last savepoint(SAVEPOINTS3). Finally, insert another row after SAVEPOINT3. You now have four rows in table T1 (‘A’, ‘B’, ‘C’, ‘D’).

You then perform a rollback to savepoint3, which removes row ‘D’ from table T1. The next rollback that you perform (rollback to savepoint1) removes rows ‘C’ and ‘B’. You then commit the transaction (commit work), which causes the savepoints to no longer be available. You now have two rows (‘A’ and ‘E’) in table T1.

Listing 13 shows you the commands you use to implement these actions.

Listing 13. Using savepoints
CREATE PROCEDURE p1()
BEGIN

  CREATE TABLE T1 (C1 CHAR);
	
  INSERT INTO T1 VALUES ('A');

  SAVEPOINT SAVEPOINT1 ON ROLLBACK RETAIN CURSORS;

  INSERT INTO T1 VALUES ('B');

  SAVEPOINT SAVEPOINT2 ON ROLLBACK RETAIN CURSORS;

  INSERT INTO T1 VALUES ('C');

  SAVEPOINT SAVEPOINT3 ON ROLLBACK RETAIN CURSORS;

  INSERT INTO T1 VALUES ('D');

  ROLLBACK TO SAVEPOINT SAVEPOINT3;

  ROLLBACK TO SAVEPOINT SAVEPOINT1;

 COMMIT WORK;
	
  INSERT INTO T1 VALUES ('E');
 	
END@

PureXML and stored procedures

DB2 9.5 is a hybrid database that supports XML data type for column definition as well as different methods and functions for XML data manipulation. SQL procedures support parameters and variables of data type XML. Those variables also can be passed as parameters to XQuery expressions in XMLEXISTS, XMLQUERY and XMLTABLE expressions.

The following stored procedure returns a result set of a relational column and attribute (customer name) from XML column:

Listing 14. Stored procedure example
CREATE PROCEDURE proc_wXML (  )
DYNAMIC RESULT SETS 1
------------------------------------------------------------------------
-- SQL Stored Procedure 
------------------------------------------------------------------------
P1: BEGIN
-- Declare cursor
DECLARE cursor1 CURSOR WITH RETURN FOR
SELECT cid, xmlquery('declare default element namespace  
               "http://posample.org";
           $cu/customerinfo/name/text()' passing d.info as "cu")
     from customer d;
-- Cursor left open for client application
OPEN cursor1;
END P1

The next procedure is accepting XML as input variable along with integer parameter 
and inset row into CUSTOMER table that has INFO column declared as XML:

CREATE PROCEDURE PROC_INS_XML1 ( v_cid int, v_info XML)
DYNAMIC RESULT SETS 1
------------------------------------------------------------------------
-- SQL Stored Procedure 
------------------------------------------------------------------------
P1: BEGIN

INSERT INTO CUSTOMER (CID, INFO)
  VALUES (v_cid, v_info);
END P1

If you run this procedure using IBM Data Studio, you can parse XML directly into input value windows as Figure 2 illustrates.

Figure 2. XML string as input parameter
XML string as input parameter

Conclusion

In this tutorial, you've learned how to declare global temporary tables and use them in stored procedures. You've also learned how you can use savepoints and the GET DIAGNOSTICS statement to improve your database application. In addition, you've learned about the new DB2 hieratical query and MERGE functionality to help you implement complex business solutions. The ability to use XML technology in your database development can greatly simplify your complex business logic. Knowledge of those advanced features will help you to pass 735 Certification Exam.

Resources

Learn

  • Read Part 1 of the series, "SQL Procedure Language," to learn about SQL Procedure Languag, including a variable, condition, and handler declaration, flow of control and iterative statements, as well as an error-handling mechanism.
  • In Part 2 of the series, "DB2 SQL Procedures," learn about DB2 9.5 SQL procedures, including an introduction to stored procedures, the advantages of using stored procedures, and the differences between SQL procedures and external procedures.
  • Part 3 of the series, "DB2 SQL functions," introduces you to user-defined functions and walks you through the basic steps used to construct user-defined functions. This tutorial also introduces you to the structure of SQL functions and covers the ins and outs of SQL function development.
  • Visit the Test 735: DB2 9.5 SQL Procedure Developer page to get comprehensive resources related to the exam.
  • The DB2 Family Fundamentals tutorial series gives you the resources you need to ace the DB2 Family Fundamentals test.
  • Visit the developerWorks resource page for DB2 for Linux, UNIX, and Windows to read articles and tutorials and connect to other resources to expand your DB2 skills.
  • Learn about DB2 Express-C, the no-charge version of DB2 Express Edition for the community.
  • The DB2 9 for z/OS Stored Procedures: Through the CALL and Beyond IBM Redbook contains information about SQL Procedures.
  • DB2 v9.5 Information Center: Learn more about DB2 SQL procedures.

Get products and technologies

  • Download IBM product evaluation versions and get your hands on application development tools and middleware products from DB2®, Lotus®, Rational®, Tivoli®, and WebSphere®.
  • Download a free trial version of DB2 9 for Linux, UNIX, and Windows.
  • Now you can use DB2 for free. Download DB2 Express-C, a no-charge version of DB2 Express Edition for the community that offers the same core data features as DB2 Express Edition and provides a solid base to build and deploy applications.

Discuss

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into Information management on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Information Management
ArticleID=347572
ArticleTitle=DB2 9.5 SQL Procedure Developer exam 735 prep, Part 5: Advanced SQL Features
publish-date=10232008