0% found this document useful (0 votes)
128 views22 pages

Fifteen: 15.1 Lesson: Introduction To Databases

This document introduces fundamental concepts of relational databases and PostgreSQL. It discusses relational database management systems (RDBMS), tables, records, columns, datatypes, normalization to reduce data redundancy, indexing to improve search performance, sequences to generate unique IDs, and entity-relationship diagrams to model table relationships. The goal is to understand how to structure data optimally in a relational database using concepts like primary keys and foreign keys.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
128 views22 pages

Fifteen: 15.1 Lesson: Introduction To Databases

This document introduces fundamental concepts of relational databases and PostgreSQL. It discusses relational database management systems (RDBMS), tables, records, columns, datatypes, normalization to reduce data redundancy, indexing to improve search performance, sequences to generate unique IDs, and entity-relationship diagrams to model table relationships. The goal is to understand how to structure data optimally in a relational database using concepts like primary keys and foreign keys.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 22

CHAPTER

FIFTEEN

MODULE: DATABASE CONCEPTS WITH POSTGRESQL

Relational Databases are an important part of any GIS system. In this module, you’ll learn about Relational Database
Management System (RDBMS) concepts and you will use PostgreSQL to create a new database to store data, as well
as learning about other typical RDBMS functions.

15.1 Lesson: Introduction to Databases

Before using PostgreSQL, let’s make sure of our ground by covering general database theory. You will not need to
enter any of the example code; it’s only there for illustration purposes.
The goal for this lesson: To understand fundamental database concepts.

15.1.1 What is a Database?

A database consists of an organized collection of data for one or more uses, typically in digital form. - Wikipedia
A database management system (DBMS) consists of software that operates databases, providing storage, access,
security, backup and other facilities. - Wikipedia

15.1.2 Tables

In relational databases and flat file databases, a table is a set of data elements (values) that is organized using a model
of vertical columns (which are identified by their name) and horizontal rows. A table has a specified number of
columns, but can have any number of rows. Each row is identified by the values appearing in a particular column
subset which has been identified as a candidate key. - Wikipedia

id | name | age
----+-------+-----
1 | Tim | 20
2 | Horst | 88
(2 rows)

In SQL databases a table is also known as a relation.

423
QGIS Training Manual

15.1.3 Columns / Fields

A column is a set of data values of a particular simple type, one for each row of the table. The columns provide
the structure according to which the rows are composed. The term field is often used interchangeably with column,
although many consider it more correct to use field (or field value) to refer specifically to the single item that exists at
the intersection between one row and one column. - Wikipedia
A column:

| name |
+-------+
| Tim |
| Horst |

A field:

| Horst |

15.1.4 Records

A record is the information stored in a table row. Each record will have a field for each of the columns in the table.

2 | Horst | 88 <-- one record

15.1.5 Datatypes

Datatypes restrict the kind of information that can be stored in a column. - Tim and Horst
There are many kinds of datatypes. Let’s focus on the most common:
• String - to store free-form text data
• Integer - to store whole numbers
• Real - to store decimal numbers
• Date - to store Horst’s birthday so no one forgets
• Boolean - to store simple true/false values
You can tell the database to allow you to also store nothing in a field. If there is nothing in a field, then the field
content is referred to as a ‘null’ value:

insert into person (age) values (40);

select * from person;

Result:

id | name | age
---+-------+-----
1 | Tim | 20
2 | Horst | 88
4 | | 40 <-- null for name
(3 rows)

There are many more datatypes you can use - check the PostgreSQL manual!

424 Chapter 15. Module: Database Concepts with PostgreSQL


QGIS Training Manual

15.1.6 Modelling an Address Database

Let’s use a simple case study to see how a database is constructed. We want to create an address database.

Try Yourself

Write down the properties which make up a simple address and which we would want to store in our database.
Check your results

Address Structure

The properties that describe an address are the columns. The type of information stored in each column is its datatype.
In the next section we will analyse our conceptual address table to see how we can make it better!

15.1.7 Database Theory

The process of creating a database involves creating a model of the real world; taking real world concepts and repre-
senting them in the database as entities.

15.1.8 Normalisation

One of the main ideas in a database is to avoid data duplication / redundancy. The process of removing redundancy
from a database is called Normalisation.
Normalization is a systematic way of ensuring that a database structure is suitable for general-purpose querying and
free of certain undesirable characteristics - insertion, update, and deletion anomalies - that could lead to a loss of data
integrity. - Wikipedia
There are different kinds of normalisation ‘forms’.
Let’s take a look at a simple example:

Table "public.people"

Column | Type | Modifiers


----------+------------------------+------------------------------------
id | integer | not null default
| | nextval('people_id_seq'::regclass)
| |
name | character varying(50) |
address | character varying(200) | not null
phone_no | character varying |
Indexes:
"people_pkey" PRIMARY KEY, btree (id)

select * from people;

id | name | address | phone_no


---+---------------+-----------------------------+-------------
1 | Tim Sutton | 3 Buirski Plein, Swellendam | 071 123 123
2 | Horst Duester | 4 Avenue du Roix, Geneva | 072 121 122
(2 rows)

Imagine you have many friends with the same street name or city. Every time this data is duplicated, it consumes
space. Worse still, if a city name changes, you have to do a lot of work to update your database.

15.1. Lesson: Introduction to Databases 425


QGIS Training Manual

15.1.9 Try Yourself

Redesign the theoretical people table above to reduce duplication and to normalise the data structure.
You can read more about database normalisation here
Check your results

15.1.10 Indexes

A database index is a data structure that improves the speed of data retrieval operations on a database table. -
Wikipedia
Imagine you are reading a textbook and looking for the explanation of a concept - and the textbook has no index!
You will have to start reading at one cover and work your way through the entire book until you find the information
you need. The index at the back of a book helps you to jump quickly to the page with the relevant information:

create index person_name_idx on people (name);

Now searches on name will be faster:

Table "public.people"

Column | Type | Modifiers


----------+------------------------+-------------------------------------
id | integer | not null default
| | nextval('people_id_seq'::regclass)
| |
name | character varying(50) |
address | character varying(200) | not null
phone_no | character varying |
Indexes:
"people_pkey" PRIMARY KEY, btree (id)
"person_name_idx" btree (name)

15.1.11 Sequences

A sequence is a unique number generator. It is normally used to create a unique identifier for a column in a table.
In this example, id is a sequence - the number is incremented each time a record is added to the table:

id | name | address | phone_no


---+--------------+-----------------------------+-------------
1 | Tim Sutton | 3 Buirski Plein, Swellendam | 071 123 123
2 | Horst Duster | 4 Avenue du Roix, Geneva | 072 121 122

15.1.12 Entity Relationship Diagramming

In a normalised database, you typically have many relations (tables). The entity-relationship diagram (ER Diagram) is
used to design the logical dependencies between the relations. Consider our non-normalised people table from earlier
in the lesson:

select * from people;

id | name | address | phone_no


----+--------------+-----------------------------+-------------
1 | Tim Sutton | 3 Buirski Plein, Swellendam | 071 123 123
(continues on next page)

426 Chapter 15. Module: Database Concepts with PostgreSQL


QGIS Training Manual

(continued from previous page)


2 | Horst Duster | 4 Avenue du Roix, Geneva | 072 121 122
(2 rows)

With a little work we can split it into two tables, removing the need to repeat the street name for individuals who live
in the same street:

select * from streets;

id | name
----+--------------
1 | Plein Street
(1 row)

and:

select * from people;

id | name | house_no | street_id | phone_no


----+--------------+----------+-----------+-------------
1 | Horst Duster | 4 | 1 | 072 121 122
(1 row)

We can then link the two tables using the ‘keys’ streets.id and people.streets_id.
If we draw an ER Diagram for these two tables it would look something like this:

The ER Diagram helps us to express ‘one to many’ relationships. In this case the arrow symbol show that one street
can have many people living on it.

Try Yourself

Our people model still has some normalisation issues - try to see if you can normalise it further and show your thoughts
by means of an ER Diagram.
Check your results

15.1.13 Constraints, Primary Keys and Foreign Keys

A database constraint is used to ensure that data in a relation matches the modeller’s view of how that data should be
stored. For example a constraint on your postal code could ensure that the number falls between 1000 and 9999.
A Primary key is one or more field values that make a record unique. Usually the primary key is called id and is a
sequence.
A Foreign key is used to refer to a unique record on another table (using that other table’s primary key).
In ER Diagramming, the linkage between tables is normally based on Foreign keys linking to Primary keys.
If we look at our people example, the table definition shows that the street column is a foreign key that references the
primary key on the streets table:

15.1. Lesson: Introduction to Databases 427


QGIS Training Manual

Table "public.people"

Column | Type | Modifiers


-----------+-----------------------+--------------------------------------
id | integer | not null default
| | nextval('people_id_seq'::regclass)
name | character varying(50) |
house_no | integer | not null
street_id | integer | not null
phone_no | character varying |
Indexes:
"people_pkey" PRIMARY KEY, btree (id)
Foreign-key constraints:
"people_street_id_fkey" FOREIGN KEY (street_id) REFERENCES streets(id)

15.1.14 Transactions

When adding, changing, or deleting data in a database, it is always important that the database is left in a good state if
something goes wrong. Most databases provide a feature called transaction support. Transactions allow you to create
a rollback position that you can return to if your modifications to the database did not run as planned.
Take a scenario where you have an accounting system. You need to transfer funds from one account and add them
to another. The sequence of steps would go like this:
• remove R20 from Joe
• add R20 to Anne
If something goes wrong during the process (e.g. power failure), the transaction will be rolled back.

15.1.15 In Conclusion

Databases allow you to manage data in a structured way using simple code structures.

15.1.16 What’s Next?

Now that we’ve looked at how databases work in theory, let’s create a new database to implement the theory we’ve
covered.

15.2 Lesson: Implementing the Data Model

Now that we’ve covered all the theory, let’s create a new database. This database will be used for our exercises for
the lessons that will follow afterwards.
The goal for this lesson: To install the required software and use it to implement our example database.

428 Chapter 15. Module: Database Concepts with PostgreSQL


QGIS Training Manual

15.2.1 Install PostgreSQL

Note: You can find PostGreSQL packages and installation instructions for your operating system at https://www.
postgresql.org/download/. Please note that the documentation will assume users are running QGIS under Ubuntu.

Under Ubuntu:

sudo apt install postgresql-9.1

You should get a message like this:

[sudo] password for qgis:


Reading package lists... Done
Building dependency tree
Reading state information... Done
The following extra packages will be installed:
postgresql-client-9.1 postgresql-client-common postgresql-common
Suggested packages:
oidentd ident-server postgresql-doc-9.1
The following NEW packages will be installed:
postgresql-9.1 postgresql-client-9.1 postgresql-client-common postgresql-common
0 upgraded, 4 newly installed, 0 to remove and 5 not upgraded.
Need to get 5,012kB of archives.
After this operation, 19.0MB of additional disk space will be used.
Do you want to continue [Y/n]?

Press Y and Enter and wait for the download and installation to finish.

15.2.2 Help

PostgreSQL has very good online documentation.

15.2.3 Create a database user

Under Ubuntu:
After the installation is complete, run this command to become the postgres user and then create a new database user:

sudo su - postgres

Type in your normal log in password when prompted (you need to have sudo rights).
Now, at the postgres user’s bash prompt, create the database user. Make sure the user name matches your unix login
name: it will make your life much easier, as postgres will automatically authenticate you when you are logged in as
that user:

createuser -d -E -i -l -P -r -s qgis

Enter a password when prompted. You should use a different password to your login password.
What do those options mean?

-d, --createdb role can create new databases


-E, --encrypted encrypt stored password
-i, --inherit role inherits privileges of roles it is a member of (default)
-l, --login role can login (default)
-P, --pwprompt assign a password to new role
-r, --createrole role can create new roles
-s, --superuser role will be superuser

15.2. Lesson: Implementing the Data Model 429


QGIS Training Manual

Now you should leave the postgres user’s bash shell environment by typing:

exit

15.2.4 Verify the new account

psql -l

Should return something like this:

Name | Owner | Encoding | Collation | Ctype |


----------+----------+----------+------------+------------+
postgres | postgres | UTF8 | en_ZA.utf8 | en_ZA.utf8 |
template0 | postgres | UTF8 | en_ZA.utf8 | en_ZA.utf8 |
template1 | postgres | UTF8 | en_ZA.utf8 | en_ZA.utf8 |
(3 rows)

Type Q to exit.

15.2.5 Create a database

The createdb command is used to create a new database. It should be run from the bash shell prompt:

createdb address -O qgis

You can verify the existence of your new database by using this command:

psql -l

Which should return something like this:

Name | Owner | Encoding | Collation | Ctype | Access privileges


----------+----------+----------+------------+------------+-----------------------
address | qgis | UTF8 | en_ZA.utf8 | en_ZA.utf8 |
postgres | postgres | UTF8 | en_ZA.utf8 | en_ZA.utf8 |
template0 | postgres | UTF8 | en_ZA.utf8 | en_ZA.utf8 | =c/postgres:␣
,→postgres=CTc/postgres

template1 | postgres | UTF8 | en_ZA.utf8 | en_ZA.utf8 | =c/postgres:␣


,→postgres=CTc/postgres

(4 rows)

Type Q to exit.

15.2.6 Starting a database shell session

You can connect to your database easily like this:

psql address

To exit out of the psql database shell, type:

\q

For help in using the shell, type:

\?

For help in using sql commands, type:

430 Chapter 15. Module: Database Concepts with PostgreSQL


QGIS Training Manual

\help

To get help on a specific command, type (for example):


\help create table

See also the Psql cheat sheet.

15.2.7 Make Tables in SQL

Let’s start making some tables! We will use our ER Diagram as a guide. First, connect to the address db:
psql address

Then create a streets table:


create table streets (id serial not null primary key, name varchar(50));

serial and varchar are data types. serial tells PostgreSQL to start an integer sequence (auto-number) to
populate the id automatically for every new record. varchar(50) tells PostgreSQL to create a character field of
50 characters in length.
You will notice that the command ends with a ; - all SQL commands should be terminated this way. When you press
Enter, psql will report something like this:
NOTICE: CREATE TABLE will create implicit sequence "streets_id_seq"
for serial column "streets.id"
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index
"streets_pkey" for table "streets"
CREATE TABLE

That means your table was created successfully, with a primary key streets_pkey using streets.id.
Note: If you hit return without entering a ;, then you will get a prompt like this: address-#. This is because PG
is expecting you to enter more. Enter ; to run your command.
To view your table schema, you can do this:
\d streets

Which should show something like this:


Table "public.streets"
Column | Type | Modifiers
--------+-----------------------+--------------------------------------
id | integer | not null default
| | nextval('streets_id_seq'::regclass)
name | character varying(50) |
Indexes:
"streets_pkey" PRIMARY KEY, btree (id)

To view your table contents, you can do this:


select * from streets;

Which should show something like this:


id | name
---+------
(0 rows)

As you can see, our table is currently empty.

15.2. Lesson: Implementing the Data Model 431


QGIS Training Manual

Try Yourself

Use the approach shown above to make a table called people:


Add fields such as phone number, home address, name, etc. (these aren’t all valid names: change them to make them
valid). Make sure you give the table an ID column with the same data-type as above.
Check your results

15.2.8 Create Keys in SQL

The problem with our solution above is that the database doesn’t know that people and streets have a logical rela-
tionship. To express this relationship, we have to define a foreign key that points to the primary key of the streets
table.

There are two ways to do this:


• Add the key after the table has been created
• Define the key at time of table creation
Our table has already been created, so let’s do it the first way:

alter table people


add constraint people_streets_fk foreign key (street_id) references streets(id);

That tells the people table that its street_id fields must match a valid street id from the streets table.
The more usual way to create a constraint is to do it when you create the table:

create table people (id serial not null primary key,


name varchar(50),
house_no int not null,
street_id int references streets(id) not null,
phone_no varchar null);

\d people

After adding the constraint, our table schema looks like this now:

Table "public.people"

Column | Type | Modifiers


-----------+-----------------------+---------------------------------
id | integer | not null default
| | nextval('people_id_seq'::regclass)
name | character varying(50) |
house_no | integer | not null
street_id | integer | not null
phone_no | character varying |
Indexes:
"people_pkey" PRIMARY KEY, btree (id)
Foreign-key constraints:
"people_streets_fk" FOREIGN KEY (id) REFERENCES streets(id)

432 Chapter 15. Module: Database Concepts with PostgreSQL


QGIS Training Manual

15.2.9 Create Indexes in SQL

We want lightning fast searches on peoples names. To provide for this, we can create an index on the name column
of our people table:

create index people_name_idx on people(name);

\d people

Which results in:

Table "public.people"

Column | Type | Modifiers


-----------+-----------------------+-----------------------------------
id | integer | not null default nextval
| | ('people_id_seq'::regclass)
name | character varying(50) |
house_no | integer | not null
street_id | integer | not null
phone_no | character varying |
Indexes:
"people_pkey" PRIMARY KEY, btree (id)
"people_name_idx" btree (name) <-- new index added!
Foreign-key constraints:
"people_streets_fk" FOREIGN KEY (id) REFERENCES streets(id)

15.2.10 Dropping Tables in SQL

If you want to get rid of a table you can use the drop command:

drop table streets;

Note: In our current example, the above command would not work. Why not? See why

If you used the same drop table command on the people table, it would be successful:

drop table people;

Note: If you actually did enter that command and dropped the people table, now would be a good time to rebuild
it, as you will need it in the next exercises.

15.2.11 A word on pgAdmin III

We are showing you the SQL commands from the psql prompt because it’s a very useful way to learn about databases.
However, there are quicker and easier ways to do a lot of what we are showing you. Install pgAdmin III and you can
create, drop, alter etc tables using ‘point and click’ operations in a GUI.
Under Ubuntu, you can install it like this:

sudo apt install pgadmin3

pgAdmin III will be covered in more detail in another module.

15.2. Lesson: Implementing the Data Model 433


QGIS Training Manual

15.2.12 In Conclusion

You have now seen how to create a brand new database, starting completely from scratch.

15.2.13 What’s Next?

Next you’ll learn how to use the DBMS to add new data.

15.3 Lesson: Adding Data to the Model

The models we’ve created will now need to be populated with the data they’re intended to contain.
The goal for this lesson: To learn how to insert new data into the database models.

15.3.1 Insert statement

How do you add data to a table? The sql INSERT statement provides the functionality for this:

insert into streets (name) values ('High street');

A couple of things to note:


• After the table name (streets), you list the column names that you will be populating (in this case only the
name column).
• After the values keyword, place the list of field values.
• Strings should be quoted using single quotes.
• Note that we did not insert a value for the id column; this is because it is a sequence and will be auto-generated.
• If you do manually set the id, you may cause serious problems with the integrity of your database.
You should see INSERT 0 1 if it is successful.
You can see the result of your insert action by selecting all the data in the table:

select * from streets;

Result:

select * from streets;


id | name
----+-------------
1 | High street
(1 row)

Try Yourself

Use the INSERT command to add a new street to the streets table.
Check your results

434 Chapter 15. Module: Database Concepts with PostgreSQL


QGIS Training Manual

15.3.2 Sequencing Data Addition According to Constraints

15.3.3 Try Yourself

Try to add a person object to the people table with the following details:
Name: Joe Smith
House Number: 55
Street: Main Street
Phone: 072 882 33 21

Note: Recall that in this example, we defined phone numbers as strings, not integers.

At this point, you should have an error report if you try to do this without first creating a record for Main Street in
the streets table.
You should have also noticed that:
• You can’t add the street using its name
• You can’t add a street using a street id before first creating the street record on the streets table
Remember that our two tables are linked via a Primary/Foreign Key pair. This means that no valid person can be
created without there also being a valid corresponding street record.
Using the above knowledge, add the new person to the database.
Check your results

15.3.4 Select data

We have already shown you the syntax for selecting records. Let’s look at a few more examples:
select name from streets;

select * from streets;

select * from streets where name='Main Road';

In later sessions we will go into more detail on how to select and filter data.

15.3.5 Update data

What if you want to make a change to some existing data? For example, a street name is changed:
update streets set name='New Main Road' where name='Main Road';

Be very careful using such update statements - if more than one record matches your WHERE clause, they will all be
updated!
A better solution is to use the primary key of the table to reference the record to be changed:
update streets set name='New Main Road' where id=2;

It should return UPDATE 1.

Note: the WHERE statement criteria are case sensitive Main Road is not the same as Main road

15.3. Lesson: Adding Data to the Model 435


QGIS Training Manual

15.3.6 Delete Data

In order to delete an object from a table, use the DELETE command:


delete from people where name = 'Joe Smith';

Let’s look at our people table now:


address=# select * from people;

id | name | house_no | street_id | phone_no


----+------+----------+-----------+----------
(0 rows)

15.3.7 Try Yourself

Use the skills you have learned to add some new friends to your database:
name | house_no | street_id | phone_no
-----------------+----------+-----------+--------------
Joe Bloggs | 3 | 2 | 072 887 23 45
Jane Smith | 55 | 3 | 072 837 33 35
Roger Jones | 33 | 1 | 072 832 31 38
Sally Norman | 83 | 1 | 072 932 31 32

15.3.8 In Conclusion

Now you know how to add new data to the existing models you created previously. Remember that if you want to
add new kinds of data, you may want to modify and/or create new models to contain that data.

15.3.9 What’s Next?

Now that you’ve added some data, you’ll learn how to use queries to access this data in various ways.

15.4 Lesson: Queries

When you write a SELECT ... command it is commonly known as a query - you are interrogating the database
for information.
The goal of this lesson: To learn how to create queries that will return useful information.

Note: If you did not do so in the previous lesson, add the following people objects to your people table. If you
receive any errors related to foreign key constraints, you will need to add the ‘Main Road’ object to your streets table
first

insert into people (name,house_no, street_id, phone_no)


values ('Joe Bloggs',3,2,'072 887 23 45');
insert into people (name,house_no, street_id, phone_no)
values ('Jane Smith',55,3,'072 837 33 35');
insert into people (name,house_no, street_id, phone_no)
values ('Roger Jones',33,1,'072 832 31 38');
insert into people (name,house_no, street_id, phone_no)
values ('Sally Norman',83,1,'072 932 31 32');

436 Chapter 15. Module: Database Concepts with PostgreSQL


QGIS Training Manual

15.4.1 Ordering Results

Let’s retrieve a list of people ordered by their house numbers:

select name, house_no from people order by house_no;

Result:

name | house_no
--------------+----------
Joe Bloggs | 3
Roger Jones | 33
Jane Smith | 55
Sally Norman | 83
(4 rows)

You can sort the results by the values of more than one column:

select name, house_no from people order by name, house_no;

Result:

name | house_no
--------------+----------
Jane Smith | 55
Joe Bloggs | 3
Roger Jones | 33
Sally Norman | 83
(4 rows)

15.4.2 Filtering

Often you won’t want to see every single record in the database - especially if there are thousands of records and you
are only interested in seeing one or two.
Here is an example of a numerical filter which only returns objects whose house_no is less than 50:

select name, house_no from people where house_no < 50;

name | house_no
-------------+----------
Joe Bloggs | 3
Roger Jones | 33
(2 rows)

You can combine filters (defined using the WHERE clause) with sorting (defined using the ORDER BY clause):

select name, house_no from people where house_no < 50 order by house_no;

name | house_no
-------------+----------
Joe Bloggs | 3
Roger Jones | 33
(2 rows)

You can also filter based on text data:

select name, house_no from people where name like '%s%';

name | house_no
(continues on next page)

15.4. Lesson: Queries 437


QGIS Training Manual

(continued from previous page)


-------------+----------
Joe Bloggs | 3
Roger Jones | 33
(2 rows)

Here we used the LIKE clause to find all names with an s in them. You’ll notice that this query is case-sensitive, so
the Sally Norman entry has not been returned.
If you want to search for a string of letters regardless of case, you can do a case in-sensitive search using the ILIKE
clause:

select name, house_no from people where name ilike '%r%';

name | house_no
--------------+----------
Roger Jones | 33
Sally Norman | 83
(2 rows)

That query returned every people object with an r or R in their name.

15.4.3 Joins

What if you want to see the person’s details and their street’s name instead of the ID? In order to do that, you need
to join the two tables together in a single query. Lets look at an example:

select people.name, house_no, streets.name


from people,streets
where people.street_id=streets.id;

Note: With joins, you will always state the two tables the information is coming from, in this case people and streets.
You also need to specify which two keys must match (foreign key & primary key). If you don’t specify that, you will
get a list of all possible combinations of people and streets, but no way to know who actually lives on which street!

Here is what the correct output will look like:

name | house_no | name


--------------+----------+-------------
Joe Bloggs | 3 | Low Street
Roger Jones | 33 | High street
Sally Norman | 83 | High street
Jane Smith | 55 | Main Road
(4 rows)

We will revisit joins as we create more complex queries later. Just remember they provide a simple way to combine
the information from two or more tables.

438 Chapter 15. Module: Database Concepts with PostgreSQL


QGIS Training Manual

15.4.4 Sub-Select

Sub-selections allow you to select objects from one table based on the data from another table which is linked via a
foreign key relationship. In our case, we want to find people who live on a specific street.
First, let’s do a little tweaking of our data:

insert into streets (name) values('QGIS Road');


insert into streets (name) values('OGR Corner');
insert into streets (name) values('Goodle Square');
update people set street_id = 2 where id=2;
update people set street_id = 3 where id=3;

Let’s take a quick look at our data after those changes: we can reuse our query from the previous section:

select people.name, house_no, streets.name


from people,streets
where people.street_id=streets.id;

Result:

name | house_no | name


--------------+----------+-------------
Roger Jones | 33 | High street
Sally Norman | 83 | High street
Jane Smith | 55 | Main Road
Joe Bloggs | 3 | Low Street
(4 rows)

Now let’s show you a sub-selection on this data. We want to show only people who live in street_id number 1:

select people.name
from people, (
select *
from streets
where id=1
) as streets_subset
where people.street_id = streets_subset.id;

Result:

name
--------------
Roger Jones
Sally Norman
(2 rows)

Although this is a very simple example and unnecessary with our small data-sets, it illustrates how useful and important
sub-selections can be when querying large and complex data-sets.

15.4.5 Aggregate Queries

One of the powerful features of a database is its ability to summarise the data in its tables. These summaries are
called aggregate queries. Here is a typical example which tells us how many people objects are in our people table:

select count(*) from people;

Result:

15.4. Lesson: Queries 439


QGIS Training Manual

count
-------
4
(1 row)

If we want the counts to be summarised by street name we can do this:

select count(name), street_id


from people
group by street_id;

Result:

count | street_id
-------+-----------
2 | 1
1 | 3
1 | 2
(3 rows)

Note: Because we have not used an ORDER BY clause, the order of your results may not match what is shown here.

Try Yourself

Summarise the people by street name and show the actual street names instead of the street_ids.
Check your results

15.4.6 In Conclusion

You’ve seen how to use queries to return the data in your database in a way that allows you to extract useful information
from it.

15.4.7 What’s Next?

Next you’ll see how to create views from the queries that you’ve written.

15.5 Lesson: Views

When you write a query, you need to spend a lot of time and effort formulating it. With views, you can save the
definition of an SQL query in a reusable ‘virtual table’.
The goal for this lesson: To save a query as a view.

440 Chapter 15. Module: Database Concepts with PostgreSQL


QGIS Training Manual

15.5.1 Creating a View

You can treat a view just like a table, but its data is sourced from a query. Let’s make a simple view based on the
above:

create view roads_count_v as


select count(people.name), streets.name
from people, streets where people.street_id=streets.id
group by people.street_id, streets.name;

As you can see the only change is the create view roads_count_v as part at the beginning. We can now
select data from that view:

select * from roads_count_v;

Result:

count | name
-------+-------------
1 | Main Road
2 | High street
1 | Low Street
(3 rows)

15.5.2 Modifying a View

A view is not fixed, and it contains no ‘real data’. This means you can easily change it without impacting on any data
in your database:

CREATE OR REPLACE VIEW roads_count_v AS


SELECT count(people.name), streets.name
FROM people, streets WHERE people.street_id=streets.id
GROUP BY people.street_id, streets.name
ORDER BY streets.name;

(This example also shows the best practice convention of using UPPER CASE for all SQL keywords.)
You will see that we have added an ORDER BY clause so that our view rows are nicely sorted:

select * from roads_count_v;

count | name
-------+-------------
2 | High street
1 | Low Street
1 | Main Road
(3 rows)

15.5.3 Dropping a View

If you no longer need a view, you can delete it like this:

drop view roads_count_v;

15.5. Lesson: Views 441


QGIS Training Manual

15.5.4 In Conclusion

Using views, you can save a query and access its results as if it were a table.

15.5.5 What’s Next?

Sometimes, when changing data, you want your changes to have effects elsewhere in the database. The next lesson
will show you how to do this.

15.6 Lesson: Rules

Rules allow the “query tree” of an incoming query to be rewritten. One common usage is to implement views,
including updatable view. - Wikipedia
The goal for this lesson: To learn how to create new rules for the database.

15.6.1 Creating a logging rule

Say you want to log every change of phone_no in your people table in to a people_log table. So you set up a new
table:

create table people_log (name text, time timestamp default NOW());

In the next step, create a rule that logs every change of a phone_no in the people table into the people_log table:

create rule people_log as on update to people


where NEW.phone_no <> OLD.phone_no
do insert into people_log values (OLD.name);

To test that the rule works, let’s modify a phone number:

update people set phone_no = '082 555 1234' where id = 2;

Check that the people table was updated correctly:

select * from people where id=2;

id | name | house_no | street_id | phone_no


----+------------+----------+-----------+--------------
2 | Joe Bloggs | 3 | 2 | 082 555 1234
(1 row)

Now, thanks to the rule we created, the people_log table will look like this:

select * from people_log;

name | time
------------+----------------------------
Joe Bloggs | 2014-01-11 14:15:11.953141
(1 row)

Note: The value of the time field will depend on the current date and time.

442 Chapter 15. Module: Database Concepts with PostgreSQL


QGIS Training Manual

15.6.2 In Conclusion

Rules allow you to automatically add or change data in your database to reflect changes in other parts of the database.

15.6.3 What’s Next?

The next module will introduce you to Spatial Database using PostGIS, which takes these database concepts and
applies them to GIS data.

15.6. Lesson: Rules 443


QGIS Training Manual

444 Chapter 15. Module: Database Concepts with PostgreSQL

You might also like