SQL inner and outer join explained

Government | Commercial

I’m asked once a while what is an inner join and outer join?
While this has become something I do in my sleep the best way to explain this is by example so here we go:

Assuming you’re joining on columns with no duplicates, which is by far the most common case:

Let’s create two small test tables a and b as follow:

Small Test table a
fred=> timing on
Timing is on.
fred=> create table smtesta (a int, description character(11));
CREATE TABLE
Time: 63.253 ms
fred=>
Small Test table b
fred=> create table smtestb (b int, description character(11));
CREATE TABLE
Time: 42.475 ms
fred=> 

Let’s quickly give table b to Fred as well

fred=# SELECT SESSION_USER, CURRENT_USER;
 session_user | current_user 
--------------+--------------
 postgres     | postgres
(1 row)

fred=# alter table smtestb owner to fred;
ALTER TABLE
fred=#

Now let’s insert some values on these.
For smtesta:

fred=> insert into smtesta (a, description)
       select x.a, 'Insert # ' || x.a
       from generate_series (1,5) as x(a);
INSERT 0 5
Time: 40.997 ms
fred=>
:Skip few inserts
fred=> insert into smtesta (a, description)
       select x.a, 'Insert # ' || x.a 
       from generate_series (11,25) as x(a);
INSERT 0 15
Time: 48.092 ms

For smtestb:

fred=> insert into smtestb (b, description)
       select x.b, 'Insert # ' || x.b 
       from generate_series (1,9) as x(b);
INSERT 0 9
Time: 44.455 ms
fred=> 

Skip few inserts:
fred=> insert into smtestb (b, description)
       select x.b, 'Insert # ' || x.b 
       from generate_series (21,34) as x(b);
INSERT 0 14
Time: 40.779 ms
fred=> 

Now lets list smtesta and smtestb:

fred=> select * from smtesta;
 a  |        description        
----+---------------------------
  1 | Insert # 1               
  2 | Insert # 2               
  3 | Insert # 3               
  4 | Insert # 4               
  5 | Insert # 5               
....           
 20 | Insert # 20              
 21 | Insert # 21              
 22 | Insert # 22              
 23 | Insert # 23              
 24 | Insert # 24              
 25 | Insert # 25              
(20 rows)

Time: 4.249 ms
fred=> select * from smtestb;
 b  |        description        
----+---------------------------
  1 | Insert # 1               
  2 | Insert # 2               
  3 | Insert # 3               
  4 | Insert # 4               
  5 | Insert # 5               
 ....            
 31 | Insert # 31              
 32 | Insert # 32              
 33 | Insert # 33              
 34 | Insert # 34              
(23 rows)
Time: 4.464 ms
fred=> 

Now we’re ready to test our inner join First:

Note the unique and common values in both smtesta and smtestb tables

Inner join
An inner join using either the “where” equivalent queries would return set with the intersection(common values) of the two tables.

Time: 4.464 ms
fred=> select * from smtesta sa INNER JOIN smtestb sb on sa.a = sb.b;
 a  |        description        | b  |        description        
----+---------------------------+----+---------------------------
  1 | Insert # 1                |  1 | Insert # 1               
  2 | Insert # 2                |  2 | Insert # 2               
  3 | Insert # 3                |  3 | Insert # 3               
  4 | Insert # 4                |  4 | Insert # 4               
  5 | Insert # 5                |  5 | Insert # 5               
 21 | Insert # 21               | 21 | Insert # 21              
 22 | Insert # 22               | 22 | Insert # 22              
 23 | Insert # 23               | 23 | Insert # 23              
 24 | Insert # 24               | 24 | Insert # 24              
 25 | Insert # 25               | 25 | Insert # 25              
(10 rows)

Time: 11.734 ms

Equivalent using "where" instead

fred=> select sa.*, sb.* 
fred-> from smtesta sa, smtestb sb
fred-> where sa.a = sb.b;
 a  |        description        | b  |        description        
----+---------------------------+----+---------------------------
  1 | Insert # 1                |  1 | Insert # 1               
  2 | Insert # 2                |  2 | Insert # 2               
  3 | Insert # 3                |  3 | Insert # 3               
  4 | Insert # 4                |  4 | Insert # 4               
  5 | Insert # 5                |  5 | Insert # 5               
 21 | Insert # 21               | 21 | Insert # 21              
 22 | Insert # 22               | 22 | Insert # 22              
 23 | Insert # 23               | 23 | Insert # 23              
 24 | Insert # 24               | 24 | Insert # 24              
 25 | Insert # 25               | 25 | Insert # 25              
(10 rows)

Time: 2.784 ms
fred=> 

Left outer join
A left outer join will return all rows in smtesta, plus any common rows in smtestb.

fred=> select * from smtesta sa LEFT OUTER JOIN smtestb sb on sa.a = sb.b;
 a  |        description        | b  |        description        
----+---------------------------+----+---------------------------
  1 | Insert # 1                |  1 | Insert # 1               
  2 | Insert # 2                |  2 | Insert # 2               
  3 | Insert # 3                |  3 | Insert # 3               
  4 | Insert # 4                |  4 | Insert # 4               
  5 | Insert # 5                |  5 | Insert # 5               
 11 | Insert # 11               |    | 
 12 | Insert # 12               |    | 
 13 | Insert # 13               |    | 
 14 | Insert # 14               |    | 
 15 | Insert # 15               |    | 
 16 | Insert # 16               |    | 
 17 | Insert # 17               |    | 
 18 | Insert # 18               |    | 
 19 | Insert # 19               |    | 
 20 | Insert # 20               |    | 
 21 | Insert # 21               | 21 | Insert # 21              
 22 | Insert # 22               | 22 | Insert # 22              
 23 | Insert # 23               | 23 | Insert # 23              
 24 | Insert # 24               | 24 | Insert # 24              
 25 | Insert # 25               | 25 | Insert # 25              
(20 rows)

Time: 4.438 ms
fred=> 

Right outer join:
A right outer join will return all rows in smtestb, plus any common rows in smtesta.

fred=> select * from smtesta sa RIGHT OUTER JOIN smtestb sb on sa.a = sb.b;
 a  |        description        | b  |        description        
----+---------------------------+----+---------------------------
  1 | Insert # 1                |  1 | Insert # 1               
  2 | Insert # 2                |  2 | Insert # 2               
  3 | Insert # 3                |  3 | Insert # 3               
  4 | Insert # 4                |  4 | Insert # 4               
  5 | Insert # 5                |  5 | Insert # 5               
    |                           |  6 | Insert # 6               
    |                           |  7 | Insert # 7               
    |                           |  8 | Insert # 8               
    |                           |  9 | Insert # 9               
 21 | Insert # 21               | 21 | Insert # 21              
 22 | Insert # 22               | 22 | Insert # 22              
 23 | Insert # 23               | 23 | Insert # 23              
 24 | Insert # 24               | 24 | Insert # 24              
 25 | Insert # 25               | 25 | Insert # 25              
    |                           | 26 | Insert # 26              
    |                           | 27 | Insert # 27              
    |                           | 28 | Insert # 28              
    |                           | 29 | Insert # 29              
    |                           | 30 | Insert # 30              
    |                           | 31 | Insert # 31              
    |                           | 32 | Insert # 32              
    |                           | 33 | Insert # 33              
    |                           | 34 | Insert # 34              
(23 rows)

Time: 2.258 ms
fred=> 

Full outer join

A full outer join will give you the union of A and B, i.e. All the rows in A and all the rows in B. If something in A doesn’t have a corresponding value in B, then the B portion is null, and vice verse.

fred=> select * from smtesta sa FULL OUTER JOIN smtestb sb on sa.a = sb.b;
 a  |        description        | b  |        description        
----+---------------------------+----+---------------------------
  1 | Insert # 1                |  1 | Insert # 1               
  2 | Insert # 2                |  2 | Insert # 2               
  3 | Insert # 3                |  3 | Insert # 3               
  4 | Insert # 4                |  4 | Insert # 4               
  5 | Insert # 5                |  5 | Insert # 5               
 11 | Insert # 11               |    | 
 12 | Insert # 12               |    | 
 13 | Insert # 13               |    | 
 14 | Insert # 14               |    | 
 15 | Insert # 15               |    | 
 16 | Insert # 16               |    | 
 17 | Insert # 17               |    | 
 18 | Insert # 18               |    | 
 19 | Insert # 19               |    | 
 20 | Insert # 20               |    | 
 21 | Insert # 21               | 21 | Insert # 21              
 22 | Insert # 22               | 22 | Insert # 22              
 23 | Insert # 23               | 23 | Insert # 23              
 24 | Insert # 24               | 24 | Insert # 24              
 25 | Insert # 25               | 25 | Insert # 25              
    |                           | 26 | Insert # 26              
    |                           | 27 | Insert # 27              
    |                           | 33 | Insert # 33              
    |                           | 31 | Insert # 31              
    |                           | 34 | Insert # 34              
    |                           | 32 | Insert # 32              
    |                           |  8 | Insert # 8               
    |                           | 28 | Insert # 28              
    |                           | 30 | Insert # 30              
    |                           |  6 | Insert # 6               
    |                           | 29 | Insert # 29              
    |                           |  9 | Insert # 9               
    |                           |  7 | Insert # 7               
(33 rows)

Time: 2.322 ms
fred=>

Ok, mates that’s it for now until next PostgreSQL or Oracle or so tutorial. Or you could check other cool PostgreSQL Tutorials: Sharing Knowledge Tutorials

Cheers
Alf