Learn SQL (Using MySQL) in One Day and Learn It Well
Learn SQL (Using MySQL) in One Day and Learn It Well
Learn SQL (Using MySQL) in One Day and Learn It Well
Well
SQL for Beginners with Hands-on Project
The only book you need to start coding in SQL
immediately
By Jamie Chan
Preface
This book is written to help you learn SQL programming FAST and learn it
WELL. We'll be using MySQL in the book, which is a free database
management system that is widely used.
If you are an absolute beginner to SQL, you'll find that this book explains
complex concepts in an easy to understand and concise manner. If you are an
experienced coder, you'll appreciate that this book covers a wide range of
topics.
Topics covered include basic concepts like table creation and data
manipulation, to more advanced concepts like triggers, cursors, stored
routines and more. These topics are carefully selected to give you a broad
exposure to SQL, while not overwhelming you with information overload.
Contents
Chapter 1: Introduction
What is SQL?
Getting Ready to Code
Installing MySQL applications
Launching MySQL Workbench
Writing our first MySQL code
Comments
Chapter 8: Views
What is a view?
Creating a View
Altering a View
Deleting a View
Chapter 9: Triggers
What is a Trigger?
Creating a Trigger
Deleting a Trigger
At the end of the book, we’ll also be working on a new project together. This
project involves building a SQL database for a sports complex. We’ll learn to
build the database, insert data, perform queries, write routines, views,
cursors, and more.
Excited and ready to start embarking on our SQL learning journey? Let’s do
it!
What is SQL?
Simply stated, SQL stands for Structured Query Language and is a language
used to manage data stored in a relational database.
Clear?
So far, we have the following terminologies:
1) SQL is a language
2) A database is a structured collection of data
3) A DBMS is a software that we use to manage our databases
With regards to DBMS, there are a large number of them available. Some are
free to download and use while others are not. The most commonly used
DBMS include MySQL, Oracle, Microsoft SQL Server and IBM DB2.
Each of these DBMS have their own versions of SQL. While this may sound
intimidating, rest assured that all DBMS support the major SQL commands
(such as SELECT, UPDATE, DELETE, INSERT) in a similar manner. Hence, if you
know one version of SQL, it is very easy to pick up other versions.
In this book, we'll be using MySQL. This is one of the most popular DBMS
available. Best of all, it’s free! From this point forward, whenever I mention
SQL, I’m referring to the MySQL version.
Scroll down and click on the first “Download” button to download the
application. You’ll be directed to another page. Scroll to the bottom of the
page and click on “No thanks, just start my download.”
Once you have downloaded the program, double-click on the file and follow
the instructions to install the software.
When prompted to choose a setup type, select “Custom” and click “Next”.
You’ll be asked to select the products and features to install.
Under “Available Products”, fully expand “MySQL Servers” by clicking on
the + signs. Once fully expanded, click on the last item and click on the green
arrow to move this item to the “Products/Features To Be Installed” section.
Once you are done, click on “Next” and follow the instructions to continue
with the installation. Stick to the default options selected at each stage.
When prompted to enter a password, enter your desired password and jot
down the password. You’ll need this password later.
Mac OS
Scroll down and click on the first “Download” button to download the
application.
Once you do that, you’ll be directed to another page. Scroll to the bottom of
the page and click on “No thanks, just start my download.”
Once you have downloaded the program, double-click on the file to unzip it.
Next, double-click on the unzipped file and follow the instructions to install
the software. At the configuration stage, choose “Use Strong Password
Encryption” and click “Next”. Enter your desired password and jot down the
password. You’ll need this password later. Ensure that the "Start MySQL
Server once the installation is complete" option is selected and click on
"Finish" to complete the installation.
Once you are done installing MySQL Server, you need to install another
software known as MySQL Workbench. This software provides us with a
graphical user interface to make it easier for us to interact with MySQL. To
download MySQL Workbench, go to
https://dev.mysql.com/downloads/workbench/.
Got that?
Great!
Type the following lines into the text editor (for the first line, be sure to add a
space after the two hyphens):
You should notice that the first line is in gray while the word SELECT is in
blue and 'Hello World' and 'MySQL is fun!' are in green.
This is the software’s way of making our code easier to read. Different words
serve different purposes in our program, hence they are displayed using
different colors. We’ll go into more details in later chapters.
There are two ways to run the SQL commands that we wrote.
The first is to select all the code that we want to run and click on the
“Execute Selected” button (the button with a lightning bolt). This button
should normally be located to the right of the “Save” button (refer to the
previous screenshot).
This will execute all the code that is selected. You’ll see a new panel called
“Result Grid”, with two tabs as shown below:
These two tabs give the results of the last two lines of code that we wrote (i.e.
the two lines that start with the word SELECT).
The first line of code that we wrote does not give any result as it is a
comment. We’ll talk about comments in the next section.
Besides running all the code at one go, you can also choose to execute them
one by one. To do that, simply position your cursor on the line that you want
to execute and click on the “Execute Statement” button to run that statement.
The “Execute Statement” button shows a lightning bolt and a cursor and is
located to the right of the “Execute Selected” button.
For instance, if you place your cursor as shown in the image below
and click on the “Execute Statement” button, you’ll get a single tab in the
“Result Grid” that displays the message Hello World.
Comments
Now that we know how to execute SQL statements, we are ready to start
learning some SQL commands. However, before we do that, there’s more
one concept that I’ll like to cover - comments.
As mentioned previously, the first line in the code that we wrote (-- Using
SELECT to display messages) is a comment.
Comments are written to make our code more readable for other
programmers. They are meant for humans only and are ignored by the
DBMS.
Last, but not least, if we want to type multiple lines comments, we can use
the /*...*/ symbols:
/* This is a comment
This is also a comment
This is the third comment */
Chapter 2: Defining the Database
Now that we have a basic understand of MySQL, let’s start learning some
SQL commands.
Next, launch MySQL Workbench and create a new file by clicking on File >
New Query Tab and save this file as practice.sql (File > Save Script As... ) in
the MySQLExamples folder.
*** Examples that you should try will be presented in bold. Whenever you
see code presented in bold, you should type them into practice.sql and
execute them yourself to try them out (even when not prompted to do so).
***
The source file for all code presented in bold can be downloaded from
http://www.learncodingfast.com/sql.
Code that is not in bold is purely for demonstration purposes. You should not
try them as they may alter the structure or content of our database.
Keywords are generally not case sensitive in SQL. Hence, you can also write
create database or CREATE database. However, the common practice is to
use uppercase for keywords. That’s the convention that we’ll be following in
this book. For all the syntaxes presented in this book, any word that is in
uppercase is a keyword.
This is known as a SQL statement. SQL statements always end with a semi-
colon (;) unless otherwise stated.
Type this statement into practice.sql and execute it. You should get
USE name_of_database;
USE companyHR;
You can see that we used square brackets [ ] in the DROP DATABASE syntax
above. These brackets will be used throughout the book to indicate optional
content.
Creating Tables
First, let’s look at how we can create tables to add to our database. To create
a table, we use the following syntax:
Each row, on the other hand, stores information about one employee. A row
is sometimes also referred to as a record.
Specifying Columns
When we create the table, we need to specify the columns. For each column,
we need to state the data type and any constraints that the column must
satisfy.
Data Types
Data type refers to the type of data that the column stores. For instance, the
first column in the previous table (id) stores numerical information. The
second column (em_name) stores textual information.
In MySQL, there are a number of commonly used data types:
Textual information, also known as strings, are commonly stored using the
CHAR or VARCHAR data type in MySQL. They can contain letters, numbers or
special characters.
CHAR(size)
CHAR stands for “character” and is used to store a fixed length string of up to
255 characters. The desired length is specified in parentheses after the CHAR
keyword.
If you try to store a string that is longer than the specified length, you’ll get
an error.
If you store a string that is shorter than the specified length, the string will be
right-padded with spaces.
For instance, if you specify a column as CHAR(5) but use it to store a string
like 'NY', it will be stored as 'NY '. However, when the string is
subsequently retrieved, these spaces will not be displayed.
VARCHAR(size)
VARCHAR is another data type for storing strings. It holds a variable length
string and can store up to 255 characters. You have to specify the maximum
length in parentheses.
As you can see, VARCHAR is more flexible and uses less storage than CHAR in
most cases. However, it can be slower than CHAR.
In most cases, if you are storing strings that are of fixed lengths (e.g. gender
represented as 'M' or 'F' or state abbreviations such as 'NY', 'AL', 'AK' etc),
you should use CHAR. Otherwise, you should use VARCHAR.
Next, let’s look at numbers. Numbers in MySQL are commonly stored using
INT, FLOAT, DOUBLE or DECIMAL.
INT
INTstands for integers (i.e. numbers with no fractional parts) and can hold
numbers from -2147483648 to 2147483647.
FLOAT(m, d)
FLOAT is used to store non integers (i.e. numbers with fractional parts). It uses
4 bytes of storage.
In MySQL, you can specify two parameters - m and d - when defining FLOAT.
mrefers to the total number of digits the FLOAT stores while d refers to the
number of digits after the decimal point.
For instance, if you store a number ‒ say 12.34567 ‒ as FLOAT(5, 3), it will
be rounded off to 12.346 (i.e. 5 digits in total, three of which are after the
decimal point).
This discrepancy is not due to rounding (as 1.23456789999 when rounded off
to 9 decimal places should be 1.234567900). Instead, this discrepancy is due
to the fact that floats are stored as approximate values.
DOUBLE(m, d)
DECIMAL(m, d)
YEAR
The YEAR data type is used to store a year in either a two-digit or a four-digit
format. Values allowed in four-digit format are from 1901 to 2155. Values
allowed in two-digit format are from 1 to 69 (representing years from 2001 to
2069) and 70 to 99 (representing years from 1970 to 1999).
DATE
The DATE datatype is used to store a date in the YYYY-MM-DD format, with a
supported range of '1000-01-01' to '9999-12-31'.
TIME
The TIME data type is used to store time in the HH:MI:SS format, with a
supported range of '-838:59:59' to '838:59:59'.
DATETIME
The DATETIME data type is used to store a date and time combination in the
YYYY-MM-DD HH:MI:SS format. The supported range is from '1000-01-01
00:00:00' to '9999-12-31 23:59:59'.
TIMESTAMP
The TIMESTAMP data type is also used to store a date and time combination in
the YYYY-MM-DD HH:MI:SS format. The supported range is from '1970-01-01
00:00:01' UTC to '2038-01-09 03:14:07' UTC.
Hence, for instance, if a user is in the UTC+4 time zone and stores a
TIMESTAMP as '2018-04-11 09:00:00', someone in the UTC time zone will
see this data as '2018-04-11 05:00:00'.
In contrast, if the data is stored using the DATETIME data type, all users will
see the data as '2018-04-11 09:00:00' regardless of which time zone they
are in.
This unique conversion feature of the TIMESTAMP data type makes it very
useful for databases that are used by users across different time zones. It is
also commonly used to record information about when a piece of data was
inserted into the database.
Column Constraints
Besides specifying the data type of each column, we can also add constraints
for those columns. These constraints are requirements that the columns must
satisfy. Each constraint must be specified using predefined keywords in
MySQL.
NOT NULL
Specifies that a column cannot be empty. In other words, it must have a value
for all rows.
UNIQUE
DEFAULT
PRIMARY KEY
Specifies that the column is a primary key. A primary key uniquely identifies
each row in a table.
AUTO_INCREMENT
Specifies that the values for this column should be automatically increased by
1 for each new record. This feature is often used to generate a primary key for
the table. By default, the starting value for an auto increment column is 1.
We’ll learn how to change this starting value in the next chapter.
Each table can only have one auto increment column and that column must
be defined as a key (such as a primary key or a unique key).
Now that we know how to specify the columns of a table, let us create our
first table - co_employees.
id: INT
em_name: VARCHAR(255)
gender: CHAR(1)
contact_number: VARCHAR(255)
age: INT
date_created: TIMESTAMP
All columns of the table cannot be null except for the contact_number
column.
Finally, the date_created column has a default value provided by the NOW()
function.
A function is a block of code that performs a certain task. The NOW() function
is a built-in function that comes with MySQL (i.e. it is already pre-coded for
us). It gives us the current date and time whenever it is being used (i.e.
whenever a new record is inserted into the table). We’ll talk more about built-
in functions in Chapter 6. For now, let’s just use the function in our table
creation code.
Copy the code into practice.sql and execute it to create the co_employees
table yourself.
To execute this statement, simply place your cursor anywhere within the
statement and click on the ‘Execute Statement’ button (refer to Chapter 1 if
you have forgotten where the ‘Execute Statement’ button is).
If you study the mentorships table, you will notice that no column is suitable
to be used as a primary key. This is because a primary key has to uniquely
identify each row; no row satisfies this requirement.
For instance, we cannot use mentor_id as the primary key as two different
rows (rows 1 and 2) have the same mentor_id. The same applies to the
mentee_id, status and project columns.
For instance, we can use the combination of the mentor_id, mentee_id and
project as the primary key.
When we do that, we will not be allowed to input two rows with the same
mentor_id, mentee_id, project combination. For instance, if we add
another row with the values
mentor_id = 1
mentee_id = 2
project = 'SQF Limited'
we’ll get an error as the first row in the table already has this combination.
This is another way of specifying the primary key of a table and is most
useful when the primary key is made up of multiple columns.
Next, let’s look at how we can add foreign key constraints to our
mentorships table.
We can then refer to the co_employees table and see that employees 1, 2 and
3 are James Lee, Peter Pasternak and Clara Couto respectively. From the
mentorships table, we know that James Lee is a mentor to Peter Pasternak
and Clara Couto.
How can we describe this relationship between the two tables in MySQL?
A foreign key is a column (or a collection of columns) in one table that links
to the primary key in another table.
To specify that mentor_id and mentee_id are foreign keys, we add the code
below when creating the mentorships table:
These two lines specify that mentor_id and mentee_id are foreign keys and
that they reference the id column in the co_employees table.
When done this way, the co_employees table is known as the parent table
and the mentorships table is known as the child table.
Stating that mentor_id and mentee_id are foreign keys add a restriction to
them. We will not be allowed to add a record to the mentorships table if the
mentor_id or mentee_id does not exist in the co_employees table.
For instance, if the co_employees table does not have a row with id = 16,
we can’t add the following record
mentor_id = 1
mentee_id = 16
status = 'Ongoing'
project = 'SQF Limited'
to our mentorships table as this mentee has a mentee_id of 16.
Next, note that we also added the ON DELETE CASCADE and ON UPDATE
RESTRICT clauses to our foreign keys.
Besides the ON DELETE CASCADE clause, there are other ON DELETE clauses
that we can add to a foreign key. These clauses include:
ON DELETE RESTRICT - The row in the parent table cannot be deleted if a row
in the child table references that parent row.
ON DELETE SET NULL - The child row foreign key value will be set to NULL if
the parent row is deleted. For this to work, the relevant column in the child
row must allow for NULL values.
ON DELETE SET DEFAULT - The child row foreign key value will be set to the
default value if the parent row is deleted.
These ON DELETE clauses can also be applied when updating the parent table.
To do that, we use the ON UPDATE clause.
We’ll try some of these examples when we insert, update and delete data
from our co_employees and mentorships tables in the next chapter.
Unique Constraint
Next, let’s talk about the unique constraint.
Suppose for the mentorships table, we do not want the same mentor to
mentor a mentee more than once. For instance, we do not want the following
case:
mentor_id = 1
mentee_id = 2
project = 'SQF Limited'
mentor_id = 1
mentee_id = 2
project = 'Flynn Tech'
This will ensure that we cannot have more than one row with the same
mentor_id, mentee_id combination.
Some readers may notice that a UNIQUE constraint is very similar to a PRIMARY
KEY constraint. Indeed, a primary key is unique by definition.
However, one of the main differences between a primary key and a unique
key is that a table can only have one primary key but can have multiple
unique keys.
Named Constraints
CONSTRAINT name_of_constraint
For the UNIQUE constraint in the previous example, we could have named it
mm_constraint as shown below:
This will make it easier to refer to the constraint in future (such as when we
want to update or delete the constraint).
mentor_id: INT
mentee_id: INT
status: VARCHAR(255)
project: VARCHAR(255)
mentor_id and mentee_id are both foreign keys that reference the id column
in the co_employees table.
Next, we do not want the same mentor to mentor a mentee more than once.
Last but not least, we want the foreign keys and unique constraint to be
named.
Altering Tables
We’ve covered quite a bit in this chapter so far. To recap, we learned that to
create a table, we need to do two things:
1) Specify the columns by stating their names, data types and constraints (if
any)
2) Specify any table constraints that the table must fulfil
Now that we have created the two tables that we need, let’s move on to learn
how we can modify tables. This is useful if we need to make any changes to
our tables after creating them.
Table Names
The first thing we can modify is the table name. To do that, we use the syntax
followed by
When adding a column, we can specify the position of the new column by
adding the keyword FIRST after the column constraints to indicate that the
new column should be the first column.
Alternatively, we can also add the AFTER column_name keywords to insert the
new column after another column.
Let’s look at some examples now. We’ll modify the employees table first.
Suppose we want to do the following:
After modifying our table, we can check if our modifications are correct by
asking MySQL to describe our table.
DESCRIBE table_name;
DESCRIBE employees;
In order to modify the foreign key constraint, we have to first drop the
original foreign key using the statement below:
Next, using a new ALTER statement (we are not allowed to drop and add a
foreign key with the same name using a single ALTER statement), we add the
foreign key back with the modified conditions. In addition, we also drop the
mm_constraint constraint:
Add the two ALTER statements above to practice.sql and execute them one by
one to alter the mentorships table as required.
Deleting Tables
Last but not least, before we end this chapter, let’s learn to delete a table. To
do that, we use the syntax below:
Inserting Data
To insert data into MySQL, we use the following syntax:
Type the statement above into practice.sql and execute it to insert these
information into your employees table.
Notice that we did not input values for id and date_created. This is because
id is automatically generated by the system (AUTO_INCREMENT) while
date_created has a DEFAULT value provided by the NOW() function.
In addition, note that we need to use quotation marks for textual information
(e.g. 'James Lee', 'M', '516-514-6568' etc) but not for numerical
information (e.g. 11).
Next, let’s try inserting data into the mentorships table. Suppose we want to
insert the following data into the mentorships table, how can we do it?
Based on what we’ve learned so far, we can do it as follows:
However, there is a shorter way to do it. We can omit the column names as
shown below:
This second method works only if we are inserting data for all columns. In
addition, the values must be in the correct order. Else, the data will be
assigned to the wrong column.
Type either (but not both) of the INSERT statement above into practice.sql and
execute it. That’ll insert the necessary data into the mentorships table.
Updating Data
Next, let’s look at how we can update our data. To do that, we use the syntax
below:
UPDATE table_name
SET column1 = value1, column2 = value2, …
WHERE condition;
For instance, suppose we want to update the contact number of 'James Lee'
from '516-514-6568' to '516-514-1729', we use the code
UPDATE employees
SET contact_number = '516-514-1729'
WHERE id = 1;
Here, we use the id column to identify 'James Lee'. We can also use other
columns. For instance, we can write
WHERE years_in_company = 11
since there is only one employee with that number of years in the company.
However, if there is more than one employee with the same number of years,
we’ll end up updating the contact numbers of all such employees, which is
not what we want.
In addition, if we omit the WHERE clause, we’ll end up updating the contact
numbers of ALL employees, which is also not what we want.
Deleting Data
Finally, let’s look at how we can delete data from a table. To do that, we use
the syntax below:
For instance, to delete 'Li Xiao Ting' (id = 5) from the employees table,
we use the following code:
Try it yourself. You should get a green tick with the message
1 row(s) affected
Constraints
Now that we know how to insert, update and delete data from our tables, let’s
revisit some of the constraints that we placed on our tables when we created
them.
Remember that we have two foreign key constraints for the mentorships
table? Let’s see how they work.
right? This is due to the fact that we do not have an employee with id = 21
in the employees table. Hence, we cannot insert a row with mentee_id = 21
into the mentorships table as the mentee_id column is supposed to reference
the id column in the employees table.
Next, let’s try to update some data in the employees table. For instance, try
adding the code below to practice.sql and execute it.
UPDATE employees
SET id = 12
WHERE id = 1;
This is due to the ON UPDATE RESTRICT clause that we added to the first
foreign key (fk1) in the mentorships table. This foreign key links the
mentor_id column in the mentorships table with the id column in the
employees table.
We are not allowed to update the id column in the employees table for id =
1 as we have two rows in the mentorships table that reference it (the first and
second rows, with mentor_id = 1).
Clear? Good!
Let’s move on to the ON UPDATE CASCADE clause that we added to the second
foreign key (fk2).
UPDATE employees
SET id = 11
WHERE id = 4;
Recall that the second foreign key has a ON UPDATE CASCADE clause? This
means that any update in the parent table (employees) will lead to a
corresponding update in the child table (mentorships).
The SELECT keyword is used to retrieve information from tables. We’ll learn
more about that in the next three chapters. For now, try executing the
statements above. You should get the following outputs.
Note that as the date_created column in the employees table is provided by
the NOW() function, the values that you get for that column will be different
from those values shown above. The values that you get will be based on the
time you created the record.
As you can see, the id for Walker Welch in the employees table is updated
from 4 to 11. This update is cascaded to the mentorships table. The
mentee_id for the last row in the mentorships table is changed from 4 to 11
too.
Good!
Last but not least, let’s look at the ON DELETE CASCADE clause. We added this
clause to both our foreign keys.
Recall that previously, we deleted 'Li Xiao Ting' from the employees
table?
If you study the employees table above, you’ll notice that the row with id =
5 is missing.
In addition, if you compare the mentorships table above with the data that
we inserted into this table, you’ll see that the last row is deleted too.
This is due to the ON DELETE CASCADE clause that we added to our foreign
keys. The last row has mentee_id = 5. Hence, it is deleted from the
mentorships table (the child table) when the referenced row is deleted from
the parent table (the employees table).
Chapter 5: Selecting Data Part 1
In this chapter, we’ll learn to select data from our database. This is a
relatively large topic, so we’ll be splitting it into three chapters.
The data used for selection are from the two tables (employees and
mentorships) that we created in the previous chapters. These tables can be
found in Appendix A for easy reference.
In the first chapter, we’ll learn to select data from a single table.
SELECT column_names_or_other_information
[AS alias]
[ORDER BY column(s)] [DESC]
FROM table_name
[WHERE condition];
Selecting Everything
If we want to select all the columns and rows from a table, we write
For instance, as we saw in the previous chapter, to select all data from the
employees table, we write
In the statement above, we did not add a WHERE clause. Without the WHERE
clause, MySQL gives us all the rows in the table.
In addition, we used the * symbol to indicate that we want to select all
columns from the employees table.
Filtering Columns
If we do not want to select all columns, we can list the columns we want.
Suppose we only want to select the em_name and gender columns from the
employees table, we write
The table below shows part of the results from this SELECT statement:
Using Aliases
If you study the table above, you’ll notice that the table uses the column
names (“em_name” and “gender”) as its column headings.
In the statement above, note that we use the backtick character (normally
found at the top left corner of the keyboard, together with the tilde ~
character) to enclose the first alias “Employee Name”. This is necessary
when the alias consists of more than one word. Alternatively, we can also use
single or double quotes as shown below:
or
If you run any of the statements above, you’ll see the heading change from
“em_name” and “gender” to “Employee Name” and “Gender” respectively.
Filtering Rows
Next, let’s look at how we can filter rows when selecting data.
LIMIT
To do that, we can use the LIMIT keyword. This limits the number of rows
retrieved by the SELECT statement. For instance, if we write
we’ll get
Only the first three rows are displayed.
DISTINCT
Another way to filter rows is to remove duplicates. This can be achieved
using the DISTINCT keyword.
If we write
we’ll get
M
M
F
F
M
F
M
F
M
as the output.
This means that the first two rows in our table have gender = M while the
third and fourth rows have gender = F etc.
M
F
as the output.
WHERE clause
Next, let’s look at how we can use the WHERE clause to filter results.
We’ve already seen the WHERE clause in the previous chapter when we used
the statement
UPDATE employees
SET contact_number = '516-514-1729'
WHERE id = 1;
In that example, we used the equality (=) operator in the WHERE clause.
Besides the equality operator, there are other operators and keywords that we
can use:
Comparison
Not Equal (!=), Greater than (>), Greater than or equal to (>=), Smaller than
(<), Smaller than or equal to (<=)
For instance, if we want to select all rows from the employees table whose id
is not equal to 1, we write
If we want to select rows with values between two numbers, we can use the
BETWEEN keyword. For instance, to select rows with id between 1 (inclusive)
and 3 (inclusive), we write
Like
If you run the statement above, you’ll get the records of Larry Zucker and
Serena Parker. This is because both names end with ‘er’.
If we want to select employees whose names have 'er' anywhere within (not
necessarily at the back), we can use the following statement:
We add the % symbol in front of and behind ‘er’ to indicate that there can be
any number of characters before and after it.
If you run the statement above, you’ll get the records of all 5 employees with
‘er’ in their names (Peter Pasternak, Jason Cerrone, Larry Zucker,
Serena Parker and Walker Welch).
Besides the % symbol, MySQL also provides us with the _ symbol.
Suppose we want to select the rows of all employees that have ‘e’ as the fifth
letter in their names, we write:
Here, we use FOUR _ symbols to indicate that there are four characters
before ‘e’. This will give us the records of Joyce Jones, Prudence Phelps
and Walker Welch.
In
This keyword is used to select rows with column values inside a certain list.
Suppose we want to select rows that have id 6, 7, or 9, we can write
Not in
Note that in both examples above, we need to enclose the ids inside a pair of
parentheses.
And, Or
Finally, let’s look at the AND and OR keywords. These keywords are used to
combine conditions in the WHERE clause.
The AND keyword gives us rows that satisfy ALL the conditions listed while
the OR keyword selects rows that satisfy at least one of the conditions.
For instance, if we want to select all female employees who have worked
more than 5 years in the company or have salaries above 5000, we can write:
Clara Couto is selected even though her income is only 3900. This is
because she has worked 8 years in the company. Hence, she satisfies the first
of the two OR conditions inside the parentheses (years_in_company > 5 OR
salary > 5000).
On the other hand, if you study the employees table, you’ll see that some
employees are not selected even though they also satisfy at least one OR
condition in the parentheses.
For instance, James Lee has worked 11 years in the company but is not
selected.
This is due to the AND keyword that requires James Lee to not only satisfy the
gender = 'F'
Clear?
Subqueries
Next, let’s look at subqueries.
Subqueries are commonly used to filter the results of one table based on the
results of a query on another table.
For instance, in our example, suppose we want to select the names of all
employees that are mentors of the 'SQF Limited' project.
We can select them using their ids. If we know that employee 1, 2 and 3 are
mentors of this project, we can use the following SELECT statement:
What we can do is use a subquery in the WHERE clause to get their ids first.
This can be achieved with the statement below:
(1, 2, 3)
with
This query is known as a subquery. We use this subquery to get the ids from
the mentorships table first. We then use this result in the WHERE clause of the
main query to get the em_name column values from the employees table.
If you run the main query (in bold) above, you’ll get James Lee, Peter
Pasternak and Clara Couto as the result.
Sorting Rows
Now that we know how to select data from our tables and filter the results,
let’s cover one last concept before we end this chapter. Let’s look at how we
can sort the results returned by the SELECT statement.
To do that, we use the ORDER BY clause. We can choose to sort the results
based on one or more columns.
Suppose we want to sort the rows of the employees table using gender,
followed by the employee’s name (em_name), we write
Within each gender, the records will be sorted by the employees’ names.
In this chapter, we’ll learn to use built-in functions in our SELECT statements.
What is a Function?
First off, what is a function?
A function is a block of code that does a certain job for us. For an analogy,
think of the mathematical functions available in MS Excel. To add numbers,
we can use the sum() function and type sum(A1:A5) instead of typing
A1+A2+A3+A4+A5.
MySQL Functions
The first two functions that we’ll look at are for working with strings.
CONCAT()
The first is the CONCAT() function. This function allows us to combine two or
more strings into a single string. This is known as concatenating the strings.
Besides using the SELECT keyword to select information from tables, we can
also use it to display messages.
You’ll get
Hello World
as the output.
SUBSTRING()
For instance,
rogramming
as we extract the substring starting from position 2 to the end of the string.
gives us
rogram
NOW()
One such function is the NOW() function that we learned in Chapter 3. This
function gives us the current date and time whenever that function is being
used. It is commonly used to record the date and time that a particular record
is inserted into a table.
CURDATE()
Next, we have the CURDATE() function. This gives us the current date.
For instance,
SELECT CURDATE();
gives me
2018-08-28
at the time of writing.
CURTIME()
Finally, we have the CURTIME() function. This gives us the current time.
Aggregate Functions
Besides the functions mentioned above, MySQL also comes with a large
number of pre-written aggregate functions.
Let’s look at some of these functions and apply them on our employees table.
The calculations below are based on the employees table found in Appendix
A.
COUNT()
The COUNT() function returns the number of the rows in the table.
If we pass in * to the function, it returns the total number of rows in the table.
If we pass in a column name instead, it returns the number of non NULL values
in that column (NULL values are ignored).
Result:
9
Example 2:
SELECT COUNT(contact_number) FROM employees;
Result:
8
The last row (Walker Welch) is excluded from the count as the value is NULL.
Example 3a:
SELECT COUNT(gender) FROM employees;
Result:
9
Example 3b:
SELECT COUNT(DISTINCT gender) FROM employees;
Result:
2
AVG()
Example:
SELECT AVG(salary) FROM employees;
Result:
6487.777777777777
If you want to format the output, you can use the ROUND() function. We need
to pass in two pieces of information to the ROUND() function - the number to
round off and the number of decimal places we want it rounded off to.
For instance,
gives us 1.235.
In our case, if we want to round off the result of the AVG() function to 2
decimal places, we write
MAX()
Example:
SELECT MAX(salary) FROM employees;
Result:
12000
MIN()
Example:
SELECT MIN(salary) FROM employees;
Result:
2500
SUM()
Result:
58390
GROUP BY
In the previous section, we learned to use aggregate functions to perform
calculations on our data. However, those calculations were performed on all
the values in the column. What if we are interested in the maximum salary of
males vs females?
HAVING
In addition to performing calculations on grouped data, we can also filter the
results of the grouped data. We do that using the HAVING clause.
Suppose we want to display rows from the previous table only when the
maximum salary is above 10000, we do that using the statement below:
In this chapter, we’ll learn to select and combine data from one or more
tables.
Joins
Let’s start with joins.
Like the name suggests, a join is used to join data from different tables based
on a related column between the tables.
SELECT [table_names.]columns_names_or_other_information
FROM
left_table
JOIN / INNER JOIN / LEFT JOIN / RIGHT JOIN
right_table
ON
left_table.column_name = right_table.column_name;
There are three main types of joins in mySQL: inner join, left join and right
join. These are represented by the Venn diagrams below:
To demonstrate the difference between them, let’s consider the following two
tables:
Here, we have two tables called one and two.
You can see that column A in table one shares some common values with
column C in table two. We can join the two tables using these two columns.
Firstly, in the statement above, table one is the left table while table two is
the right table. The left table refers to the first table mentioned after the FROM
keyword.
Thirdly, for some of the columns (specifically column B), we need to prefix
the column name with a table name (such as one.B, two.B etc). This is done
to avoid ambiguity as there is a column B in both tables one and two. If we do
not include the table name, mySQL will not know which table we want to get
the values from. If we want, we can also prefix the other columns with their
table names, such as one.A and two.C. However, that is optional as columns
A and C are not ambiguous.
Lastly, an inner join is the default join. In the statement above, we could have
omitted the INNER keyword.
If you create the two tables yourself and run the SELECT statement above,
you’ll get the following output:
If you study the output carefully, you’ll notice that an inner join selects rows
where a common value exists for both tables. Hence, only A=2 and A=3 are
selected from table one as these two values (2 and 3) exist in column C of
table two as well.
Clear?
Good!
A left join selects all rows from the left table as shown in the output above.
As the two tables are joined using the condition A = C and table two (the
right table) does not have a row with C = 1, the values for table two are
displayed as NULL when A=1 (refer to the last row in the results table above).
Finally, let’s do a right join. A right join selects all rows from the right table.
To do a right join, we replace LEFT JOIN with RIGHT JOIN in the SQL
statement. This will give us the following output:
As table one (the left table) does not have a row with A = 4, the values for
table one are displayed as NULL for that row.
Clear?
Great!
Now, let’s look at an example using our employees and mentorships tables.
In the example above, if you do not want the id and mentor_id columns to
show, you can use the code below:
You do not have to select the columns that you use for joining. The statement
above gives us the same rows but without the id and mentor_id columns.
Unions
Now that we understand how joins work, let’s move on to unions. This is a
relatively easy concept.
The UNION keyword is used to combine the results of two or more SELECT
statements. Each SELECT statement must have the same number of columns.
The syntax is:
SELECT_statement_one
UNION
SELECT_statement_two;
The column names from the first SELECT statement will be used as the
column names for the results returned.
Note that by default, the UNION keyword removes any duplicates from the
result. If you do not want that to happen, you can use the UNION ALL
keywords.
In this chapter, we’ll learn another concept that is closely related to SELECT
statements - the concept of views.
What is a view?
Simply stated, an SQL view is a virtual table.
In contrast to actual tables, views do not contain data. Instead, they contain
SELECT statements. The data to display is retrieved using those SELECT
statements.
In addition, views also allow us to restrict access to certain data in our tables.
For instance, suppose we have a table with three columns - id, password and
email. If we do not want other programmers to have access to all the data in
the tables (such as the password column), we can create a view with only the
id and email columns and let them have access to the view instead.
Creating a View
The syntax for creating a view is:
The code below shows how we can create a view for that SELECT statement:
The only additional code is the first line (CREATE VIEW myView AS).
To use this view, we need to first execute the CREATE VIEW statement above
to create the view.
Next, we can select data from it like how we select data from a table.
If we only want the mentor_id and Project Name columns, we can write
Note that in the SELECT statement above, we have to enclose Project Name
using the backtick (`) character. If we use single or double quotes as shown
below:
Single or double quotes can only be used when naming the alias for the
column. For selecting the column, we have to use backticks.
Altering a View
After creating a view, if we want to make any changes to it, we use the ALTER
VIEW keywords.
The syntax is
This changes the alias for the mentorships.project column from Project
Name to Project (refer to underlined code).
Deleting a View
Last but not least, to delete a view, we use the DROP VIEW keywords. The
syntax is
What is a Trigger?
A trigger is a series of actions that is activated when a defined event occurs
for a specific table. This event can either be an INSERT, UPDATE or DELETE.
Triggers can be invoked before or after the event.
Suppose one of the employees has just resigned from the company and we
want to delete this employee from the employees table. However, before we
do that, we would like to transfer the data into another table called
ex_employees as a form of back up. We can do this using a trigger.
Creating a Trigger
Next, we’ll use the following syntax to create our trigger (line numbers are
added on the left for reference and are not part of the syntax):
1 DELIMITER $$
2
3 CREATE TRIGGER name_of_trigger BEFORE/AFTER
UPDATE/DELETE/INSERT ON name_of_table FOR EACH ROW
4
5 BEGIN
6 -- Actions to take
7 END $$
8
9 DELIMITER ;
This syntax may look overwhelming at first. Do not worry, we’ll go over
each keyword one by one.
The code on line 1 (DELIMITER $$) tells MySQL that we want to use $$ as
the delimiter for our CREATE TRIGGER statement (from lines 3 to 7).
The reason for this is that a trigger contains SQL statements within itself. We
use the ; character to signify the end of those SQL statements inside the
trigger and use the $$ characters to signify the end of the trigger itself.
Using $$ as the delimiter is necessary only for statements that contain SQL
statements within itself (such as the CREATE TRIGGER and CREATE PROCEDURE
statements that we'll cover in the next chapter).
Clear?
Finally, the keywords FOR EACH ROW is standard syntax to inform MySQL
that this trigger is to be activated for each of the rows affected by the UPDATE,
INSERT or DELETE event.
After line 3, we have the BEGIN and END $$ markers on lines 5 and 7. These
two markers mark the beginning and end of the trigger. Between these two
markers, we insert the SQL statements that define the actions to take.
DELIMITER $$
DELIMITER ;
Next, within the BEGIN and END $$ markers, we have an INSERT statement to
insert a new row into the ex_employees table. This INSERT statement should
be quite familiar to you, except for the OLD keyword.
For triggers activated by a DELETE event, we use the OLD keyword to retrieve
the deleted values (or values to be deleted).
For triggers activated by an INSERT event, we use the NEW keyword to retrieve
the inserted data (or data to be inserted).
For triggers activated by an UPDATE event, we use the OLD keyword to retrieve
the original data, and the NEW keyword to retrieve the updated data.
To get a feel of how triggers work, type the trigger above into practice.sql
and execute it. Next, execute the statements below:
We first delete employee 10 from the employees table. Next, we retrieve data
from both the employees and ex_employees tables.
You’ll see that employee 10 is removed from the employees table and a new
row is added to the ex_employees table automatically. Cool right?
Deleting a Trigger
Finally, let’s look at how we can delete an existing trigger.
In this chapter, we’ll cover something more advanced. Specifically, we’ll talk
about stored routines.
Variables
So what is a variable?
A variable is a name given to data that we need to store and use in our SQL
statements.
To do that, we can first declare and initialize a variable using the statement
below:
SET @em_id = 1;
Once the variable is declared and initialized, we can modify our SELECT
statements to make use of this variable:
In order to get the data for employee 1 now, we have to execute the SET
statement and the three SELECT statements.
SET @em_id = 2;
We can then execute this SET statement and re-execute the SELECT statements.
We’ll get the information for employee 2. There is no need to change any of
the SELECT statements.
Clear?
Variables make it very easy for us to reuse our SQL statements and are
extremely useful in stored routines. We’ll cover stored routines in the next
section.
In the first SET statement, we declare a variable called @price and initialize it
to 12.
In the second SET statement, we update the value of @price by adding 3 to it.
SET statements always work from right to left (i.e. the right side of the
statement is executed first).
Last but not least, we can assign the result of a function to a variable.
However, if you choose to do that, you have to use the := symbol to do the
assignment in the SELECT statement. The = symbol will not work correctly.
If you run the statement above, you’ll get 3 as the result too.
Stored Routines
Next, let’s move on to stored routines.
A stored routine is a set of SQL statements that are grouped, named and
stored together in the server. Do not worry if this does not make much sense
to you at the moment. We’ll discuss it in greater depth later.
There are two types of stored routines - stored procedures and stored
functions.
Stored Procedures
Let’s first look at stored procedures.
We can create a stored procedure using the syntax below:
DELIMITER $$
DELIMITER ;
Most of the syntax is pretty similar to the syntax for creating a trigger.
The main difference is, instead of using CREATE TRIGGER, we use CREATE
PROCEDURE to create the stored procedure.
For now, let’s first look at a simple example on creating a procedure without
parameters.
DELIMITER $$
DELIMITER ;
We first change the delimiter to $$. Next, we use the CREATE PROCEDURE
keywords to create the stored procedure.
After the BEGIN keyword, we add two SELECT statements to the stored
procedure. Next, we end the stored procedure with END $$.
Try typing the stored procedure into practice.sql yourself and execute it to
create the procedure. We always need to execute the CREATE PROCEDURE
statement to create our procedures before we can use them.
Next, let’s run this stored procedure. To do that, we use the CALL keyword:
CALL select_info();
If you execute the statement above, you’ll see the employees and
mentorships tables displayed in two separate tabs in the results grid.
Got it?
Great!
Suppose instead of selecting everything from the two tables, we only want to
select the records of a particular employee. We can create a stored procedure
as follows:
DELIMITER $$
CREATE PROCEDURE employee_info(IN p_em_id INT)
BEGIN
SELECT * FROM mentorships WHERE mentor_id = p_em_id;
SELECT * FROM mentorships WHERE mentee_id = p_em_id;
SELECT * FROM employees WHERE id = p_em_id;
END $$
DELIMITER ;
Most of the code should be familiar to you. The only exception is the code
inside the parentheses:
IN p_em_id INT
Here, we declare a variable called p_em_id. You may notice that we did not
prefix this variable with @.
There are three types of parameters for stored procedures: IN, OUT and INOUT.
An OUT parameter is used to get information from the stored procedure while
an INOUT parameter serves as both an IN and OUT parameter.
We’ll look at OUT and INOUT parameters in the next few examples.
CALL employee_info(1);
Here, we pass in the value 1 to the stored procedure. This tells MySQL that
the value of p_em_id should be replaced by 1 in all the SQL statements
within the stored procedure.
becomes
The three SELECT statements will then only select information for employee
1.
Clear?
Next, let’s look at an example for OUT parameters. Suppose we want to get the
name and gender of a particular employee. In addition, we want to store these
information into variables so that we can use them in our subsequent SQL
statements. Here’s how we can do it:
DELIMITER $$
DELIMITER ;
Here, we declare an IN parameter called p_em_id and two OUT parameters
called p_name and p_gender.
Inside the stored procedure, we use the INTO keyword to store the results
returned by the SQL statement into the OUT parameters.
Inside the parentheses, we pass the value 1 to the IN parameter and the
variables @v_name and @v_gender to the OUT parameters.
You may notice that we did not declare @v_name and @v_gender before
passing them to our stored procedure. This is allowed in MySQL. When we
pass in variables that have not been declared previously, MySQL will declare
the variables for us. Hence, there is no need for us to declare @v_name and
@v_gender before using them.
After we call the stored procedure, MySQL will store the result of the SELECT
statement (em_name and gender in this example) into the @v_name and
@v_gender variables. We can then use these variables in subsequent SQL
statements. For instance, if we run the previous CALL statement followed by
the SELECT statement below:
we’ll get the information of all male employees (since @v_gender = 'M' for
employee 1).
Last but not least, let’s look at an example of an INOUT parameter. Suppose
we want to get the mentor_id of a record based on its mentee_id and
project values, here's how we can do it:
DELIMITER $$
DELIMITER ;
SET @v_id = 3;
We can then pass this variable (@v_id) and the project name (e.g. 'Wayne
Fibre') to the stored procedure as shown below:
When we do that, the stored procedure executes the SELECT statement within
and updates the value of @v_id to 1 (which is the mentor_id returned by the
SELECT statement). If we want to view the value of @v_id after the procedure
is called, we use the following SELECT statement:
SELECT @v_id;
Stored Functions
Clear about stored procedures?
Great! Let’s move on to stored functions. Stored functions are very similar to
stored procedures except for some differences.
One of the key differences is that a stored function must return a value using
the RETURN keyword. We’ll learn how to do that later.
In addition, stored functions and stored procedures are executed differently.
Stored functions are executed using a SELECT statement while stored
procedures are executed using the CALL keyword.
DELIMITER $$
DELIMITER ;
The syntax may look confusing to you. Do not worry, let’s look at an
example to see how it works.
DELIMITER $$
After declaring the parameters, the two words RETURNS DOUBLE state that this
function returns a result that is of DOUBLE type.
Next, we specify the characteristics of the function. Here, we state that the
function is DETERMINISTIC.
DETERMINISTIC is a keyword that tells MySQL that the function will always
return the same result given the same input parameters.
On the other hand, if we state that the function is NOT DETERMINISTIC, we are
telling MySQL that the function may return a different result given the same
input parameters.
These include NO SQL (indicates that the function does not contain SQL
statements), READS SQL DATA (indicates that the function will only read data
from the database, but will not modify the data), MODIFIES SQL DATA
(indicates that the function may modify the data in the database) and
CONTAINS SQL (indicates that the function contains SQL instructions, but does
not contain statements that read or write data).
Here, the name of the variable is bonus and the data type is DOUBLE(8, 2).
We did not declare a default value for the variable. (Declaring a default value
initializes the variable to that value. We'll see an example of that in Chapter
12.)
RETURN bonus;
A function exits with a RETURN statement. Any task after the RETURN
statement is ignored.
As you can see, the code for creating a stored function is very similar to that
for creating a stored procedure, except for minor differences (like having to
return a result). However, the way to call a stored function is very different.
To call this function, we use it within a SELECT statement. For instance, we
can do the following:
Here, we pass in the column salary and the value 1.5 to the calculateBonus
function for the first and second parameters respectively. The fact that we did
not prefix salary with @ indicates that salary is a column and not a user
defined variable.
While MySQL allows us to alter our stored routines (using the ALTER
keyword), we can only make very limited modifications to it.
Hence, if we need to edit our stored routines, the easier way is actually to
delete and recreate them.
For instance, suppose we want to pay 2 months bonus for employees with
salary below 3000, but 1 month bonus for employees with salary above that?
In order to achieve the above, we need to use control flow tools. These
include IF, CASE, and LOOP statements.
Ready?
IF statement
Let’s start with the IF statement. The syntax for the IF statement is as
follows:
The IF statement first checks if the first condition is met. If it is, it’ll perform
task A. If it is not, it’ll move on to the first ELSEIF statement. If this condition
is met, it’ll perform task B. If it is not, it’ll move down the ELSEIF statements
until it finds a condition that is met. If no conditions are met, it’ll perform
task Z.
In addition, both the ELSEIF and ELSE statements are optional. You do not
need to include them if there are no other conditions to check.
However, if you omit the ELSE statement and there exists a case that is not
fulfilled by any of the IF and ELSEIF statements, MySQL will give you an
error.
Example 1
DELIMITER $$
CREATE FUNCTION if_demo_A(x INT) RETURNS VARCHAR(255)
DETERMINISTIC
BEGIN
END $$
DELIMITER ;
This IF statement first checks if the input (x) is greater than zero. If it is, it
returns 'x is positive'.
If both the first two conditions are not met, it moves on to the ELSE statement
and returns 'x is negative'.
SELECT if_demo_A(2);
We’ll get 'x is positive'.
If we change the input from 2 to 0 or -1, we’ll get 'x is zero' and 'x is
negative' respectively.
Example 2
DELIMITER $$
CREATE FUNCTION if_demo_B(x INT) RETURNS VARCHAR(255)
DETERMINISTIC
BEGIN
END $$
DELIMITER ;
This example is similar to the previous one except that we omitted the ELSE
statement. If we run the function now, we’ll get the following outputs:
SELECT if_demo_B(2);
Output:
x is positive
SELECT if_demo_B(-1);
Output:
Error Code: 1321. FUNCTION if_demo_B ended without RETURN
You can see that if we pass a value to the function that it is unable to process
(a negative number in this case), the function returns an error.
CASE statement
Next, let’s move on to CASE statements.
The CASE statement is very similar to the IF statement and can often be used
interchangeably. In most cases, choosing between IF and CASE is a matter of
personal preference.
CASE case_variable
WHEN value_1 THEN do task A;
WHEN value_2 THEN do task B;
...
ELSE do task Z;
END CASE;
Or
CASE
WHEN condition 1 is met THEN do task A;
WHEN condition 2 is met THEN do task B;
...
ELSE do task Z;
END CASE;
The first syntax allows you to match the value of a variable against a set of
distinct values. The second syntax allows you to perform more complex
matches such as matching using ranges.
Example 1
DELIMITER $$
CREATE FUNCTION case_demo_A(x INT) RETURNS VARCHAR(255)
DETERMINISTIC
BEGIN
CASE x
WHEN 1 THEN RETURN 'x is 1';
WHEN 2 THEN RETURN 'x is 2';
ELSE RETURN 'x is neither 1 nor 2';
END CASE;
END $$
DELIMITER ;
SELECT case_demo_A(1);
Output:
x is 1
SELECT case_demo_A(5);
Output:
x is neither 1 nor 2
Example 2
DELIMITER $$
CREATE FUNCTION case_demo_B(x INT) RETURNS VARCHAR(255)
DETERMINISTIC
BEGIN
CASE
WHEN x > 0 THEN RETURN 'x is positive';
WHEN x = 0 THEN RETURN 'x is zero';
ELSE RETURN 'x is negative';
END CASE;
END $$
DELIMITER ;
This second example uses the second syntax and tests x for a range instead of
a single value.
SELECT case_demo_B(1);
Output:
x is positive
SELECT case_demo_B(-1);
Output
x is negative
WHILE statement
The next control flow statement is the WHILE statement. This statement allows
us to specify a task to be done repeatedly while a certain condition is valid.
Example
DELIMITER $$
CREATE FUNCTION while_demo(x INT, y INT) RETURNS VARCHAR(255)
DETERMINISTIC
BEGIN
DECLARE z VARCHAR(255);
SET z = '';
RETURN z;
END $$
DELIMITER ;
Here, we first declare a local variable z and initialize it to an empty string (an
empty string is a string with no content).
Next, the WHILE condition checks if x is smaller than y. (Both x and y are
input parameters to the function.)
First, it increases x by 1.
Next, it uses the concat() function to concatenate z with the new value of x.
Finally, it assigns the result back to z.
Recall that concat() is a built-in MySQL function that joins two strings
together. If one (or both) of the values is a number (like in our example), it
converts them to strings before joining them together.
you’ll get
2345
as the output.
z becomes '23'.
REPEAT statement
Next, let’s look at the REPEAT statement. A REPEAT statement is also used to
perform repetitive tasks.
It repeatedly performs some tasks until the UNTIL condition is met. The
syntax of a REPEAT statement is:
Example
DELIMITER $$
CREATE FUNCTION repeat_demo(x INT, y INT) RETURNS VARCHAR(255)
DETERMINISTIC
BEGIN
DECLARE z VARCHAR(255);
SET z = '';
REPEAT
SET x = x + 1;
SET z = concat(z, x);
UNTIL x>=y
END REPEAT;
RETURN z;
END $$
DELIMITER ;
you’ll get 6 as the result because even though the REPEAT condition (UNTIL
x>=y) is already met, the two tasks inside the REPEAT statement are executed
at least once since the check is done after the tasks are completed.
Clear?
LOOP statement
Last but not least, let’s move on to the LOOP statement. This statement is very
similar to the WHILE and REPEAT statements, except that it does not come with
a condition to exit the loop.
Example 1
DELIMITER $$
CREATE FUNCTION loop_demo_A(x INT, y INT) RETURNS VARCHAR(255)
DETERMINISTIC
BEGIN
DECLARE z VARCHAR(255);
SET z = '';
simple_loop: LOOP
SET x = x + 1;
IF x > y THEN
LEAVE simple_loop;
END IF;
SET z = concat(z, x);
END LOOP;
RETURN z;
END $$
DELIMITER ;
LEAVE simple_loop;
If the LEAVE condition is not met, we remain in the loop and concatenate z
with x.
as the output.
Example 2
Besides using the LEAVE keyword, we can also use the ITERATE keyword. In
contrast to the LEAVE keyword that exits the loop completely, the ITERATE
keyword only skips one iteration of the loop.
DELIMITER $$
CREATE FUNCTION loop_demo_B(x INT, y INT) RETURNS VARCHAR(255)
DETERMINISTIC
BEGIN
DECLARE z VARCHAR(255);
SET z = '';
simple_loop: LOOP
SET x = x + 1;
IF x = 3 THEN ITERATE simple_loop;
ELSEIF x > y THEN LEAVE simple_loop;
END IF;
SET z = concat(z, x);
END LOOP;
RETURN z;
END $$
DELIMITER ;
you’ll get
245
as the output.
Clear?
Good!
We’ll be exploring one of the major uses of the LOOP statement in the next
chapter.
Chapter 12: Cursors
Cool! We’ve come to the final chapter before the project.
In this chapter, we are going to look at a major use of the LOOP statement that
we learned in the previous chapter.
What is a Cursor?
First off, what is a cursor?
After declaring the cursor, we also need to declare a handler that defines what
the cursor should do when it reaches the last row of the results returned by
the SELECT statement. Normally, we want the cursor to set a variable to a
certain value. For instance, we may want it to set a variable called v_done to
1.
The syntax is:
OPEN cursor_name;
Once the cursor is open, we can use a loop to step through the rows. Within
the loop, we need to retrieve the row that the cursor is currently pointing at
and store the data into variables. The syntax for retrieving the row is:
After the row is retrieved, the cursor will automatically move to the next row.
The LOOP statement will repeatedly fetch each row into the variables and
process those variables until it reaches the end of the results.
When that happens, we leave the loop and close the cursor. The syntax for
closing the cursor is:
CLOSE cursor_name;
Clear so far?
Example
Let’s look at an example:
Suppose we want to get the names and genders of all employees in our
employees table and combine them into a single line of text, here’s how we
do it:
DELIMITER $$
CREATE FUNCTION get_employees () RETURNS VARCHAR(255)
DETERMINISTIC
BEGIN
DECLARE v_employees VARCHAR(255) DEFAULT '';
DECLARE v_name VARCHAR(255);
DECLARE v_gender CHAR(1);
DECLARE v_done INT DEFAULT 0;
OPEN cur;
employees_loop: LOOP
FETCH cur INTO v_name, v_gender;
IF v_done = 1 THEN LEAVE employees_loop;
ELSE SET v_employees = concat(v_employees, ', ',
v_name, ': ', v_gender);
END IF;
END LOOP;
CLOSE cur;
Next, we declare a cursor called cur for the following SELECT statement:
SELECT em_name, gender FROM employees;
After that, we open the cursor and declare a loop called employees_loop to
work with the cursor.
Within the loop, we fetch the row that cur is currently pointing at and store
the results into the v_name and v_gender variables. We need to store the
results into two variables as we selected two columns from the employees
table.
v_done equals 1 when the cursor reaches the end of the table (i.e. when there
is no more data for the cursor to fetch).
If v_done equals 1, we exit the loop. Else, we use the concat() function to
concatenate v_employees with v_name and v_gender, separated by a colon
and comma.
, James Lee: M
as James Lee and M is the name and gender of the first employee.
The LOOP statement then repeats the same process for all the other rows in the
table.
After looping, we close the cursor and return the value of v_employees.
However, before we do that, we use the substring() function to remove the
first comma from the string.
SELECT get_employees();
you’ll get
James Lee: M, Peter Pasternak: M, Clara Couto: F, Joyce Jones: F,
Jason Cerrone: M, Prudence Phelps: F, Larry Zucker: M, Walker
Welch: M
as the result.
Chapter 13: Project
Great! We’ve covered all the fundamental concepts of MySQL. We are now
ready to start working on our project. You are strongly encouraged to work
through this project to help you gain a stronger grasp of the concepts covered.
Only registered users are allowed to make a booking. After booking, the
complex allows users to cancel their bookings latest by the day prior to the
booked date. Cancellation is free. However, if this is the third (or more)
consecutive cancellations, the complex imposes a $10 fine.
Tables
members
pending_terminations
rooms
bookings
View
member_bookings
Stored Procedures
insert_new_member
delete_member
update_member_password
update_member_email
make_booking
update_payment
view_bookings
search_room
cancel_booking
Trigger
payment_check
Stored Function
check_cancellation
Launch MySQL Workbench and create a new file by clicking on File > New
Query Tab. Save this file as sportsDB.sql (File > Save Script As... ).
In addition, you can also create another SQL file called drop.sql. If you make
any mistake in your SQL code and need to make amendments, you can drop
the object that you created using the drop.sql file.
After deleting, you can return to sportsDB.sql to recreate the database. Clear?
Adding Tables
Now, we are ready to add tables to our database. We need to add four tables:
members, pending_terminations, rooms and bookings.
members
id
This column stores the id of each member. The id is alphanumeric
(VARCHAR(255) will be a good choice) and uniquely identifies each member
(in other words, it is a primary key).
password
This column stores the password of each member. It is alphanumeric and
cannot be null.
email
This column stores the email of each member. It is also alphanumeric and
cannot be null.
member_since
This column stores the timestamp (consisting of the date and time) that a
particular member is added to the table. It cannot be null and uses the NOW()
function to get the current date and time as the DEFAULT value.
payment_due
This column stores the amount of balance that a member has to pay. The
amount is in dollars and cents (e.g. 12.50). The column cannot be null and
has a default value of 0.
Try creating this table yourself. You’ll need to decide on the appropriate data
type to use for each column and add the necessary restrictions (e.g. NOT
NULL) yourself. You can refer to Chapter 3 for more information on creating
tables.
Done?
The suggested code for this project can be found in Appendix C for reference
if you run into any problems.
pending_terminations
The data types and constraints of the id, password and payment_due columns
match that of the same columns in the members table.
rooms
Now, let’s move on to the rooms table. This table has three columns:
id
This column stores the id of each room. It is alphanumeric and uniquely
identifies each room.
room_type
This column stores a short description of each room. It is also alphanumeric
and cannot be null.
price
This column stores the price of each room. Prices are stored up to 2 decimal
places. It cannot be null.
Once you are done, we can move on to the hardest table - the bookings table.
bookings
id
This column stores the id of each booking. It is numeric, auto incremented
and uniquely identifies each booking.
room_id
This column has the same data type as the id column in the rooms table and
cannot be null.
booked_date
This column stores a date in the YYYY-MM-DD format (e.g. '2017-10-18') and
cannot be null.
booked_time
This column stores time in the HH:MM:SS format and cannot be null.
member_id
This column has the same data type as the id column in the members table
and cannot be null.
datetime_of_booking
This column stores the timestamp that a particular booking is added to the
table. It cannot be null and uses the NOW() function to get the current date and
time as the DEFAULT value.
payment_status
This column stores the status of the booking. It is alphanumeric, cannot be
null and has a default value of 'Unpaid'.
Besides the 7 columns stated above, the bookings table also has a UNIQUE
constraint called uc1. This constraint states that the room_id, booked_date
and booked_time columns must be unique. In other words, if one row has the
values
The first foreign key is called fk1 and links the member_id column with the
id column in the members table. In addition, if the record in the parent table
is updated or deleted, the record in the child table will also be updated or
deleted accordingly.
The second foreign key is called fk2 and links the room_id column with the
id column in the rooms table. If the record in the parent table is updated or
deleted, the record in the child table will also be updated or deleted
accordingly.
Try updating the bookings table to add these two foreign keys.
Inserting Data
Now, we need to add some values to our tables so that we have data to work
with in the project later.
members
Try modifying the code above to insert all the rows yourself.
Once you are done, you can move on to the rooms and bookings tables.
rooms
bookings
Try inserting data for these two tables yourself. These tables can be found in
Appendix B for easy reference.
Done? Good!
View
Now that we have created the tables and inserted some data, we are ready to
select data from our tables.
Specifically, we’ll create a view that shows us all the booking details of a
booking.
If you refer to the bookings table created previously, you can see that it lists
the id, room_id, booked_date, booked_time, member_id,
datetime_of_booking and payment_status of each booking.
What if in addition to the information above, we are also interested to know
what each of the room ids stand for? For instance, we may want to know
what AR stands for. In addition, what if we also want to know the price of
each room?
The room_type and price columns are from the rooms table while the
remaining columns are from the bookings table.
In order to combine these two tables, we need a SELECT statement that joins
the rooms and bookings tables. In addition, we also want to sort the results
by the id column of the bookings table.
Hint: You need to join the two tables using bookings.room_id = rooms.id.
You can refer to Chapter 7 for reference on doing joins in SELECT statements.
Clear?
If you execute the SELECT statement, you should get the following table:
Got it? Good!
Now, let’s create a view for this SELECT statement. We’ll call this view
member_bookings. Try doing this yourself. You can refer to Chapter 8 for
reference on creating views.
Once you are done, we can move on to create some stored procedures for our
database.
Stored Procedures
In this exercise, we will create a total of nine stored procedures. Before we
start coding them, let’s first change the delimiter by adding the line
DELIMITER $$
We’ll code all the stored procedures after this line; there is no need to change
the delimiter back to a semi-colon after each procedure. We’ll change the
delimiter back after we finish coding all the procedures and functions.
Ready?
insert_new_member
The first stored procedure is for inserting a new member into the members
table.
If you study the structure of the members table, you can see that it has a total
of 5 columns: id, password, email, member_since and payment_due.
As the last two columns have default values, we only need to provide values
for the first three columns when inserting a new member.
Next, let’s add the BEGIN and END $$ markers to our stored procedure to
define the start and end of this procedure.
Between the BEGIN and END $$ markers, we need to add an INSERT statement
to insert the values of p_id, p_password and p_email to our table. These
values will be inserted into the id, password and email columns of the
members table respectively.
A bit lost? Let’s start with a hint for the first stored procedure.
Suppose we have a table called demo_table and we want to insert the value
of a parameter called p_age to the age column of the table. Here’s how we do
it:
If you need help, you can refer to Chapter 4 and 10 for reference on writing
an INSERT statement and a stored procedure respectively.
Once you have coded the stored procedure, you can place your cursor
anywhere between the BEGIN and END $$ markers and click on the ‘Execute
Statement’ button to execute this procedure. (Remember to do so for all
subsequent stored procedures and functions.)
If all goes well, you should get a green tick in the output window.
delete_member
This stored procedure only has one IN parameter, p_id. Its data type matches
that of the id column in the members table.
Within the procedure, we have a DELETE statement that deletes the member
whose id equals p_id. Try coding this procedure yourself.
Next, we’ll code two stored procedures to help us update data in the
members table.
Both procedures use the UPDATE statement to update the password and email
of a member with id = p_id. Try coding them yourself.
make_booking
The data types of the parameters match the data types of the room_id,
booked_date, booked_time and member_id columns of the bookings table.
Within the procedure, we first declare two local variables v_price and
v_payment_due. The data type of v_price matches the data type of the price
column in the rooms table while that of v_payment_due matches the data
type of the payment_due column in the members table.
This statement selects the price of the room with id = p_room_id. This price
is then stored into the local variable v_price using the INTO keyword.
To do that, we need to first get the payment_due value for the member
making the booking. We get that from the members table (WHERE id =
p_member_id) and store the information into the v_payment_due variable.
Next, we update the members table and set the payment_due column to
v_payment_due + v_price for this particular member (WHERE id =
p_member_id).
update_payment
This procedure is for updating the bookings and members tables after a
member makes payment for his/her booking.
After updating the bookings table, the members table will also be updated to
reflect the new amount of money (if any) the member has to pay.
The data types of v_member_id and v_payment_due match the data types of
the id and payment_due columns in the members table while the data type of
v_price matches the data type of the price column in the rooms table.
To do that, we need to first select the member_id and price columns from the
member_bookings view for this particular booking (WHERE id = p_id) and
store the information into the v_member_id and v_price variables
respectively.
After gathering the information that we need, we are now ready to update the
members table. We’ll use the UPDATE statement to set the payment_due
column to v_payment_due - v_price for the member who made the booking
(WHERE id = v_member_id).
Try coding this UPDATE statement yourself. Once that is done, the procedure is
complete.
view_bookings
search_room
Now, in trial.sql, try writing a SELECT statement to select all the information
from the bookings table and execute that statement.
Done? Good.
Now, let’s do some filtering. Suppose we are only interested in bookings
where
Try to modify your SELECT statement such that it only shows these rooms.
Finally, modify the statement such that only the room_id column is
displayed.
If you execute the statement, you should just get AR as the result.
Got it? Now we have the ids of all the rooms that have been booked on 2017-
12-26 at 1pm and have not been cancelled.
Try to write a SELECT statement to achieve that. (Hint: You need to use the
previous SELECT statement as a subquery.)
For this stored procedure, you simply need to paste the previous SELECT
statement into the procedure (between the BEGIN and END $$ markers) and
change
With that, the stored procedure is complete. You can refer to the suggested
solution in Appendix C if you are stuck.
Once you are done, we can move on to the most complicated procedure.
cancel_booking
In addition, the data type of v_price matches the data type of the price
column in the rooms table and the data type of v_payment_due matches the
data type of the payment_due column in the members table.
Next, let’s set the value of v_cancellation to 0 using the SET keyword.
Done?
The sports complex allows members to cancel their bookings latest by the
day prior to the booked date.
For instance, if the booked date is 17th Sep 2018 and the current date is 16th
Sep 2018, members will be allowed to cancel their booking. However, if the
current date is 17th Sep 2018 or later, members will not be allowed to cancel
the booking.
In addition, members are not allowed to cancel bookings that have already
been cancelled or paid for.
In the IF clause, we first use the built-in CURDATE() function to get the
current date.
If the current date is greater than or equal to the booked date, we use a
SELECT statement to store the message
Finally, we proceed to the ELSE clause. This is where we handle the actual
cancellation.
Clear? Good!
Now that we are clear about the IF statement, let’s work on the cancellation
code for the ELSE clause. You can replace the comment
Next, we need to calculate the new amount that the member who made this
booking has to pay and update the payment_due column for this member in
the members table.
Finally, we need to store the message 'Booking Cancelled' into the OUT
parameter to indicate that the booking has been cancelled.
Step 1
Step 2
Next, we need to calculate how much the member owes the sports complex
now.
As the booking has been cancelled, the member no longer needs to pay for
the booking. Hence, we need to first set the value of v_payment_due to
v_payment_due - v_price using a SET statement.
Done?
v_payment_due now stores the final updated amount that the member has to
pay the complex.
Step 3
For the last step, we simply need to use a SELECT statement to store the
message 'Booking Cancelled' into the OUT parameter.
Try doing it yourself. Once you are done, the ELSE clause is complete and so
is the stored procedure.
Trigger
Now that we have finished coding our stored procedures, let’s move on to
triggers. We’ll only be coding one trigger - payment_check.
As you can see, this trigger is activated when we try to delete a record from
the members table.
Next, between the BEGIN and END $$ markers, we need to do a few things:
Next, we need to select the payment_due column from the members table for
the member that we are trying to delete and store that data into the
v_payment_due variable. (Hint: As this trigger is activated by a DELETE event,
we need to use OLD.id to retrieve his/her id.)
Try coding this trigger yourself. You can refer to Chapter 9 for reference if
you have forgotten how to code a trigger.
Stored Function
The final thing that we need to code is the check_cancellation function.
Try declaring this function yourself. You can refer to Chapter 10 for
reference on declaring a function.
Within the function (between the BEGIN and END $$ markers), we need to use
a cursor to loop through the bookings table vertically. To begin, let’s first
declare three local variables called v_done, v_cancellation and
v_current_payment_status.
Next, using this member_id, we need to get the payment_status of all the
bookings made by this member.
Got it?
Once you are done, execute the statement. You should get the following
results:
Unpaid
Paid
Paid
With that, you can return to the sportsDB.sql fie and continue working on the
check_cancellation function.
Copy and paste the query you just wrote into the function and change the
WHERE clause in the subquery from
WHERE id = 5
to
WHERE id = p_booking_id
Done?
Once you have declared the cursor, you need to declare a CONTINUE HANDLER
for this cursor. This can be done using the statement below:
So far so good?
Now, we need to set the values of v_done and v_cancellation to 0 using the
SET keyword.
Once we have declared and set everything that we need, we are ready to start
looping through the payment_status column.
Within the loop, we need to use the FETCH statement to fetch the value that
the cursor is currently pointing at into the v_current_payment_status
variable.
After fetching the value, we’ll use an IF statement to check the values of
v_current_payment_status and v_done.
In addition, if v_done equals 1, we have come to the end of the result set from
the SELECT statement and can also leave the loop.
Once you are done with the IF clause, you can work on the ELSE clause.
Once you are done, you can end the IF statement, end the loop, close the
cursor and return the value of v_cancellation. Try doing these yourself.
Once that is complete, we have finished coding our stored routines. We can
now change the delimiter back to a semi-colon using the statement below:
DELIMITER ;
First, we’ll check if our tables are created correctly. Try executing the
following statements to check if the tables are correct:
members, bookings and rooms should contain the same information as the
respective tables shown in Appendix B.
You should get the table below. The new member added is shown on the first
row of the table.
Got it?
Now we’ll delete two members from this table. The two members that we’ll
delete are little31 and afeil. Note that little31 has an outstanding
payment of $10.
You should see that both afeil and little31 are deleted from the members
table. However, as little31 has an outstanding payment of $10, a new
record is added to the pending_terminations table. This is due to the
payment_check trigger that we wrote.
Next, let’s try updating a member’s password and email. Try executing the
following statements:
You should see the password and email address of noah51 updated to
18Oct1976 and noah51@hotmail.com respectively.
Next, we’ll test the update_payment procedure. Before we do that, let’s first
run the following two statements:
You should see that marvin1 has an outstanding payment of 10.00 in the
members table and an unpaid booking (id = 9) in the bookings table.
We’ll update the payment status (from 'Paid' to 'UnPaid') for this booking.
The payment_due column for marvin1 in the members table should now
show 0.00 and the payment_status column in the bookings table should be
updated to Paid.
Next, let’s try the search_room procedure. Try executing the following
statement:
CALL search_room('Archery Range', '2017-12-26', '13:00:00');
You should get two rows returned as both badminton courts (B1 and B2) are
available on 2018-04-15 at 2pm.
You should only get one row returned (B1). This is because B2 has already
been booked for the specified date and time (id = 10 in the bookings table).
Next, let’s try to make a booking. Try executing the following statement:
This is due to the unique key constraint (uc1) that we added to the bookings
table.
You should see two new bookings added to the bookings table. Take note of
the booking ids for these bookings. We’ll need them later.
If you study the code above, you may notice something new. For the two
CALL statements, instead of providing the make_booking procedure with a
date (such as '2017-12-26'), we provided it with
Here, we use the built-in CURDATE() function to get the current date. Next, we
use the INTERVAL keyword to add an interval of 2 weeks to the current date.
The code above allows us to make bookings two weeks from the current date.
For instance, if the current date is 1st Oct, the bookings will be for 15th Oct.
in the "Result Grid". This is because the booked date for booking 1 is 2017-
12-26, which is already over.
Next, replace *** in the statement below with the booking id of the new
booking made by noah51 and execute the two statements:
Booking Cancelled
Finally, execute the following statements, replacing ^^^ with the booking id
of the new booking made by macejkovic73:
Booking Cancelled
You should notice that the payment_due value of noah51 is 0 while that of
macejkovic73 is 10.
Clear?
I sincerely hope you’ve found this book useful and that you've enjoyed the
course.
If you run into any problems with the project, do not be discouraged. I
strongly encourage you to download the source code from
http://www.learncodingfast.com/sql and compare your code with the
suggested solution. Finding errors and amending your code is one of the best
way to learn.
mentorships
Appendix B: Tables for sportsDB
members
bookings
rooms
Appendix C: Suggested Solution for Project
sportsDB.sql
DELIMITER $$
test.sql