Beruflich Dokumente
Kultur Dokumente
This is the fifth part of a series of articles showing the basics of SQL. In this article we
take a look at some of the common joins, both ANSI and non-ANSI, available in SQL.
Setup
Introduction
[INNER] JOIN ... ON
LEFT [OUTER] JOIN
RIGHT [OUTER] JOIN
FULL [OUTER] JOIN
CROSS JOIN
NATURAL JOIN
[INNER] JOIN ... USING
Additional Joins
Setup
You can perform all these queries online for free using SQL Fiddle.
These tables are a variant of the EMP and DEPT tables from the SCOTT schema. You
will see a lot of Oracle examples on the internet using the tables from the SCOTT schema.
You can find the original table definitions in the
"$ORACLE_HOME/rdbms/admin/utlsampl.sql" script.
Introduction
Joins are used to combine data from multiple tables to form a single result set. Oracle
provides two approaches to joining tables, the non-ANSI join syntax and the ANSI join
syntax, which look quite different.
The non-ANSI join syntax has historically been the way you perform joins in Oracle and
it is still very popular today. The tables to be joined are listed in the FROM clause and the
join conditions are defined as predicates in the WHERE clause. Even if you don't like it,
you are going to have to get used to it as there is a lot of code out there that still uses it. If
you are not familiar with the syntax you will struggle to bug fix any existing code and
some of the examples on the internet will look rather mysterious to you.
The ANSI join syntax was introduced in Oracle 9i. It has a number of advantages over
the original syntax.
Despite all these advantages, many Oracle developers still use the non-ANSI join syntax.
Partly this is just because of habit. Partly this is because the Oracle optimizer transforms
most ANSI join syntax into the non-ANSI join syntax equivalent before it is executed.
For a beginner, my personal opinion is you should focus on the ANSI join syntax, but be
aware of the non-ANSI equivalent. In this article I will show the ANSI and non-ANSI
syntax for each example, where relevant.
Some join methods are more popular than others, so initially focus your attention on
those you are most likely to see. The most common joins you are likely to see in code are
the following.
CROSS APPLY
OUTER APPLY
SELECT d.department_name,
e.employee_name
FROM departments d
JOIN employees e ON d.department_id = e.department_id
WHERE d.department_id >= 30
ORDER BY d.department_name;
DEPARTMENT_NAM EMPLOYEE_N
-------------- ----------
SALES ALLEN
SALES BLAKE
SALES JAMES
SALES MARTIN
SALES TURNER
SALES WARD
6 rows selected.
SQL>
SELECT d.department_name,
e.employee_name
FROM departments d, employees e
WHERE d.department_id = e.department_id
AND d.department_id >= 30
ORDER BY d.department_name;
DEPARTMENT_NAM EMPLOYEE_N
-------------- ----------
SALES ALLEN
SALES BLAKE
SALES JAMES
SALES MARTIN
SALES TURNER
SALES WARD
6 rows selected.
SQL>
Using the previous example, but switching to a LEFT OUTER JOIN means we will see the
OPERATIONS department, even though it has no employees.
SELECT d.department_name,
e.employee_name
FROM departments d
LEFT OUTER JOIN employees e ON d.department_id = e.department_id
WHERE d.department_id >= 30
ORDER BY d.department_name, e.employee_name;
DEPARTMENT_NAM EMPLOYEE_N
-------------- ----------
OPERATIONS
SALES ALLEN
SALES BLAKE
SALES JAMES
SALES MARTIN
SALES TURNER
SALES WARD
7 rows selected.
SQL>
Here is the non-ANSI equivalent of the previous statement. Notice the "(+)" is used to
indicate the side of the join condition that may be missing. For a multi-column join
condition, each column must have the "(+)" present. Unlike the ANSI join syntax, the
non-ANSI join syntax is not affected by the order of the tables.
SELECT d.department_name,
e.employee_name
FROM departments d, employees e
WHERE d.department_id = e.department_id (+)
AND d.department_id >= 30
ORDER BY d.department_name, e.employee_name;
DEPARTMENT_NAM EMPLOYEE_N
-------------- ----------
OPERATIONS
SALES ALLEN
SALES BLAKE
SALES JAMES
SALES MARTIN
SALES TURNER
SALES WARD
7 rows selected.
SQL>
Adding filters to columns returned from an outer joined table is a common cause for
confusion. If you test for a specific value, for example "salary >= 2000", but the value for
the SALARY column is NULL because the row is missing, a regular condition in the
WHERE clause will throw the row away, therefore defeating the object of doing an outer
join. Both the ANSI and non-ANSI methods have a way of dealing with this.
Using the ANSI join syntax, filters on columns from the outer joined table are included in
the join itself, rather than being placed in the WHERE clause.
SELECT d.department_name,
e.employee_name
FROM departments d
LEFT OUTER JOIN employees e ON d.department_id = e.department_id AND
e.salary >= 2000
WHERE d.department_id >= 30
ORDER BY d.department_name, e.employee_name;
DEPARTMENT_NAM EMPLOYEE_N
-------------- ----------
OPERATIONS
SALES BLAKE
2 rows selected.
SQL>
Using the non-ANSI join syntax, the "(+)" is used to indicate a column may have a
NULL value as a result of an outer join.
SELECT d.department_name,
e.employee_name
FROM departments d, employees e
WHERE d.department_id = e.department_id (+)
AND e.salary (+) >= 2000
AND d.department_id >= 30
ORDER BY d.department_name, e.employee_name;
DEPARTMENT_NAM EMPLOYEE_N
-------------- ----------
OPERATIONS
SALES BLAKE
2 rows selected.
SQL>
SELECT d.department_name,
e.employee_name
FROM employees e
RIGHT OUTER JOIN departments d ON e.department_id = d.department_id
WHERE d.department_id >= 30
ORDER BY d.department_name, e.employee_name;
DEPARTMENT_NAM EMPLOYEE_N
-------------- ----------
OPERATIONS
SALES ALLEN
SALES BLAKE
SALES JAMES
SALES MARTIN
SALES TURNER
SALES WARD
7 rows selected.
SQL>
Remember, the non-ANSI outer join syntax is not dependent on table order, so there is no
real concept of right or left outer joins, just outer joins.
SELECT d.department_name,
e.employee_name
FROM employees e
FULL OUTER JOIN departments d ON e.department_id = d.department_id
ORDER BY d.department_name, e.employee_name;
DEPARTMENT_NAM EMPLOYEE_N
-------------- ----------
ACCOUNTING CLARK
ACCOUNTING KING
ACCOUNTING MILLEROPERATIONS
RESEARCH ADAMS
RESEARCH FORD
RESEARCH JONES
RESEARCH SCOTT
RESEARCH SMITH
SALES ALLEN
SALES BLAKE
SALES JAMES
SALES MARTIN
SALES TURNER
SALES WARD
JONES
16 rows selected.
SQL>
There is no direct equivalent of a full outer join using the non-ANSI join syntax, but we
can recreate it by combining two outer join queries using a UNION ALL, as shown below.
SELECT d.department_name,
e.employee_name
FROM employees e, departments d
WHERE e.department_id = d.department_id (+)
UNION ALL
SELECT d.department_name,
e.employee_name
FROM departments d, employees e
WHERE d.department_id = e.department_id (+)
AND e.employee_name IS NULL
ORDER BY 1, 2;
DEPARTMENT_NAM EMPLOYEE_N
-------------- ----------
ACCOUNTING CLARK
ACCOUNTING KING
ACCOUNTING MILLEROPERATIONS
RESEARCH ADAMS
RESEARCH FORD
RESEARCH JONES
RESEARCH SCOTT
RESEARCH SMITH
SALES ALLEN
SALES BLAKE
SALES JAMES
SALES MARTIN
SALES TURNER
SALES WARD
JONES
16 rows selected.
SQL>
Interestingly, when you run an ANSI FULL OUTER JOIN, the Oracle optimizer rewrites it
to a non-ANSI join equivalent, so there is no performance improvement associated with it.
It's just easier on the eye.
Let's remove that extra employee so it doesn't affect any other examples.
CROSS JOIN
A CROSS JOIN is the deliberate creation of a Cartesian product. There are no join columns
specified, so every possible combination of rows between the two tables is produced.
SELECT e.employee_name,
d.department_name
FROM employees e
CROSS JOIN departments d
ORDER BY e.employee_name, d.department_name;
EMPLOYEE_N DEPARTMENT_NAM
---------- --------------
ADAMS ACCOUNTING
ADAMS OPERATIONS
ADAMS RESEARCH
ADAMS SALES
WARD ACCOUNTING
WARD OPERATIONS
WARD RESEARCH
WARD SALES
56 rows selected.
SQL>
Here is the non-ANSI equivalent of the previous statement. Notice, there are no join
conditions in the WHERE clause.
SELECT e.employee_name,
d.department_name
FROM employees e, departments d
ORDER BY e.employee_name, d.department_name;
EMPLOYEE_N DEPARTMENT_NAM
---------- --------------
ADAMS ACCOUNTING
ADAMS OPERATIONS
ADAMS RESEARCH
ADAMS SALES
WARD ACCOUNTING
WARD OPERATIONS
WARD RESEARCH
WARD SALES
56 rows selected.
SQL>
NATURAL JOIN
A NATURAL JOIN is a variant on an INNER JOIN. The join columns are determined
implicitly, based on the column names. Any columns that share the same name between
the two tables are assumed to be join columns. Here is an example using the ANSI join
syntax.
SELECT e.employee_name,
d.department_name
FROM employees e
NATURAL JOIN departments d
ORDER BY e.employee_name, d.department_name;
EMPLOYEE_N DEPARTMENT_NAM
---------- --------------
ADAMS RESEARCH
ALLEN SALES
BLAKE SALES
CLARK ACCOUNTING
FORD RESEARCH
JAMES SALES
JONES RESEARCH
KING ACCOUNTING
MARTIN SALES
MILLER ACCOUNTING
SCOTT RESEARCH
SMITH RESEARCH
TURNER SALES
WARD SALES
14 rows selected.
SQL>
Using a NATURAL JOIN is a bad idea. If someone adds a new column to one of the tables
that happens to have the same name as a column in the other table, they may break any
existing natural joins. It is effectively a bug waiting to happen.
You can't apply any aliased filters to columns used in natural joins, as shown in the
following example.
SELECT e.employee_name,
d.department_name
FROM employees e
NATURAL JOIN departments d
WHERE d.department_id = 20
ORDER BY e.employee_name;
WHERE d.department_id = 20
*
ERROR at line 5:
ORA-25155: column used in NATURAL join cannot have qualifier
SQL>
Instead you must remove the alias, which in other circumstances would result in an
ambiguous reference error.
SELECT e.employee_name,
d.department_name
FROM employees e
NATURAL JOIN departments d
WHERE department_id = 20
ORDER BY e.employee_name;
EMPLOYEE_N DEPARTMENT_NAM
---------- --------------
ADAMS RESEARCH
FORD RESEARCH
JONES RESEARCH
SCOTT RESEARCH
SMITH RESEARCH
5 rows selected.
SQL>
SELECT e.employee_name,
d.department_name
FROM employees e
JOIN departments d USING (department_id)
ORDER BY e.employee_name;
EMPLOYEE_N DEPARTMENT_NAM
---------- --------------
ADAMS RESEARCH
ALLEN SALES
BLAKE SALES
CLARK ACCOUNTING
FORD RESEARCH
JAMES SALES
JONES RESEARCH
KING ACCOUNTING
MARTIN SALES
MILLER ACCOUNTING
SCOTT RESEARCH
SMITH RESEARCH
TURNER SALES
WARD SALES
14 rows selected.
SQL>
This is a safe join syntax as it can't be affected by addition of columns to either table.
Similar to the NATURAL JOIN, you can't apply any aliased filters to columns used in the
join, but if you remove the alias it works.
SELECT e.employee_name,
d.department_name
FROM employees e
JOIN departments d USING (department_id)
WHERE d.department_id = 20
ORDER BY e.employee_name;
WHERE d.department_id = 20
*
ERROR at line 5:
ORA-25154: column part of USING clause cannot have qualifier
SQL>
SELECT e.employee_name,
d.department_name
FROM employees e
JOIN departments d USING (department_id)
WHERE department_id = 20
ORDER BY e.employee_name;
EMPLOYEE_N DEPARTMENT_NAM
---------- --------------
ADAMS RESEARCH
FORD RESEARCH
JONES RESEARCH
SCOTT RESEARCH
SMITH RESEARCH
5 rows selected.
SQL>
Additional Joins
The CROSS APPLY and OUTER APPLY joins are available in Oracle, but they have
only been supported for use in your application code from Oracle 12c onward, so you are
unlikely to see them in application code for some time.