Академический Документы
Профессиональный Документы
Культура Документы
Andrew Johansen
Table of Contents
Introduction
Chapter 1: SQL The Basics
Chapter 2: The SQL Commands That You Can Use
Chapter 3: Data Types
Chapter 4: How to Manage Database Objects
Chapter 5: Database Normalization
Chapter 6: Data Manipulation
Chapter 7: How to Manage Database Transactions
Chapter 8: How to Get Excellent Results from Database
Queries
Chapter 9: Categorize Information Using Database Operators
Conclusion
Introduction
I want to thank you and congratulate you for getting my
book
SQL: The Ultimate Beginners Guide!
This book will teach you the basics of SQL and database
operations. Since SQL is a language used to manage
databases, you have to familiarize yourself with its basics
and nuances. Dont worry if you have never used SQL
before: this book will turn you from a beginner to an efficient
SQL-user.
This book will cover important topics about SQL. For
instance, a chapter focuses on the operators that you can use.
Another chapter, however, concentrates on giving you
accurate results from your database queries. Overall, youll
be an effective SQL user after reading this book.
Thanks again for getting a copy of this book, I hope you enjoy
it!
What Is SQL?
SQL stands for Structured Query Language. This is the basic
language used to interact with databases. The original
version was created by IBM during the 70s. During 1979,
right after IBM released the prototype, Relational Software
Inc. published the first SQL tool in the world. They called
this product ORACLE. This product became so successful
the entire company is now called Oracle Corporation. Today,
ORACLE is one of the leaders in database technologies.
You can pronounce SQL in two ways:
1. Read the letters one by one, as in S-Q-L
2. Read the letters as Sequel
Modern users prefer the second way of pronouncing the
name. Basically, you will use SQL to tell a database about
your needs and wants. This process is similar to ordering
food in a restaurant: you can utilize SQL to get the
information you need.
Databases
Simply put, databases are groups of information. Some
people consider databases as organized mechanisms that can
store information, through which users can access data
browser.
CREATE INDEX
Important Note: These commands (and the ones youll see
below) will be explained further in the succeeding
chapters.
Data Manipulation Language (also known as DML)
This is the aspect of SQL used to modify information
inside database objects. DML has three main
commands, namely:
UPDATE
INSERT
DELETE
Data Query Language (also called DQL) This is the
most powerful aspect of SQL, particularly when used
with modern database systems. DQL is composed of a
single command, which is:
SELECT
You can use this command to run queries for relational
databases. If you want to get detailed results, you may
add clauses and options to this command.
Data Control Language (also known as DCL) You
should use these SQL commands if you want control the
access rights for a database. DCL commands are often
used to generate database objects related to access
Important Notes:
Precision is the overall length of a numeric value.
For this value: (5.3), 5 is the precision. It is the length
assigned to a numeric value.
Scale refers to the total number of digits found on
the decimal points right side. In the previous example
(i.e. 5.3), 3 is the scale of the numeric value.
The Integers
Integers are numeric values that dont involve a decimal
point. That means integers are whole numbers (regardless if
they are negative or positive). Here are some examples of
valid integers:
2
0
-3
99
-199
200
The Floating-Point Decimals
A floating-point decimal is a decimal value whose scale and
precision have varying lengths. Virtually, floating-point
Year
Hour
Second
Minute
Month
The Literal Strings
Literal strings are series of characters (e.g. names, phone
numbers, etc.) that are specified by a program or database
user. In general, a literal string is composed of data with
similar characteristics. The value of the entire string is
identified. The columns value, on the other hand, is often
unknown since different values exist between data columns
and data rows.
When using literal strings, you dont really specify the data
types. You are just specifying the strings. The following list
shows some literal strings:
Morning
50000
50000
5.60
August 1, 1991
Alphanumeric strings require quotation marks (either single
manipulated.
Lets assume that you received login credentials (i.e.
username and password) from a database administrator. The
username is PERSON1. Lets say you accessed the database
and created a table named EMPLOYEES_TBL. In the
databases records, the files actual name is
PERSON1.EMPLOYEES_TBL. The tables schema name
is PERSON1, which is also the creator/owner of that table.
When accessing a schema that you own, you are not required
to use the schema name. That means you have two ways of
accessing the imaginary file given above. These are:
PERSON1.EMPLOYEES_TBL
EMPLOYEES_TBL
As you can see, the second option involves fewer characters.
This is the reason why schema owners prefer this method of
accessing their files. If other users want to view the file,
however, they must include the schema in their database
query.
The screenshot below shows two schemas within a database.
Rows
Rows are records of data within a table. For instance, a row
in a customer database table might hold the name, fax
number, and identification number of a certain customer.
Rows are composed of fields that hold information from a
single record in the table.
SQL Statement CREATE TABLE
CREATE TABLE is an SQL statement used to generate a
table. Even though you can create tables quickly and easily,
you should spend time and effort in planning the structures of
your new table. That means you have to do some research
and planning before issuing this SQL statement.
Here are some of the questions you should answer when
creating tables:
What kind of data am I working on?
What name should I choose for this table?
What column will form the main key?
What names should be assigned to the fields/columns?
What type of data can be assigned to those columns?
Which columns can be empty?
What is the maximum length for every column?
You can assign primary keys this way while creating a new
table. In this example, the tables primary key is an implicit
condition. As an alternative, you may specify primary keys as
explicit conditions while creating a table. Heres an
example:
In the example given above, the primary key is given after the
comma list.
If you need to form a primary key using multiple columns,
you use this method:
normalization:
To attain the first form, you should divide the data into
smaller units. Each unit must have a primary key and
free from redundant data. The large table transformed
into smaller tables, namely: PRODUCTS_TBL,
EMPLOYEE_TBL, and CUSTOMER_TBL. Often, the
primary keys are found in the initial column of each
table. For this example, these are: CUST_ID,
PROD_ID, and EMP_ID.
2. The Second Form The goal of this form is to find data
that is partially reliant on the primary keys. Then, this
data will be transferred onto a different table. The
image below explains this normal form:
With this syntax, you should specify each column in the list
named VALUES. As you can see, the values in this list are
separated by commas. You should use quotation marks to
enclose the values you want to insert, particularly if you are
working with date/time and character data types. You dont
have to use quotation marks for NULL values or numeric
data. Each column within the table should contain a value.
In the example below, you will insert a record into a table
named PRODUCTS_TBL.
The tables current structure:
For this example, you inserted three values into a table that
has three columns. The values you inserted follow the
arrangement of columns within the table. Two of the values
are enclosed with quotation marks because their columns are
of the character type. The final value (i.e. cost) is a number
data type: quotation marks are optional.
How to Insert Data into Specific Columns
You may insert data into certain columns. For example, lets
assume that you need to insert the values for your employee
except his pager number. In this case, you should determine a
VALUES list and a column list while running the INSERT
statement. Heres a screenshot of the values you may use:
This syntax has one SET and three columns: the columns are
separated by commas. In general, you should use a comma to
segregate different kinds of arguments.
Case Sensitivity
You should understand this concept completely if you want to
use SQL. Usually, SQL statements and clauses are not
sensitive to uppercase and lower case characters. That
means you can enter clauses and statements with the Caps
Lock on: it wont affect your SQL commands in any way.
However, case sensitivity becomes extremely important
when you are dealing with data objects. Most of the time,
data is stored using uppercase letters. This method helps
database users in maintaining the consistency of data.
For example, your database will be inconsistent if youll
enter data this way:
JOHN
John
John
If the data was stored as JOHN and you executed a query for
John, you wont get relevant output.
Chapter 9: Categorize
Information Using Database
Operators
Operators The Basics
Operators are reserved words or characters mainly used in
the WHERE clause of SQL statements. As their name
suggests, operators are used to perform operations (e.g.
comparisons and mathematical operations). Operators can
specify parameters for your SQL statements. Lastly, they can
connect multiple parameters within the same SQL statement.
This chapter will use the following operators:
Logical Operators
Comparison Operators
Arithmetic Operators
Operators for Negating Conditions
Lets discuss each operator type in detail:
Logical Operators
LIKE
Here, youll use wildcard operators to compare a value
against similar ones. You can combine LIKE with the
following wildcard operators:
EXISTS
You can use this operator to find data rows that meet your
chosen criteria. Heres an example:
BETWEEN
You can use BETWEEN to find values within a specific
range. Here, youll assign the maximum value and the
minimum value. You must include the maximum and minimum
values in your conditional set. Check this example:
IS NULL
You can use IS NULL to compare your chosen value with a
NULL one. For instance, you can identify the products that
dont have wheels by checking for NULL values in the
WHEEL column of your PRODUCTS_TBL table.
In the example below, you wont get a NULL value:
Comparison Operators
These operators can test single values within SQL
statements. This category is composed of <, >, <>, and =.
You can use these operators to test:
Non-equality
Equality
Greater-than values
Less-than values
Non-equality
As an SQL user, you should use <> to test non-equality.
The operation gives TRUE if the data is not equal; FALSE if
the data is equal.
Important Note: You may also use the != operator.
Actually, many SQL implementations are using this operator
to test inequality. Check the implementation you are using to
find out more about this topic.
Equality
You can use this operator to test single values in your SQL
statements. Obviously, = (i.e. the equal sign) represents
equality. When checking for equality, you will only get data if
the chosen values are identical. If the values are equal, youll
get TRUE as the result. If the values arent equal, youll get
FALSE.
Greater-than, Less-than
In general, < and > can serve as stand-alone operators.
However, you can improve the effectiveness of your
operations if youll combine them with other operators.
Addition
You can perform addition using + (i.e. the plus sign). Study
the following SQL statements:
SELECT MATERIALS + OVERHEAD FROM
PRODUCTION_COST_TBL In this SQL statement, youll
add up the values in the MATERIALS column and the
OVERHEAD column.
SELECT MATERIALS FROM PRODUCTION_COST_TBL
WHERE MATERIALS + OVERHEAD < 500 This
operation will return values where the sum of MATERIALS
and OVERHEAD is less than 500.
Subtraction
You can use - (i.e. the minus sign) to perform subtraction.
To help you understand this process, two examples are given
below:
SELECT SALES COST FROM
COMPANY_FINANCIALS_TBL For this SQL statement,
the COST column will be deducted from the SALES column.
SELECT SALES FROM COMPANY_FINANCIALS_TBL
WHERE SALES COST < 100000 This statement will
give you values where SALES minus COST is less than
100,000.
Multiplication
Result
12
(2 + 2) * 5
20 8 / 4 + 2
(20 8) / (4 + 2)
20
20
2
Result
8 * 12 / 4
(8 * 12) / 4
8 * (12/4)
24
24
24
third positions.
WHERE PRICE NOT LIKE 1_%_% This statement
WILL NOT find values that begin with 1 and are three
characters long.
IS NOT NULL
You can use IS NOT NULL operator to negate IS NULL. This
procedure is usually done to check for data that isnt NULL.
Heres an example:
WHERE PRICE IS NOT NULL This operation will return
price values that are not null.
NOT EXISTS
This operator can help you negate EXIST. Study the example
below:
a few options for data type, but for this example we'll just go
with the TEXT data type. In the next chapter, we'll have a
more in-depth discussion about data types in SQL. Now if
you typed everything correctly, your code should now look
something like our example below:
1 /** Shopping list:
2 Blouse (4)
3 Pants (1)
4 Underwear (2)
6 **/
7
8 CREATE TABLE shopping_list (name TEXT);
If you're using SQLite to follow along, you will see that our
new table is now listed with one column at the ride hand
side. Keep in mind that, for our list, we also need to specify
how many of each thing to buy, like our four blouses. So let's
go ahead and add a quantity column as well. Keep in mind
that the values in the quantity column will always be a whole
number. So, let's use an integer for the data type. Your code
should now look something like the example below:
1 /** Shopping list:
2 Blouse (4)
3 Pants (1)
4 Underwear (2)
6 **/
7
8 CREATE TABLE shopping_list (name TEXT, quantity
INTEGER);
And now, looking at the right hand side if you're using
SQLite, you can see the new column listed in our table. At
this point, our database now looks pretty good. But we're
missing something that we need in databases, and that is a
unique identifier for each row.
In SQL, we almost always need unique IDs for each row in a
database. Why? Because we need a way to identify rows
later when we're updating or deleting them. Under no
circumstances should an SQL programmer be dependent on
other columns for row identification. This is because the
values within those rows could change anytime.
When creating columns in SQL, we typically specify the ID
column first. So go ahead and move your cursor before the
"name" column. We'll call our ID column "ID," which is
standard. And for the data type, we'll have to type the SQL
constraint "INTEGER PRIMARY KEY." This signals the
database that it should treat it as the row identifier, and that
each row must have a unique numeric value for this column.
Generally in SQL, constraints define rules in a database table
data. The "INTERGER PRIMARY KEY" constraint
specifically just says that a constraint should be a number,
not a zero value, and that it must be unique. Your code should
now look something like the example below:
1 /** Shopping list:
2 Blouse (4)
3 Pants (1)
4 Underwear (2)
6 **/
7
8 CREATE TABLE shopping_list (id INTEGER
PRIMARY KEY, name TEXT, quantity INTEGER);
Now, we have our shopping_list table with three columns in
it. However, at this point, it's empty. Our next task would be
to put some data in it. On a new line, we'll write "INSERT
INTO" and then the table name "shopping_list," and then
"VALUES", and then followed by an opening parenthesis.
After the opening parenthesis, we now start listing the
column values in the order that we declared the columns.
The first column is "id," so we'll put "1" for it since we
haven't used it yet. The second column is name, so we'll
write "Blouse" since its one of the items on our list. Then
third column is quantity, so we'll write the number "4" since
the quantity that we declared for Blouse in our list is four.
After that, go ahead and type a closing parenthesis, and then
followed by a semicolon. Your code should now look like
the one below:
2 Blouse (4)
3 Pants (1)
4 Underwear (2)
6 **/
7
8 CREATE TABLE shopping_list (id INTEGER
PRIMARY KEY, name TEXT, quantity INTEGER);
9
10 INSERT INTO shopping_list VALUES (1, "Blouse",
4);
11 INSERT INTO shopping_list VALUES (2, "Pants",
1);
12 INSERT INTO shopping_list VALUES (3,
"Underwear", 2);
It will now say that it has three rows in SQLite. However, to
confirm that the database actually contains data, you have to
either click on the table name on the right, or type the line of
code below:
13 SELECT * FROM shopping_list;
Doing this will insert a select statement in your code that
would display all the data within the table. Your code should
now look like this:
1 /** Shopping list:
2 Blouse (4)
3 Pants (1)
4 Underwear (2)
6 **/
7
8 CREATE TABLE shopping_list (id INTEGER
PRIMARY KEY, name TEXT, quantity INTEGER);
9
10 INSERT INTO shopping_list VALUES (1, "Blouse",
4);
11 INSERT INTO shopping_list VALUES (2, "Pants",
1);
12 INSERT INTO shopping_list VALUES (3,
"Underwear", 2);
13 SELECT * FROM shopping_list;
As you can see in our example code above, the code
"SELECT * FROM shopping_list;" is inserted at line 13.
And by inserting this line, you're actually making a query
request to display everything in the shopping list table.
Hence, it would yield the output below:
id
name
quantity
Blouse
4
Pants
1
3
2
Underwear
Again, you'll see the list of shopping items under the "name"
column on the right-hand side if you're using SQLite. Now,
what if we want all of the column names? To get all of the
column names, we can just replace "name" with an asterisk *
symbol in line 9 of our code above. Note that "SELECT *
FROM shopping_list;" is the same code that gets inserted in
a new line when you click on the table name in SQLite.
The list that it will show is out of order, though. If we went
from top to bottom at the apparel store with this list, we'd
have to keep changing aisles. We'd rather have it ordered by
aisle, so that we can be more efficient at the store. To do that,
we can just add an "ORDER BY" command to our query,
specifying which column we want to order by. Your code
should now look like the one below:
1 CREATE TABLE shopping_list (id INTEGER
PRIMARY KEY, name TEXT, quantity INTEGER, aisle
INTEGER);
2 INSERT INTO shopping_list VALUES (1, "Blouse",
4, 7);
3 INSERT INTO shopping_list VALUES (2, "Pants", 1,
2);
4 INSERT INTO shopping_list VALUES (3,
"Underwear", 2, 2);
5 INSERT INTO shopping_list VALUES (4, "Shorts", 1,
12);
name
aisle
Pants
2
Underwear
2
T-shirt
2
6
1
1
4
4
Hat
4
Blouse
7
Shorts
12
Now that's better. We can now get our items faster. However,
to be even more efficient, let's say you and your significant
other like to shop together and split the store. You both shop
one half of the store and you both meet at the checkout
counter. There are twelve aisles in this apparel store. So, for
your list, let's say you just want to know which items are in
aisles 6 through 12.
To filter results out in our SQL table, we need to use a
WHERE clause, followed by the column name, and then what
we want to compare it to. At this point, this is how your code
should look like:
1 CREATE TABLE shopping_list (id INTEGER
PRIMARY KEY, name TEXT, quantity INTEGER, aisle
INTEGER);
2 INSERT INTO shopping_list VALUES (1, "Blouse",
4, 7);
3 INSERT INTO shopping_list VALUES (2, "Pants", 1,
2);
4 INSERT INTO shopping_list VALUES (3,
"Underwear", 2, 2);
5 INSERT INTO shopping_list VALUES (4, "Shorts", 1,
12);
6 INSERT INTO shopping_list VALUES (5, "T-shirt",
6, 2);
6, 2);
7 INSERT INTO shopping_list VALUES (6, "Hat", 1,
4);
8
9 SELECT SUM(quantity) FROM shopping_list;
10
If you're using SQLite, you can see on the right hand side that
the SUM is 15. This means that you should have 15 items in
your cart if you got everything correctly. Notice that if you go
back to line 2 of our code and change the number of Blouses,
you can see the SUM increase in real time.
Now, we could easily try out other aggregate functions here,
because SUM is not the only one. If we want to know what is
the most that we'll be buying of any one item for example, we
could use the MAX aggregate function. What if we wanted to
make sure we had the right number of items after each aisle?
Well, we can do that in SQL using the GROUP BY clause.
We add the GROUP BY clause at the end of a query,
specifying the column name to GROUP BY, which in this
case is "aisle." Your code should now look something like
the one below:
1 CREATE TABLE shopping_list (id INTEGER
PRIMARY KEY, name TEXT, quantity INTEGER, aisle
INTEGER);
SUM(quantity)
9
1
4
1
As you can see, we're going to get nine items in aisle two,
one item in aisle four, four items in aisle seven, and one in
aisle twelve. Now you might be asking, how did that actually
work behind the scenes? What happened is that the SQL
12
13 SELECT * FROM workout_logs;
Let's now see what we have in our workout_logs table. We
have an "id" of course, an "activity," which is a string like
"bench_press" or "tread_mill," "minutes," for how many
minutes you've spent, "calories," for how many you've
burned, and "pulse_rate," for how high it went.
If you look closely, you'll see that we're inserting the data
using a slightly different syntax from before. Notice how
we're specifying the column names between an open and
close parenthesis after the table name. When we do that, it
means that we don't have to specify the columns again and
again after every VALUES clause. We only have to specify
the data that we want to enter in the column names. Here's a
question, do you see which column we didn't specify in our
example code?
The missing column is "id," and that's very much on purpose.
As you can see in line 2 of our code, the "id" column is the
primary key constraint in the table and it's set to autoincrement. What this means is the database will
automatically put an id that's different from the other rows in
the table for us. It's usually a number that's one bigger than
the biggest number so far.
We'd rather have the database figure out the id designation
120);
12
13 SELECT * FROM workout_logs WHERE calories >
50 ORDER BY calories;
Once you put type that code in at line 13, you'll most likely
get the result below:
id
calories
1
3
activity
pulse_rate
bench_press
100
tread_mill
200
minutes
30
110
15
120
As you will most likely see in the result, the tread mill
workout is the only activity that you've done that burned more
than 50 calories, but only took less than 30 minutes to do.
In a similar way, we could use the "OR" operator to return
rows that meet any of some conditions. For example, let's say
we only need to return rows where 'calories' are greater than
50, 'OR pulse_rate' is above 100.
1 CREATE TABLE workout_logs
2
(id INTEGER PRIMARY KEY
AUTOINCREMENT,
3
activity TEXT,
4
minutes INTEGER,
5
calories INTEGER,
6
pulse_rate INTEGER);
7
8
9 INSERT INTO workout_logs(activity, minutes,
calories, pulse_rate) VALUES ("bench_press", 30, 100,
110);
10 INSERT INTO workout_logs(activity, minutes,
calories, pulse_rate) VALUES ("bench_press", 10, 30,
105);
11 INSERT INTO workout_logs(activity, minutes,
calories, pulse_rate) VALUES ("tread_mill", 15, 200,
120);
12
activity
pulse_rate
bench_press
100
110
bench_press
30
105
minutes
30
10
activity
minutes
pulse_rate
bench_press
30
100
110
bench_press
10
30
105
dumbbells
30
70
90
dumbbells
25
72
80
deadlift
30
90
squats
60
80
85
activity
pulse_rate
treadmill
120
minutes
15
activity TEXT,
reason TEXT);
5
6 INSERT INTO trainer_favorites(activity, reason) VALUES
(bench_press, Improves upper body strength.);
7 INSERT INTO trainer_favorites(activity, reason) VALUES
(squats, Improves lower body strength.);
So, here weve created a table of fitness trainer
recommended activities, which just has an activity, which
is the activity from our previous code, and a reason, which
states why the doctor recommended it. Now, what if we
wanted to see all of your workout routines that correspond to
activity
minutes
calories
1
2
3
pulse_rate
bench_press
100
110
bench_press
30
105
squats
80
85
30
10
60
3
activity TEXT,
4
minutes INTEGER,
5
calories INTEGER,
6
pulse_rate INTEGER);
7
8
9 INSERT INTO workout_logs(activity, minutes,
calories, pulse_rate) VALUES ("bench_press", 30, 100,
110);
10 INSERT INTO workout_logs(activity, minutes,
calories, pulse_rate) VALUES ("bench_press", 10, 30,
105);
11 INSERT INTO workout_logs(activity, minutes,
calories, pulse_rate) VALUES ("tread_mill", 15, 200,
120);
12 INSERT INTO workout_logs(activity, minutes,
calories, pulse_rate) VALUES (dumbbells, 30, 70,
90);
13 INSERT INTO workout_logs(activity, minutes,
calories, pulse_rate) VALUES (dumbbells, 25, 72,
80);
14 INSERT INTO workout_losgs(activity, minutes,
calories, pulse_rate) VALUES (deadlift, 30, 70, 90);
15 INSERT INTO workout_logs(activity, minutes,
calories, pulse_rate) VALUES (squats, 60, 80, 85);
16
activity
pulse_rate
squats
85
minutes
60
activity
pulse_rate
squats
85
minutes
60
SUM(calories)
160
365
80
deadlift
dumbbells
70
142
total_calories
160
365
avg_calories
80
182.5
80
71
pulse_rate
110
105
120
120
90
80
90
85
27 FROM workout_logs;
At this point, our conditions are all done. However, we
haven't told SQL what to name this new column. For that, just
insert the below code on line 26 of our code:
26
END as "pulse_zone"
So now, for each of the rows, we can see the new column
with a nice description of what zone they're in.
activity
pulse_rate
pulse_zone
bench_press
110
within target
bench_press
105
within target
treadmill
120
within target
treadmill
120
within target
dumbbells
90
below target
dumbbells
80
below target
deadlift
90
below target
squats
85
below target
Now that we've done that successfully, we can now make a
query that summarizes how many of your logs are in each of
the zones. This query is a whole lot easier compared to when
we set up our "CASE" clause. What we're going to do is just
take our code from lines 20 through 27, copy and paste it in a
new line, and then just add the GROUP BY statement below
at the last line:
GROUP BY pulse_zone;
After that, just modify the SELECT statement to include a
"COUNT(*)" statement and then remove "pulse_rate" and
"activity." Here's how your code should look like overall:
1 CREATE TABLE workout_logs
2
(id INTEGER PRIMARY KEY
AUTOINCREMENT,
3
activity TEXT,
4
minutes INTEGER,
5
calories INTEGER,
6
pulse_rate INTEGER);
7
8
9 INSERT INTO workout_logs(activity, minutes,
calories, pulse_rate) VALUES ("bench_press", 30, 100,
110);
10 INSERT INTO workout_logs(activity, minutes,
calories, pulse_rate) VALUES ("bench_press", 10, 30,
105);
11 INSERT INTO workout_logs(activity, minutes,
calories, pulse_rate) VALUES ("treadmill", 15, 200,
120);
12 INSERT INTO workout_logs(activity, minutes,
calories, pulse_rate) VALUES (treadmill, 15, 165,
120);
13 INSERT INTO workout_logs(activity, minutes,
calories, pulse_rate) VALUES (dumbbells, 30, 70,
90);
14 INSERT INTO workout_logs(activity, minutes,
calories, pulse_rate) VALUES (dumbbells, 25, 72,
80);
15 INSERT INTO workout_logs(activity, minutes,
calories, pulse_rate) VALUES (deadlift, 30, 70, 90);
16 INSERT INTO workout_logs(activity, minutes,
calories, pulse_rate) VALUES (squats, 60, 80, 85);
17
18 SELECT * FROM workout_logs;
19
20 SELECT activity, pulse_rate,
21
CASE
22
WHEN pulse_rate > 220-30 THEN
"above max"
23
WHEN pulse_rate > ROUND(0.90
* (220-30)) THEN "above target"
24
WHEN pulse_rate > ROUND(0.50
* (220-30)) THEN "within target"
25
ELSE "below target"
26
END as "pulse_zone"
27 FROM workout_logs;
28
29
30
31 SELECT COUNT(*),
32
CASE
33
WHEN pulse_rate > 220-30 THEN
"above max"
34
WHEN pulse_rate > ROUND(0.90
* (220-30)) THEN "above target"
35
WHEN pulse_rate > ROUND(0.50
* (220-30)) THEN "within target"
36
ELSE "below target"
37
END as "pulse_zone"
27 FROM workout_logs;
28 GROUP BY pulse_zone;
And now we see that we've got four below target and for
within target in the results.
COUNT(*)
4
4
pulse_zone
below target
within target
14
student_id INTEGER,
15
test TEXT,
16
grade INTEGER);
17
18 INSERT INTO student_grades (student_id,
grade)
19
VALUES (1, "Nutrition", 95);
20 INSERT INTO student_grades (student_id,
grade)
21
VALUES (2, "Nutrition",
92);
22 INSERT INTO student_grades (student_id,
grade)
23
VALUES (1, "Chemistry", 85);
24 INSERT INTO student_grades (student_id,
grade)
25
VALUES (2, "Chemistry", 95);
26
27 SELECT * FROM student_grades
test,
test,
test,
test,
student_id
grade
1
test
Nutrition
2
2
92
3
Chemistry
4
Chemistry
Nutrition
1
85
2
95
As you can see from the result above, SQL returned a result
that shows the student IDs, test names, and grades. However,
what we actually want to be able to see are student names,
emails, etc. Now, what you might notice is that the student ID
in "student_grades" actually corresponds to the ID in
"students." So, student_id number one in student_grades is
actually Peter Rabbit in our "students" table, and so forth.
What we want to do next is form a query that will output the
student name and email next to each test and grade. Note that
we have to extract those information from two different
tables. So basically, what we will be doing is join two
tables. There are a few ways to join multiple tables in SQL.
One of the most basic ways to join multiple tables in SQL is
the Cross join method.
Cross Join
The simplest way to join two tables in a database is called a
Cross Join. We can make that happen by putting both table
names after the "FROM" clause at line 27 of our code. Look
below for how your line 27 should look like after applying
the Cross Join:
27 SELECT * FROM student_grades, students;
After applying the cross join in your SQL code, you'll most
likely see lots of rows. Basically, what the cross join did
was for each row in the students table, it created a row for
each of the rows in the student_grades table. That means we
end up with eight rows, because it created the four rows for
each of the rows in the students table.
While the Cross Join is the simplest join in SQL, it is also
the least useful. So, we don't want every row matched with
every other row. We only want them matched if the student ID
matches. To do that, we can apply what's called an Inner
Join.
Inner Join
The Inner Join isn't actually much more work at all, and it is
way more useful. There are actually a few ways of doing an
inner join. We'll start with just building off of the last query
and add a "WHERE" clause. The "WHERE" clause will
check and make sure that the student_ID in our
student_grades table matches the ID in our students table.
Here's how your code should look like at line 27 and 28 after
applying an Inner Join:
27 SELECT * FROM student_grades, students
28
students.id;
WHERE student_grades.student_id =
student_id
test
id
first_name
last_name
1
Nutrition
95
Peter
Rabbit
2
Nutrition
92
Alice
Wonderland
1
Chemistry
85
Peter
Rabbit
2
Chemistry
95
Alice
Wonderland
JOIN student_grades
29
ON students.id = student_grades.student_id;
Once you put that code in, you'll now have the same results
as before. Now that we have these tables joined, we're just
going to whittle down the columns we're outputting to the
names, the email, the test name, and the grade. We can do this
by just modifying our explicit inner join code a bit to
something like the one below:
27 SELECT first_name, last_name, email, test, grade
FROM students
28
JOIN students_grades
29
ON students.id = student_grades.student_id;
The above code should give you the result below:
first_name
test
Peter
Nutrition
Alice
Nutrition
last_name
grade
Rabbit
email
peter@rabbit.com
95
Wonderland
92
alice@wonderland.com
Peter
Chemistry
Alice
Chemistry
Rabbit
peter@rabbit.com
85
Wonderland
alice@wonderland.com
95
Now the cool thing is, once we've done joins, we can still
use "WHERE" and "GROUP BY," and all those nifty SQL
statements. For example, let's say that we want to filter down
the results to just grades that are greater than 90. To see how
this is done, refer to the code below:
27 SELECT first_name, last_name, email, test, grade
FROM students
28
JOIN students_grades
29
ON students.id = student_grades.student_id;
30
WHERE grade > 90;
Now, what would happen if our tables both contain columns
with the same column name but different meanings? For
example, right now, our student grades table has a grade
column. But what if our students table also has a grade
column for their overall class grade?
If that were the case, then it would mean that at line 27 of our
code where we selected grade, it wouldn't know which table
to pull it from. Why? Because there'd be a grade column in
both of our tables. So, just to be on the safe side, we should
prefix our columns with the table name that they're from.
12
13 CREATE TABLE student_grades (id INTEGER
PRIMARY KEY,
14
student_id INTEGER,
15
test TEXT,
16
grade INTEGER);
17
18 INSERT INTO student_grades (student_id, test,
grade)
19
VALUES (1, "Nutrition", 95);
20 INSERT INTO student_grades (student_id, test,
grade)
21
VALUES (2, "Nutrition",
92);
22 INSERT INTO student_grades (student_id, test,
grade)
23
VALUES (1, "Chemistry", 85);
24 INSERT INTO student_grades (student_id, test,
grade)
25
VALUES (2, "Chemistry", 95);
26
27 CREATE TABLE student_projects (id INTEGER
PRIMARY KEY,
28
student_id INTEGER,
29
title TEXT);
30
31 INSERT INTO student_projects (student_id, title)
32
last_name
title
Rabbit
Carrotapault
Based on the result, we can see one project: Peter and his
very promising carrotapault experiment. But where is the
student named Alice? Well, we're missing Alice because an
"INNER JOIN" only creates rows if there are matching
records in the two tables. There's no row for Alice, because
last_name
Rabbit
Wonderland
Now we see Alice, and there's a big old "NULL" for the
project title. How this works is the "LEFT" statement tells
SQL that it should make sure to retain every row from the left
table, which is the one after the "FROM students." The
"OUTER" statement tells SQL that it should retain the rows
changed in the results. Now, let's say the user made a change.
However, she later decides that she actually wants to delete
the entire log entry. In that case, we would have to make use
of the "DELETE" clause. Take a look at the code below:
15 SELECT * FROM journal_logs:
16
17 UPDATE journal_logs SET content = "I had a stupid
fight with my boyfriend." WHERE id = 1;
18
19 DELETE FROM journal_logs WHERE id = 1;
If you do another SELECT statement to see our journal_logs,
you'll notice that we only have one row left, which means
that our "DELETE" worked. You should be very careful
whenever you're doing "UPDATE" or "DELETE." You want
to make sure that you're updating the actual rows that you
intend to update. You don't want to update the wrong rows, or
delete the wrong rows and lose data.
In fact, some apps never issue "DELETE"they never really
delete rows. Instead, they'll add a "deleted" column to their
database, and they'll do something like set "deleted" to "true"
whenever the user wants to delete. They would then filter the
data based on "deleted = FALSE" in the SELECT queries.
So now, with "SELECT," "INSERT," "UPDATE," and
"DELETE," you have all the commands you need to handle
what a user would want out of your journal app. And now,
hopefully, you can better imagine which commands are
happening behind the scenes when you use your favorite apps
every day.
Conclusion
I hope this book really helped you master the basics of SQL.
The next step is to practice using the SQL statements youve
learned. Once you are comfortable using them, you will be
able to manage databases easily.