Subject: Oracle9i ANSI/ISO SQL:1999-Compliant SQL Content Type: TEXT/PLAIN Creation Date: 21-MAR-2001 Last Revision Date: 02-JAN-2003 PURPOSE This article is intended to introduce the new types of SQL statements available in Oracle9i which support ANSI/ISO SQL:1999 standards. SCOPE &APPLICATION This article assumes the reader has an understanding of SQL and the current syntax. RELATED DOCUMENTS Oracle9i SQL Reference Oracle9i ANSI/ISO SQL:1999-compliant SQL ======================================== This article introduces new features for conformance to ANSI/ISO SQL:1999: 1. SQL:1999 joins 2. CASE expressions 3. Explicit defaults 4. WITH clause 1. SQL:1999 Joins ----------------- SQL:1999 join syntax differs from traditional Oracle join syntax in that the join type is specified explicitly in the FROM clause. Oracle9i introduces the following SQL:1999-compliant joins: 1.1 CROSS Join 1.2 NATURAL Join 1.3 OUTER Join 1.3.1 LEFT OUTER Join 1.3.2 RIGHT OUTER Join 1.3.3 FULL OUTER Join 1.1 CROSS Join -------------- A CROSS join is the cross-product of two tables. It is the equivalent of a Cartesian product. Example: SELECT last_name, department_name FROM employees CROSS JOIN departments; This is equivalent to the following: SELECT last_name, department_name FROM employees, departments; 1.2 NATURAL Join --------------- A NATURAL join selects rows from the tables which have equal values in all matched columns(same column names). If the columns having the same names have different datatypes, an error is returned. If SELECT * syntax is used, the common columns appear only once in the result set. Qualifiers such as table names or aliases may not be used for those columns involved in the natural join. Example: For this example, assume tables employees and departments have one common column, department_id. SELECT employee_id, last_name, department_id FROM employees NATURAL JOIN departments; This is equivalent to the following: SELECT employee_id, last_name, departments.department_id FROM employees, departments WHERE employees.department_id = departments.department_id; If several columns have the same names but all of the datatypes do not match, then the NATURAL JOIN can be modified to a join with a USING clause which specifies which columns should be used for the equi-join. The columns in the USING clause should also not have a qualifier (table name or alias) anywhere in the SQL statement. The key words NATURAL and USING are mutually exclusive. Example: Assume tables employees and job_history have two columns in common, employee_id and department_id. The following query joins the two tables together using only the employee_id as a join condition. SELECT employee_id, last_name, job_history.department_id FROM employees JOIN job_history USING (employee_id); This is equivalent to the following: SELECT employees.employee_id, last_name, job_history.department_id FROM employees, job_history WHERE employees.employee_id=job_history.employee_id; To specify arbitrary conditions or specify columns to be used in the join, the ON clause is used. This also separates the join condition from other conditions. It can also be used to specify a join for which the columns have different names. Another use for the ON clause is simply to make the SQL easier to read and understand by explicitly stating the join condition. Examples: The following is equivalent to the example above for the USING clause. SELECT employees.employee_id, last_name, job_history.department_id FROM employees JOIN job_history ON (employees.employee_id=job_history.employee_id); This example adds an additional where clause to the query: SELECT employees.employee_id, last_name, job_history.department_id FROM employees JOIN job_history ON (employees.employee_id=job_history.employee_id) WHERE mgrid=3; The following illustrates a self-join of the employees table to itself based on the employee_id and manager_id columns: SELECT e.last_name employee, m.last_name manager FROM employees e JOIN employees m ON (e.manager_id=m.employee_id); 1.3 OUTER Join -------------- The join of two tables returning only matched rows is considered an INNER JOIN. A join between two tables which returns the result of the INNER join as well as unmatched rows from the LEFT(or RIGHT) table is a LEFT(or RIGHT) OUTER join. INNER and OUTER JOINS were available in prior relaeses. The '(+)' symbol was used on one of the tables to create outer joins. 1.3.1 LEFT OUTER Join --------------------- A join between two tables which returns the result of the INNER join as well as unmatched rows from the LEFT table is a LEFT OUTER join. Example: This query returns rows from the departments table even if no employees are assigned to it. SELECT employee_id, department_name FROM departments d LEFT OUTER JOIN employees e ON (e.department_id= d.department_id); This is equivalent to the following pre-Oracle9i outer join notation: SELECT employee_id, department_name FROM departments d, employees e WHERE e.department_id(+) = d.department_id; 1.3.2 RIGHT OUTER Join ---------------------- A join between two tables which returns the result of the INNER join as well as unmatched rows from the RIGHT table is a RIGHT OUTER join. Example: This query returns rows from the customers table even if this customer has not placed any orders. SELECT cust_last_name, order_id,order_date FROM orders o RIGHT OUTER JOIN customers c ON (o.customer_id = c.customer_id); This is equivalent to the following pre-Oracle9i outer join notation: SELECT cust_last_name, order_id, order_date FROM orders o, customers c WHERE c.customer_id = o.customer_id(+); 1.3.3 FULL OUTER Join --------------------- A join between two tables which returns the result of the INNER join as well as unmatched rows from the LEFT and RIGHT outer joins is a FULL OUTER join. This is a new type of join in Oracle9i which did not exist previously. Example: This query returns locations which have no corresponding country and countries which have no locations assigned. SELECT l.city, c.country_name FROM locations l FULL OUTER JOIN countries c ON (l.country_id = c.country_id); This could have been accomplished in earlier versions using a UNION: SELECT l.city, c.country_name FROM locations l, countries c WHERE l.country_id = c.country_id(+) UNION SELECT l.city, c.country_name FROM locations l, countries c WHERE l.country_id(+) = c.country_id; Restrictions ------------ Use of these new joins is allowed in the definition of materialized views. However, query rewrite is only possible if the query could also be expressed using traditional Oracle syntax. The same restrictions apply when determining whether the materialized view can be fast refreshed. For example, the following materialized view can make use of query rewrite: CREATE MATERIALIZED VIEW mv1 AS SELECT employee_id, department_name FROM departments d LEFT OUTER JOIN employees e ON (e.department_id= d.department_id); But, this query cannot use query rewrite because the use of subqueries was not permitted with outer joins: CREATE MATERIALIZED VIEW mv2 AS SELECT employee_id, department_name FROM departments d LEFT OUTER JOIN employees e ON (e.department_id= d.department_id) WHERE e.department_id >10; The columns that are referenced in the USING or ON clause cannot be a collection or LOB type. 2. CASE statements ------------------ There are 4 types of CASE logic available in SQL:1999: 2.1 Simple CASE 2.2 Searched CASE 2.3 NULLIF 2.4 COALESCE 2.1 Simple CASE --------------- The simple CASE is similar to DECODE. It was available in Oracle 8.1.7. It can be used to search and replace values within a given expression. You can specify a return value for each searched value. No comparison operators can be used in the simple CASE. Example: Assume the employees table has a salgrade column. SELECT employee_id, CASE salgrade WHEN 'A' THEN 'Low' WHEN 'B' THEN 'Medium' WHEN 'C' THEN 'High' END "Salary Grade" FROM employees; It is also possible to have an ELSE clause for a default. The above query is equivalent to the following with DECODE: SELECT employee_id, DECODE(salgrade,'A','Low','B','Medium','C','High') FROM employees; 2.2 Searched CASE ----------------- The searched CASE is similar to IF..THEN..ELSE. Searched CASE was available beginning in version 8.1.6. Each WHEN clause can be different and logical operators can be used to combine multiple conditions. Comparison operators are also allowed. Example: SELECT employee_id, CASE WHEN salary >=75000 THEN 'High' WHEN salary <=30000 THEN 'Low' ELSE 'Medium' END "Salary" FROM employees; 2.3 NULLIF ---------- NULLIF returns NULL if the first argument is equal to the second. Otherwise, the first value of the first argument is returned. Example: SELECT employee_id, NULLIF(commission_pct,.1) "new commission" FROM employees; 2.4 COALESCE ------------ COALESCE returns the first argument (beginning on the left) that is NOT NULL. It is similar to the NVL function, but it can take multiple alternate values. It accepts any number of arguments. Example: SELECT employee_id, COALESCE(to_char(commission_pct),'none') commission FROM employees; 3. Explicit Defaults -------------------- If a column has a default value defined, the explicit default allows the DEFAULT keyword to be used in an INSERT or UPDATE statement. It can also be used in bind variables. The use of the explicit default eliminates any ambiguity of which values will be placed in a column and avoids hard-coding of literals in applications. Benefits of EXPLICIT DEFAULTS: * provides better data integrity * avoids the use of hard-coded litterals in applications * more user-friendly and provide a more flexible interface Examples: Assume the employee table has a default defined for the department_id column. INSERT INTO employees (employee_id, first_name, last_name, department_id) VALUES (1, 'Tom', 'Smith', DEFAULT); UPDATE employees SET department_id = DEFAULT WHERE department_id=20; 4. WITH clause -------------- The WITH clause allows a query block to be assigned a name and used multiple times in a query by referring to this name. This is very useful to reduce the cost of a query block which will need to be evaluated more than once in a query by materializing the query block. Here is an example of a query which benefits from a WITH clause: SELECT department_name, SUM(salary) dept_total FROM employees, departments WHERE employees.department_id=departments.department_id GROUP BY department_name HAVING SUM(salary) >( SELECT sum(salary) * 1/3 FROM employees, departments WHERE employees.department_id=departments.department_id ) ORDER BY dept_total; This query can be rewritten as follows: WITH summary AS ( SELECT department_name, SUM(salary) dept_total FROM employees, departments WHERE employees.department_id=departments.department_id GROUP BY department_name ) SELECT department_name, dept_total FROM summary WHERE dept_total >( SELECT SUM(dept_total) * 1/3 FROM summary ) ORDER BY dept_total DESC; By using the WITH clause, summarizing the department total more than once is avoided. Another example: WITH dept_costs AS ( SELECT department_name, SUM(salary) dept_total FROM employees e, departments d WHERE e.department_id = d.department_id GROUP BY department_name), avg_cost AS ( SELECT SUM(dept_total)/COUNT(*) avg FROM dept_costs) SELECT * FROM dept_costs WHERE dept_total > (SELECT avg FROM avg_cost) ORDER BY department_name; . ---------------------------------------------------------------------------- MSN 8 helps ELIMINATE E-MAIL VIRUSES. [1]Get 2 months FREE*. --- Links --- 1 http://g.msn.com/8HMAEN/2743??PS=