Skip to content

PostgeSQLđź”—

In this practical session, we cover many examples of database queries with the popular DBMS PostgreSQL.

Based on the TP by Christophe Garion, CC BY-NC-SA 2015.

Setupđź”—

Before class, please install PostgreSQL and pgAdmin.

PostgreSQL installationđź”—

For this session, students should install PostgreSQL (v9 or higher) and pgAdmin (v4). Follow the installation instructions and make sure you have an initial database setup and the postgresql service running.

Additionally, add your login user as a postgresql superuser to enable database creation with your user:

# bash shell in Linux or OSX
$ sudo su -l postgres
[postgres]$ createuser --interactive

pgAdminđź”—

You can do all exercises directly through the psql shell for this class. However, it is useful to have a graphical confirmation of the database configuration. pgAdmin is one of many front-ends for Postgres. Install it by following the instructions on the pgAdmin site.

Setup - database creationđź”—

Once you've installated and configured PostgreSQL, create the first exercise database:

# bash shell in Linux or OSX or windows powershell
$ createdb db-mexico86

you can also do this through an SQL shell:

# SQL shell
postgres=# CREATE DATABASE "db-mexico86";

Confirm with pgAdmin that your database db-mexico86 was created. If you don't have any servers, create one by right-clicking. The host address is 127.0.0.1 and the maintenance database and username should be postgres.

In pgAdmin, if you are asked for a password and don't know what your password is, you can reset the password of the postgres user:

change password
postgres=# ALTER USER postgres WITH PASSWORD "newpassword";

Mexico86 database - simple queriesđź”—

This database contains data from the 1986 football World Cup.

You can download the database creation script individually:

$ wget https://raw.githubusercontent.com/SupaeroDataScience/DE/master/scripts/mexico86/create-tables-std.sql

Or git clone the class repository and navigate to the creation and insertion scripts.

Once you have the scripts, run the database creation script in the mexico folder.

# bash shell in Linux or OSX, or windows powershell
$ psql -d db-mexico86 -f mexico86/create-tables-std.sql

If that doesn't work, you can copy the script into the Query Tool in pgAdmin.

Exercise 1.1: Look at the database creation scripts. What are the tables being created? What are their fields? Which fields are keys? Confirm these values in pgAdmin.

Response Pays: (nom, groupe)
Typematch: (type)
Match: (paysl, paysv, butsl, butsv, type, date)

You should be able to make queries now. You can either use PostgreSQL in interactive mode by running

$ psql -d db-mexico86

or write your solutions in an SQL file and run the file:

$ echo "SELECT groupe FROM pays;" > a.sql
$ psql -d db-mexico86 -f a.sql

You can also use the Query Editor in pgAdmin for a graphical interface.

Exercise 1.2: Write a query which lists the countries participating in the World Cup.

Response
        nom 
---------------------
Argentine
Italie
Bulgarie
Corée
Mexique
Paraguay
Belgique
Irak
URSS
Hongrie
France
Canada
Brésil
Espagne
Irlande du Nord
Algérie
Danemark
RFA
Uruguay
Écosse
Maroc
Angleterre
Pologne
Portugal
(24 rows)

Exercise 1.3: Write a query which lists all matches as a pair of countries per match.

Response
        paysl        |        paysv 
---------------------|---------------------
Bulgarie            | Italie
Argentine           | Corée
Italie              | Argentine
Corée               | Bulgarie
Corée               | Italie
Argentine           | Bulgarie
Belgique            | Mexique
Paraguay            | Irak
Mexique             | Paraguay
Irak                | Belgique
Irak                | Mexique
Paraguay            | Belgique
Canada              | France
URSS                | Hongrie
France              | URSS
Hongrie             | Canada
URSS                | Canada
Hongrie             | France
Espagne             | Brésil
Algérie             | Irlande du Nord
Brésil              | Algérie
Irlande du Nord     | Espagne
Irlande du Nord     | Brésil
Algérie             | Espagne
Uruguay             | RFA
Écosse              | Danemark
Danemark            | Uruguay
RFA                 | Écosse
Écosse              | Uruguay
Danemark            | RFA
Maroc               | Pologne
Portugal            | Angleterre
Angleterre          | Maroc
Pologne             | Portugal
Angleterre          | Pologne
Maroc               | Portugal
Brésil              | Pologne
France              | Italie
Maroc               | RFA
Mexique             | Bulgarie
Argentine           | Uruguay
Angleterre          | Paraguay
URSS                | Belgique
Espagne             | Danemark
Brésil              | France
RFA                 | Mexique
Argentine           | Angleterre
Belgique            | Espagne
France              | RFA
Argentine           | Belgique
RFA                 | Argentine
(51 rows)

Exercise 1.4: Write a query which lists the matches which took place on June 5, 1986.

Response
        paysl        |   paysv
---------------------|-----------
Italie              | Argentine
Corée               | Bulgarie
France              | URSS
(3 rows)

Exercise 1.5: Write a query which lists the countries which France played against (hint, France could have played either side).

Response
pays
---------
Brésil
Canada
Hongrie
Italie
RFA
URSS
(6 rows)

Exercise 1.6: Write a query which returns the winner of the World Cup

Response
pays
-----------
Argentine
(1 row)

Beer databaseđź”—

We'll now use a database which tracks the beers that a group of friends enjoy. Create the database and populate it using the provided scripts.

$ createdb db-beer
$ psql -d db-beer -f beer/create-tables-std.sql
$ psql -d db-beer -f beer/insert.sql

Exercise 2.1: Look at the database creation scripts. What are the tables being created? What are their fields? Which fields are keys? Confirm these values in pgAdmin.

Response Frequente: (buveur, bar)
Sert: (bar, biere)
Aime: (buveur, biere)

Write queries which respond to the following questions. Hint, understanding natural joins may help.

Exercise 2.2 What is the list of bars which serve the beer that Martin likes?

Response
        bar 
-------------------
 Ancienne Belgique
 La Tireuse
 Le Filochard
(3 rows)

Exercise 2.3 What is the list of drinkers who go to at least one bar which servers a beer they like?

Response
 buveur 
--------
 Bob
 David
 Emilie
 Martin
(4 rows)

Exercise 2.3 What is the list of drinkers who don't go to any bars which serve the beer they like?

Response
 buveur 
--------
 Cecile
 Alice
(2 rows)

Complex queries - Mexico databaseđź”—

Exercise 3.1: Create a table with an entry for each match which lists the total number of goals (scored by either side), the match type, and the date. As we'll use this table later on, create a VIEW called "matchbutsglobal" with this information.

Response
        paysl        |        paysv        | buts |  type  |    date 
---------------------+---------------------+------+--------+------------
 URSS                | Belgique            |    7 | 1/8    | 1986-06-15
 France              | Italie              |    2 | 1/8    | 1986-06-17
 Maroc               | Pologne             |    0 | Poule  | 1986-06-02
 RFA                 | Argentine           |    5 | Finale | 1986-06-29
 Brésil              | France              |    2 | 1/4    | 1986-06-21
 Italie              | Argentine           |    2 | Poule  | 1986-06-05
 Maroc               | Portugal            |    4 | Poule  | 1986-06-11
 Brésil              | Algérie             |    1 | Poule  | 1986-06-06
 Paraguay            | Belgique            |    4 | Poule  | 1986-06-11
 Hongrie             | France              |    3 | Poule  | 1986-06-09
 Irak                | Belgique            |    3 | Poule  | 1986-06-08
 Danemark            | RFA                 |    2 | Poule  | 1986-06-13
 Irlande du Nord     | Espagne             |    3 | Poule  | 1986-06-07
 Algérie             | Irlande du Nord     |    2 | Poule  | 1986-06-03
 RFA                 | Mexique             |    0 | 1/4    | 1986-06-21
 URSS                | Hongrie             |    6 | Poule  | 1986-06-02
 Mexique             | Paraguay            |    2 | Poule  | 1986-06-07
 Belgique            | Espagne             |    2 | 1/4    | 1986-06-22
 Irak                | Mexique             |    1 | Poule  | 1986-06-11
 Espagne             | Brésil              |    1 | Poule  | 1986-06-01
 Angleterre          | Maroc               |    0 | Poule  | 1986-06-06
 Irlande du Nord     | Brésil              |    2 | Poule  | 1986-06-12
 Maroc               | RFA                 |    1 | 1/8    | 1986-06-17
 Belgique            | Mexique             |    3 | Poule  | 1986-06-03
 Bulgarie            | Italie              |    2 | Poule  | 1986-05-31
 Écosse              | Uruguay             |    0 | Poule  | 1986-06-13
 Algérie             | Espagne             |    3 | Poule  | 1986-06-12
 Argentine           | Belgique            |    2 | 1/2    | 1986-06-25
 Brésil              | Pologne             |    4 | 1/8    | 1986-06-16
 Danemark            | Uruguay             |    7 | Poule  | 1986-06-08
 Corée               | Italie              |    5 | Poule  | 1986-06-10
 Canada              | France              |    1 | Poule  | 1986-06-01
 Argentine           | Uruguay             |    1 | 1/8    | 1986-06-16
 France              | RFA                 |    2 | 1/2    | 1986-06-25
 France              | URSS                |    2 | Poule  | 1986-06-05
 Uruguay             | RFA                 |    2 | Poule  | 1986-06-04
 Angleterre          | Pologne             |    3 | Poule  | 1986-06-11
 Portugal            | Angleterre          |    1 | Poule  | 1986-06-03
 Écosse              | Danemark            |    1 | Poule  | 1986-06-04
 Angleterre          | Paraguay            |    3 | 1/8    | 1986-06-18
 Hongrie             | Canada              |    2 | Poule  | 1986-06-06
 Argentine           | Corée               |    4 | Poule  | 1986-06-02
 Pologne             | Portugal            |    1 | Poule  | 1986-06-07
 RFA                 | Écosse              |    3 | Poule  | 1986-06-08
 Mexique             | Bulgarie            |    2 | 1/8    | 1986-06-15
 URSS                | Canada              |    2 | Poule  | 1986-06-09
 Espagne             | Danemark            |    6 | 1/8    | 1986-06-18
 Paraguay            | Irak                |    1 | Poule  | 1986-06-04
 Argentine           | Bulgarie            |    2 | Poule  | 1986-06-10
 Argentine           | Angleterre          |    3 | 1/4    | 1986-06-22
 Corée               | Bulgarie            |    2 | Poule  | 1986-06-05
(51 rows)

Exercise 3.2: Write a query which caluculates the number of goals scored on average in all the matches of the French team.

Response
    Moyenne buts
--------------------
 2.0000000000000000
(1 row)

Exercise 3.3: Write a query which calculates the total number of goals scored only by the French team.

Response
 buts 
------
    8
(1 row)

Exercise 3.4: Write a query which caluclates the total number of goals scored in each Poule match. Order the results by group.

Response
 groupe | sum 
--------+-----
 A      |  17
 B      |  14
 C      |  16
 D      |  12
 E      |  15
 F      |   9
(6 rows)

Exercise 3.5: Write a function vainquer which takes in the two countries of a match and the match type and which returns the winner. Apply your function to the following pairs:

SELECT * FROM vainqueur('Espagne', 'Danemark', '1/8');
SELECT * FROM vainqueur('Brésil', 'France', '1/4');
Response
 vainqueur 
-----------
 Espagne
(1 row)

 vainqueur 
-----------
 Match nul
(1 row)

Exercise 3.6: Write a function butsparequipe which returns the total and the average number of points scored by a team. Apply your function to the French team. Bonus points for making the result display the name of the team.

SELECT * FROM butsparequipe('France');
Response
  pays  | total |      moyenne 
--------+-------+--------------------
 France |     8 | 1.3333333333333333
(1 row)

Exercise 3.7: Using the butsparequipe function, write a query which lists all countries and the points they scored.

Response
        pays         | total 
---------------------+-------
 Argentine           |    14
 Italie              |     5
 Bulgarie            |     2
 Corée               |     4
 Mexique             |     6
 Paraguay            |     4
 Belgique            |    10
 Irak                |     1
 URSS                |    12
 Hongrie             |     2
 France              |     8
 Canada              |     0
 Brésil              |     9
 Espagne             |    11
 Irlande du Nord     |     2
 Algérie             |     1
 Danemark            |    10
 RFA                 |     8
 Uruguay             |     2
 Écosse              |     1
 Maroc               |     3
 Angleterre          |     7
 Pologne             |     1
 Portugal            |     2
(24 rows)

Exercise 3.8: Using the butsparequipe function, write a query which shows the country which scored the most points and the number of points they scored.

Response
   pays    | total 
-----------+-------
 Argentine |    14
(1 row)

Pull the triggerđź”—

In this exercise, we're going to create a TRIGGER, a mechanism which allows for automatically executing actions when an event occurs.

Create the db-trigger database.

$ createdb db-trigger

Exercise 4.1: Create a table rel(nom, value) where nom is a string of characters and value is an integer. nom will be the primary key

Solution
CREATE TABLE IF NOT EXISTS rel (
    nom VARCHAR(20),
    valeur INTEGER,
    PRIMARY KEY (nom)
);

Exercise 4.2: Add 5 tuples into the table

Solution
INSERT INTO rel VALUES
       ('Alice', 10),
       ('Bob', 5),
       ('Carl', 20),
       ('Denise', 11),
       ('Esther', 6);

Exercise 4.3: Write a trigger such that, when adding new tuples, the average value of val cannot decrease. If a new tuple is added which would decrease the average, an exception should be raised.

The following insertion should work:

INSERT INTO rel VALUES ('Fab', 15);

SELECT * FROM rel;

As we can see, the (Fab, 15) tuple was added:

  nom   | valeur 
--------+--------
 Alice  |     10
 Bob    |      5
 Carl   |     20
 Denise |     11
 Esther |      6
 Fab    |     15
(6 rows)

However, the following insertion should give an exception:

INSERT INTO rel VALUES ('Guy', 2);
Solution
CREATE OR REPLACE FUNCTION verifier_moyenne()
                  RETURNS trigger AS $verifier_moyenne$
    DECLARE
      moyenne FLOAT;
      nb      INTEGER;
    BEGIN
        moyenne := AVG(valeur) FROM rel;
        nb := COUNT(*) FROM rel;

        IF ((nb * moyenne + NEW.valeur) / (nb + 1)) < moyenne THEN
            RAISE EXCEPTION 'problem with insertion: valeur average is decreasing!';
        END IF;

        RETURN NEW;
    END;
$verifier_moyenne$ LANGUAGE plpgsql;

CREATE TRIGGER VerificationMoyenne
BEFORE INSERT ON rel
FOR EACH ROW
EXECUTE PROCEDURE verifier_moyenne();