Monday, January 28, 2008

Overview of JOINS in Oracle

Full Outer Joins in Oracle

Among several new features, Oracle has introduced the ANSI standard join syntax. The new join syntax is not only SQL92 compliant, but it is elegant and makes the outer join syntax more intuitive. The biggest advantage of the new join syntax is its support for full outer joins, which is not directly feasible in Oracle8i. In this article I discuss the full outer join feature of Oracle9i, and provide some examples.
An outer join extends the result of an inner join by including rows from one table (say Table A) that don't have corresponding rows in another table (say Table B). An important thing to note here is that the outer join operation will not include the rows from Table B that don't have corresponding rows in Table A. In other words, an outer join is unidirectional. But there are situations when you may want a bidirectional outer join, i.e., you want to include all the rows from A and B:
• rows from the result of the inner join
• rows from A that don't have corresponding rows in B
• rows from B that don't have corresponding rows in A
Let's look at an example to understand this further. Consider the following two tables:
SQL> desc part
Name Null? Type
----------------------------------------- -------- -----------------
PART_ID NOT NULL VARCHAR2(4)
SUPPLIER_ID VARCHAR2(4)

SQL> select * from part;
PART SUPP
---- ----
P1 S1
P2 S2
P3
P4

SQL> desc supplier
Name Null? Type
----------------------------------------- -------- -----------------
SUPPLIER_ID NOT NULL VARCHAR2(4)
SUPPLIER_NAME NOT NULL VARCHAR2(20)

SQL> select * from supplier;

SUPP SUPPLIER_NAME
---- --------------------
S1 Supplier#1
S2 Supplier#2
S3 Supplier#3
Notice above that there are two parts (P3 and P4) that don't have a supplier yet. Also, there is a supplier (S3) who doesn't yet supply any part.

Result of Inner Join

Let's say our company is consolidating all of the parts and the suppliers for managing the inventory of its parts properly, and I was asked to generate a report of all the parts and their corresponding suppliers. So I performed a join of these two tables, which gave the following result:
SQL> select p.part_id, s.supplier_name
2 from part p, supplier s
3 where p.supplier_id = s.supplier_id;

PART SUPPLIER_NAME
---- --------------------
P1 Supplier#1
P2 Supplier#2
The join shown above is an inner join, which results in just the rows that have corresponding rows in both tables. Therefore, the parts that don't have a supplier, or the suppliers that don't supply any part are excluded from the result set.

Result of Outer Join

If we want all parts to be listed in the result set, irrespective of whether they are supplied by any supplier or not, then we need to perform an outer join.
SQL> select p.part_id, s.supplier_name
2 from part p, supplier s
3 where p.supplier_id = s.supplier_id (+);

PART SUPPLIER_NAME
---- --------------------
P1 Supplier#1
P2 Supplier#2
P3
P4
The outer join above lists all of the parts. For the parts that don't have a corresponding supplier, null values are displayed for the SUPPLIER_NAME column. However, not all the suppliers are displayed. Since supplier S3 doesn't supply any parts, it gets excluded from the result set of the above outer join. If we want all the suppliers listed in the result set, irrespective of whether they supply any part or not, we need to perform an outer join like the following:
SQL> select p.part_id, s.supplier_name
2 from part p, supplier s
3 where p.supplier_id (+) = s.supplier_id;

PART SUPPLIER_NAME
---- --------------------
P1 Supplier#1
P2 Supplier#2
Supplier#3
The outer join above lists all the suppliers. For the suppliers that don't supply any part, null values are displayed for the PART_ID column. However, not all the parts are displayed. Since parts P3 and P4 are not supplied by any suppliers, they get excluded from the result set of the above outer join.

Full Outer Join

If we want all the parts (irrespective of whether they are supplied by any supplier or not), and all the suppliers (irrespective of whether they supply any part or not) listed in the same result set, we have a problem. That's because the traditional outer join (using the '+' operator) is unidirectional, and you can't put (+) on both sides in the join condition. The following will result in an error:
SQL> select p.part_id, s.supplier_name
2 from part p, supplier s
3 where p.supplier_id (+) = s.supplier_id (+);
where p.supplier_id (+) = s.supplier_id (+)
*
ERROR at line 3:
ORA-01468: a predicate may reference only one outer-joined table
Up through Oracle8i, Oracle programmers have used a workaround to circumvent this limitation. The workaround involves two outer join queries combined by a UNION operator, as in the following example:
SQL> select p.part_id, s.supplier_name
2 from part p, supplier s
3 where p.supplier_id = s.supplier_id (+)
4 union
5 select p.part_id, s.supplier_name
6 from part p, supplier s
7 where p.supplier_id (+) = s.supplier_id;

PART SUPPLIER_NAME
---- --------------------
P1 Supplier#1
P2 Supplier#2
P3 Supplier#3
P4
Notice the use of the UNION set operator to combine the results of two separate outer join queries, to list all the parts and all the suppliers in the same result set.

New Join Syntax

Oracle introduced the ANSI compliant join syntax. This new join syntax uses the new keywords inner join, left outer join, right outer join, and full outer join, instead of the (+) operator.
The inner join using this new join syntax will look like:
SQL> select p.part_id, s.supplier_name
2 from part p inner join supplier s
3 on p.supplier_id = s.supplier_id;

PART SUPPLIER_NAME
---- --------------------
P1 Supplier#1
P2 Supplier#2
Remember, if we want to retain all the parts in the result set, irrespective of whether any supplier supplies them or not, then we need to perform an outer join. The corresponding outer join query using the new syntax will be:
SQL> select p.part_id, s.supplier_name
2 from part p left outer join supplier s
3 on p.supplier_id = s.supplier_id;

PART SUPPLIER_NAME
---- --------------------
P1 Supplier#1
P2 Supplier#2
P4
P3
This is called a "left outer join" because all the rows from the table on the left (PART) are retained in the result set. If we want to retain all the suppliers in the result set, irrespective of whether they supply any part or not, then we need to perform a "right outer join". That would look like:
SQL> select p.part_id, s.supplier_name
2 from part p right outer join supplier s
3 on p.supplier_id = s.supplier_id;

PART SUPPLIER_NAME
---- --------------------
P1 Supplier#1
P2 Supplier#2
Supplier#3
However, the biggest advantage of the new join syntax is its support for full outer joins. Introduction of the ANSI standard join syntax in Oracle9i greatly simplifies the full outer join query. We are no longer limited by unidirectional outer join, and no longer need to use the UNION operation to perform the full outer join. Oracle9i introduced the full outer join operation to carry out such operations, as in the following example:
SQL> select p.part_id, s.supplier_name
2 from part p full outer join supplier s
3 on p.supplier_id = s.supplier_id;

PART SUPPLIER_NAME
---- --------------------
P1 Supplier#1
P2 Supplier#2
P4
P3 Supplier#3
The above SQL statement is not only smaller in size, it is much more elegant and intuitive as well. This ANSI join syntax is also more efficient than the UNION method of achieving a full outer join.

No comments: