7 min read

Joins

1 Introduction

Source: C.L. Moffatt

A topic which must not be missing in the course of SQL are Joins. In this article I will talk about the different types of joins and explain how they work and what differences they have to each other.

2 Preparation

For the following examples, I set up a new database.

SET LANGUAGE ENGLISH


CREATE DATABASE Joins;

USE Joins;

Below I will create 4 example tables that we will continue to work with.

CREATE TABLE Countries_Main
    (Country_ID INT NOT NULL IDENTITY(1,1) PRIMARY KEY,
     Country_Name VARCHAR(100) NOT NULL)
    ;

INSERT INTO Countries_Main (Country_Name) VALUES ('Egypt')
INSERT INTO Countries_Main (Country_Name) VALUES ('Brazil')
INSERT INTO Countries_Main (Country_Name) VALUES ('Germany')
INSERT INTO Countries_Main (Country_Name) VALUES ('Malta')

SELECT * FROM Countries_Main

CREATE TABLE Countries_Capital
    (ID INT NOT NULL IDENTITY(1,1) PRIMARY KEY,
     Country_ID INT NOT NULL FOREIGN KEY REFERENCES Countries_Main(Country_ID),
     Capital_Name VARCHAR(100) NOT NULL)
    ;

INSERT INTO Countries_Capital (Country_ID, Capital_Name) VALUES (1, 'Cairo')
INSERT INTO Countries_Capital (Country_ID, Capital_Name) VALUES (2, 'Brasilia')
INSERT INTO Countries_Capital (Country_ID, Capital_Name) VALUES (3, 'Berlin')
INSERT INTO Countries_Capital (Country_ID, Capital_Name) VALUES (4, 'Valletta')

SELECT * FROM Countries_Capital

CREATE TABLE Countries_Inhabitants
    (ID INT NOT NULL IDENTITY(1,1) PRIMARY KEY,
     Country_ID INT NOT NULL FOREIGN KEY REFERENCES Countries_Main(Country_ID),
     Inhabitants FLOAT NOT NULL)
    ;

INSERT INTO Countries_Inhabitants (Country_ID, Inhabitants) VALUES (1, 93.4)
INSERT INTO Countries_Inhabitants (Country_ID, Inhabitants) VALUES (2, 207.9)
INSERT INTO Countries_Inhabitants (Country_ID, Inhabitants) VALUES (4, 0.44)

SELECT * FROM Countries_Inhabitants

CREATE TABLE Countries_Land_Area
    (ID INT NOT NULL IDENTITY(1,1) PRIMARY KEY,
     Country_ID INT NOT NULL FOREIGN KEY REFERENCES Countries_Main(Country_ID),
     Land_Area FLOAT NOT NULL)
    ;

INSERT INTO Countries_Land_Area (Country_ID, Land_Area) VALUES (1, 1)
INSERT INTO Countries_Land_Area (Country_ID, Land_Area) VALUES (2, 8.5)
INSERT INTO Countries_Land_Area (Country_ID, Land_Area) VALUES (3, 0.36)

SELECT * FROM Countries_Land_Area

Here again briefly an overview of all 4 created tables:

SELECT * FROM Countries_Main
SELECT * FROM Countries_Capital
SELECT * FROM Countries_Inhabitants
SELECT * FROM Countries_Land_Area

3 Types of Joins

Below I will walk through the different types of joins and explain how they work using the sample tables I have created. Furthermore I will always show at the beginning, with which tables exactly I will execute the following join.

3.1 Inner Join

Source: C.L. Moffatt

SELECT * FROM Countries_Inhabitants
SELECT * FROM Countries_Land_Area

This join will return all of the records in the left table (Countries_Inhabitants [t1]) that have a matching record in the right table (Countries_Land_Area [t2]).

SELECT t1.Country_ID, 
       t1.Inhabitants, 
       t2.Land_Area
    FROM Countries_Inhabitants AS t1
    INNER JOIN Countries_Land_Area AS t2
        ON t1.Country_ID = t2.Country_ID
    ;

3.2 Left Join

Source: C.L. Moffatt

SELECT * FROM Countries_Main
SELECT * FROM Countries_Capital

This join will return all of the records in the left table (Countries_Main [t1]) regardless if any of those records have a match in the right table (Countries_Capital [t2]).

SELECT t1.Country_ID,
       t1.Country_Name,
       t2.Capital_Name
    FROM Countries_Main AS t1
    LEFT JOIN Countries_Capital AS t2
        ON t1.Country_ID = t2.Country_ID
    ;

But what would happen if a value in the second (right) table is not available? Have a look here:

SELECT * FROM Countries_Main
SELECT * FROM Countries_Inhabitants

SELECT t1.Country_ID,
       t1.Country_Name,
       t2.Inhabitants
    FROM Countries_Main AS t1
    LEFT JOIN Countries_Inhabitants AS t2
        ON t1.Country_ID = t2.Country_ID
    ;

The missing information is replaced with a NULL value.

3.3 Right Join

Source: C.L. Moffatt

SELECT * FROM Countries_Main
SELECT * FROM Countries_Inhabitants

This join will return all of the records in the right table (Countries_Inhabitants [t2]) regardless if any of those records have a match in the left table (Countries_Main [t1]).

SELECT t1.Country_ID,
       t1.Country_Name,
       t2.Inhabitants
    FROM Countries_Main AS t1
    RIGHT JOIN Countries_Inhabitants AS t2
        ON t1.Country_ID = t2.Country_ID
    ;

You can also generate this output with a Left JOIN by swapping the two table specifications in the FROM statement and LEFT JOIN statement.

SELECT t1.Country_ID,
       t2.Country_Name,
       t1.Inhabitants
    FROM Countries_Inhabitants AS t1
    LEFT JOIN Countries_Main AS t2
        ON t1.Country_ID = t2.Country_ID
    ;

For better readability I have adjusted the order of the returned columns.

Here again for comparison the relevant section from the Left JOIN shown above.

...
    FROM Countries_Main AS t1
    LEFT JOIN Countries_Inhabitants AS t2
...

3.4 Outer Join

Source: C.L. Moffatt

As known as FULL OUTER JOIN or FULL JOIN.

SELECT * FROM Countries_Inhabitants
SELECT * FROM Countries_Land_Area

This join will return all of the records from both tables, joining records from the left table (Countries_Inhabitants [t1]) that match records from the right table (Countries_Land_Area [t2]).

SELECT t1.Country_ID AS Country_ID_t1,
       t1.Inhabitants,
       t2.Country_ID AS Country_ID_t2,
       t2.Land_Area
    FROM Countries_Inhabitants AS t1
    FULL OUTER JOIN Countries_Land_Area AS t2
        ON t1.Country_ID = t2.Country_ID
    ;

Here again the output of the Inner JOIN for comparison:

SELECT t1.Country_ID, 
       t1.Inhabitants, 
       t2.Land_Area
    FROM Countries_Inhabitants AS t1
    INNER JOIN Countries_Land_Area AS t2
        ON t1.Country_ID = t2.Country_ID
    ;

3.5 Left Excluding Join

Source: C.L. Moffatt

SELECT * FROM Countries_Inhabitants
SELECT * FROM Countries_Land_Area

This query will return all of the records in the left table (Countries_Inhabitants [t1]) that do not match any records in the right table (Countries_Land_Area [t2]).

SELECT t1.Country_ID AS Country_ID_t1,
       t1.Inhabitants,
       t2.Country_ID AS Country_ID_t2,
       t2.Land_Area
    FROM Countries_Inhabitants AS t1
    LEFT JOIN Countries_Land_Area AS t2
        ON t1.Country_ID = t2.Country_ID
        WHERE t2.Country_ID IS NULL
    ;

3.6 Right Excluding Join

Source: C.L. Moffatt

SELECT * FROM Countries_Inhabitants
SELECT * FROM Countries_Land_Area

This join will return all of the records in the right table (Countries_Land_Area [t2]) that do not match any records in the left table (Countries_Inhabitants [t1]).

SELECT t1.Country_ID AS Country_ID_t1,
       t1.Inhabitants,
       t2.Country_ID AS Country_ID_t2,
       t2.Land_Area
    FROM Countries_Inhabitants AS t1
    RIGHT JOIN Countries_Land_Area AS t2
        ON t1.Country_ID = t2.Country_ID
        WHERE t1.Country_ID IS NULL
    ;

3.7 Outer Excluding Join

Source: C.L. Moffatt

SELECT * FROM Countries_Inhabitants
SELECT * FROM Countries_Land_Area

This join will return all of the records in the left table (Countries_Inhabitants [t1]) and all of the records in the right table (Countries_Land_Area [t2]) that do not match.

SELECT t1.Country_ID AS Country_ID_t1,
       t1.Inhabitants,
       t2.Country_ID AS Country_ID_t2,
       t2.Land_Area
    FROM Countries_Inhabitants AS t1
    FULL OUTER JOIN Countries_Land_Area AS t2
        ON t1.Country_ID = t2.Country_ID
        WHERE t1.Country_ID IS NULL OR t2.Country_ID IS NULL
    ;

Here again for comparison the result of the Outer JOIN / Full Outer JOIN:

SELECT t1.Country_ID AS Country_ID_t1,
       t1.Inhabitants,
       t2.Country_ID AS Country_ID_t2,
       t2.Land_Area
    FROM Countries_Inhabitants AS t1
    FULL OUTER JOIN Countries_Land_Area AS t2
        ON t1.Country_ID = t2.Country_ID
    ;

4 Multiple Joins

Of course, you also have the possibility to combine several joins with each other.

SELECT * FROM Countries_Main
SELECT * FROM Countries_Capital
SELECT * FROM Countries_Inhabitants
SELECT * FROM Countries_Land_Area

In each of the tables the primary key (‘Country_ID’) from the main table ‘Countries_Main’ is stored as the foreing key. After that we join all tables together:

SELECT t1.Country_ID,
       t1.Country_Name,
       t2.Capital_Name,
       t3.Inhabitants,
       t4.Land_Area
    FROM Countries_Main AS t1
    LEFT JOIN Countries_Capital AS t2
        ON t1.Country_ID = t2.Country_ID
    LEFT JOIN Countries_Inhabitants AS t3
        ON t1.Country_ID = t3.Country_ID
    LEFT JOIN Countries_Land_Area AS t4
        ON t1.Country_ID = t4.Country_ID
    ;

5 Join Tables from 2 different Databases

Furthermore, it is also possible to join different tables from 2 different databases. Let’s take another look at our main table in advance:

Now I create another database called ‘Joins2’ and add another sample table (‘Countries_Currency’) there.

CREATE DATABASE Joins2;

USE Joins2;

Note: A foreign key relationship between two databases is not possible with SQL Server. Therefore I will leave out this definition when defining the columns.

CREATE TABLE Countries_Currency
    (ID INT NOT NULL IDENTITY(1,1) PRIMARY KEY,
     Country_ID INT NOT NULL,
     Currency VARCHAR(100) NOT NULL)
    ;

INSERT INTO Countries_Currency (Country_ID, Currency) VALUES (1, 'Egyptian Pound')
INSERT INTO Countries_Currency (Country_ID, Currency) VALUES (2, 'Brazilian Real')
INSERT INTO Countries_Currency (Country_ID, Currency) VALUES (3, 'Euro')
INSERT INTO Countries_Currency (Country_ID, Currency) VALUES (4, 'Maltese Lira')

SELECT * FROM Countries_Currency

The two tables (‘Countries_Capital’ & ‘Countries_Currency’) from the two databases (‘Joins’ & ‘Joins2’) can be joined together using the following syntax:

SELECT table_1.*,
       table_2.*
    FROM [Database_1].[Table_Schema].[Table_Name_1] table_1
    JOIN [Database_2].[Table_Schema].[Table_Name_2] table_2 
        ON table_1.id = table_2.id
    ;

And here is the syntax applied to our present case:

SELECT t1.Country_ID,
       t1.Country_Name,
       t2.Currency
    FROM [Joins].[dbo].[Countries_Main] AS t1
    LEFT JOIN [Joins2].[dbo].Countries_Currency AS t2
        ON t1.Country_ID = t2.Country_ID
    ;

6 Conclusion

In this post I have shown what different types of joins exist and how to use them.