1.6. Joins Between Tables

Thus far, our queries have only accessed one table at a time. Queries can access multiple tables at once, or access the same table in such a way that multiple rows of the table are being processed at the same time. A query that accesses multiple rows of the same or different tables at one time is called a join query. For example, say you wish to list all the employee records together with the name and location of the associated department. To do that, we need to compare the deptno column of each row of the emp table with the deptno column of all rows in the dept table, and select the pairs of rows where these values match. This would be accomplished by the following query:

SELECT *
FROM emp, dept
WHERE emp.deptno = dept.deptno;

empno | ename  |    job    | mgr  |  hiredate  |   sal   |  comm   | deptno | deptno |   dname    |   loc
-------+--------+-----------+------+------------+---------+---------+--------+--------+------------+----------
  7782 | CLARK  | MANAGER   | 7839 | 1981-06-09 | 2450.00 |         |     10 |     10 | ACCOUNTING | NEW YORK
  7839 | KING   | PRESIDENT |      | 1981-11-17 | 5000.00 |         |     10 |     10 | ACCOUNTING | NEW YORK
  7934 | MILLER | CLERK     | 7782 | 1982-01-23 | 1300.00 |         |     10 |     10 | ACCOUNTING | NEW YORK
  7369 | SMITH  | CLERK     | 7902 | 1980-12-17 |  800.00 |         |     20 |     20 | RESEARCH   | DALLAS
  7566 | JONES  | MANAGER   | 7839 | 1981-04-02 | 2975.00 |         |     20 |     20 | RESEARCH   | DALLAS
  7788 | SCOTT  | ANALYST   | 7566 | 1987-04-19 | 3000.00 |         |     20 |     20 | RESEARCH   | DALLAS
  7876 | ADAMS  | CLERK     | 7788 | 1987-05-23 | 1100.00 |         |     20 |     20 | RESEARCH   | DALLAS
  7902 | FORD   | ANALYST   | 7566 | 1981-12-03 | 3000.00 |         |     20 |     20 | RESEARCH   | DALLAS
  7499 | ALLEN  | SALESMAN  | 7698 | 1981-02-20 | 1600.00 |  300.00 |     30 |     30 | SALES      | CHICAGO
  7521 | WARD   | SALESMAN  | 7698 | 1981-02-22 | 1250.00 |  500.00 |     30 |     30 | SALES      | CHICAGO
  7654 | MARTIN | SALESMAN  | 7698 | 1981-09-28 | 1250.00 | 1400.00 |     30 |     30 | SALES      | CHICAGO
  7698 | BLAKE  | MANAGER   | 7839 | 1981-05-01 | 2850.00 |         |     30 |     30 | SALES      | CHICAGO
  7844 | TURNER | SALESMAN  | 7698 | 1981-09-08 | 1500.00 |    0.00 |     30 |     30 | SALES      | CHICAGO
  7900 | JAMES  | CLERK     | 7698 | 1981-12-03 |  950.00 |         |     30 |     30 | SALES      | CHICAGO
(14 rows)

Observe two things about the result set:

Since all the columns had different names, the parser automatically found out which table they belong to, but it is a good style to fully qualify column names in join queries:

SELECT emp.empno, emp.ename, emp.sal, dept.dname, dept.loc
FROM emp, dept
WHERE emp.deptno = dept.deptno;

Join queries of the kind seen thus far can also be written in this alternative form:

SELECT * FROM dept
INNER JOIN emp ON (emp.deptno = dept.deptno);

This syntax is not as commonly used as the one above, but we show it here to help you understand the following topics.

You will notice that in all the above results for joins no employees were returned that belonged to department # 40. Now we will figure out how we can get the department #40 record in. What we want the query to do is to scan the emp table and for each row to find the matching deptno row. If no matching row is found we want some "empty values" to be substituted for the emp table's columns. This kind of query is called an outer join. (The joins we have seen so far are inner joins.) The command looks like this:

SELECT dept.deptno, emp.ename
FROM dept LEFT OUTER JOIN emp
ON dept.deptno = emp.deptno
ORDER BY dept.deptno;

 deptno | ename  |   sal
--------+--------+---------
     10 | CLARK  | 2450.00
     10 | KING   | 5000.00
     10 | MILLER | 1300.00
     20 | SMITH  |  800.00
     20 | JONES  | 2975.00
     20 | SCOTT  | 3000.00
     20 | ADAMS  | 1100.00
     20 | FORD   | 3000.00
     30 | ALLEN  | 1600.00
     30 | WARD   | 1250.00
     30 | MARTIN | 1250.00
     30 | BLAKE  | 2850.00
     30 | TURNER | 1500.00
     30 | JAMES  |  950.00
     40 |        |
(15 rows)

This query is called a left outer join because the table mentioned on the left of the join operator will have each of its rows in the output at least once, whereas the table on the right will only have those rows output that match some row of the left table. When outputting a left-table row for which there is no right-table match, empty (null) values are substituted for the right-table columns.

An alternative syntax of a left outer join is using the outer join operator (+) to all the columns of the emp table in the join condition within the WHERE clause. So for all the rows in the dept table that have no matching rows in the emp table, EnterpriseDB returns null for any select list expressions containing columns of emp. Hence the above example could be rewritten as:

     SELECT dept.deptno, emp.ename
     FROM dept, emp
     WHERE dept.deptno = emp.deptno(+)
     ORDER BY dept.deptno;
    

We can also join a table against itself. This is called a self join. As an example, suppose we wish to find the name of each employee along with the name of that employee's manager. So we need to compare the mgr column of each emp row to the empno column of all other emp rows.

SELECT e1.ename || ' works for ' || e2.ename AS "Employees and their Managers"
FROM emp e1, emp e2
WHERE e1.mgr = e2.empno

Employees and their Managers
------------------------------
 SCOTT works for JONES
 FORD works for JONES
 ALLEN works for BLAKE
 WARD works for BLAKE
 MARTIN works for BLAKE
 TURNER works for BLAKE
 JAMES works for BLAKE
 MILLER works for CLARK
 ADAMS works for SCOTT
 JONES works for KING
 BLAKE works for KING
 CLARK works for KING
 SMITH works for FORD
(13 rows)

Here we have re-labeled the emp table as e1 and e2 to be able to distinguish the left and right side of the join. You can also use these kinds of aliases in other queries to save some typing, e.g.:

SELECT *
FROM emp e, dept d
WHERE e.deptno = d.deptno;

You will encounter this style of abbreviating quite frequently.