9 SQL
This BP chapter is a bit different from many of the DataCamp-based chapters you’ve done. For Econ 380, you need to understand R programming well, but you likely won’t need to use SQL at all. I’m having you learn about basic SQL because having a basic understanding of SQL might be useful for you in the future (e.g., to get an internship/job and/or to do well there). The purpose of this BP chapter is for you to become familiar with a few fundamentals of SQL. You will not know enough to develop a database for a large corporation, but you should know enough to be able to get data from a SQL database (e.g., an internship that has something like “familiarity with SQL queries” should apply to you).
There are several ways for you to achieve this goal. One is to go through part or all of the following DataCamp courses, along with the main things you should learn in each (note each title is a link to the course)):
- Introduction to SQL
- What SQL is, and knowledge of the existence of different SQL flavors (e.g., SQL Server and PostgreSQL)
- Structure of relational databases (e.g., tables, fields)
SELECTstatement
- Intermediate SQL
DISTINCTWHEREAND,OR- NULL values
- Aggregate Functions (
AVG(),SUM(),MIN(),MAX(),COUNT()) ORDER BY(sorting)GROUP BYHAVING(thereWHEREthat goes withGROUP BY)
- Joining Data in SQL
INNER JOINLEFT JOIN(orRIGHT JOIN)
Note that these 3 courses are the first 3 courses in the DataCamp “SQL Fundamentals” skill track. If you are interested in learning more about SQL, this skill track has additional courses, as well as DataCcamp “projects” to practice your skills.
The other way to learn about these aspects of SQL is you can read about them in this tutorial: https://www.sqlitetutorial.net/sqlite-sample-database. I’m giving you this particular website/tutorial because it comes with a useful sample database that also works in R (as shown below). I want you to learn about all the things included in the outline above. You can learn about them through any combination of DataCamp and this online tutorial. For example, to learn about ORDER BY, you could go through that part of the Intermediate SQL DC course, and/or you could read through (and experiment with) the order by page in the online tutorial. You are also free to use other websites, videos, AI, etc. I don’t care how you learn about SQL, I just want you to be familiar with it.
The website explains how to do some things if you install SQLite on your computer. You do not need to do that. You can (and should) do everything in R via the RSQLite package, as explained below.
Note that the specifics on this website are for the SQLite version of SQL. DataCamp seems to be specific to PostgreSQL. But you’re just learning about the basics, and the basics work the same in all the different versions/flavors. There are a few exceptions. For example, you might want to use the keyword LIMIT to only output the top, say, 10 rows. This is done using LIMIT in SQLite, PostgreSQL, and MySQL. If instead you use Microsoft SQL Server or Access (not unlikely at an internship or job), the keyword is TOP instead of LIMIT. That’s the only one I can think of that you might want to use now. Other than that, they are the same. Thus, any website discussing the basics should be the same if you want to look beyond the DataCamp courses and website I’ve given you.
To demonstrate what you have learned, and to serve as a reference for you later, you are going to take notes here on the key features of SQL with which I want you to be familiar. You should use the RSQLite package with the chinook.db database. I demonstrate below how to do so. This means that you should have working examples of everything in SQL, just like you do for R. I provide you a working example of SELECT below.
I will add section headings to serve as a structure for your notes, and to serve as a checklist so you include everything I want you to include. As with all BP chapters, you are free to add more if you want.
9.1 Loading a SQL database in R via RSQLite
Packages used in this chapter:
Load the connection (that we have named con) to the chinook database (the one from the website tutorial):
As the online documentation for the database shows, this database has the following tables:
## [1] "albums" "artists" "customers" "employees"
## [5] "genres" "invoice_items" "invoices" "media_types"
## [9] "playlist_track" "playlists" "sqlite_sequence" "sqlite_stat1"
## [13] "tracks"
You can also see the list of fields in a particular table like this:
## [1] "CustomerId" "FirstName" "LastName" "Company" "Address"
## [6] "City" "State" "Country" "PostalCode" "Phone"
## [11] "Fax" "Email" "SupportRepId"
You can then create working examples using SQL code chunks, like this:
| FirstName | LastName |
|---|---|
| Luís | Gonçalves |
| Leonie | Köhler |
| François | Tremblay |
| Bjørn | Hansen |
| František | Wichterlová |
| Helena | Holý |
| Astrid | Gruber |
| Daan | Peeters |
| Kara | Nielsen |
| Eduardo | Martins |
The rest of this file should have your notes, including code chunks with working examples similar to the one above (the code chunk with a working example showing how to use SELECT). If you want the notes to go in a different order, feel free to rearange the headings. But make sure to include an example of everything with a level 3 (###) or 4 (####) heading below.
9.2 Selecting data
Fields(columns) should be lowercase without spaces, using underscores instead. Databases in SQL are structured with linked tables.
9.2.1 Select
SELECT can be used to indicate what fields should be selected. This can also be used with FROM to choose what table to come from. We can also select multiple field using commas between them. Using SELECT * selects all fields.
## [1] "TrackId" "Name" "AlbumId" "MediaTypeId" "GenreId"
## [6] "Composer" "Milliseconds" "Bytes" "UnitPrice"
SELECT TrackId, Name, Composer, Milliseconds FROM tracks
-- comments in SQL code chunks start with --| TrackId | Name | Composer | Milliseconds |
|---|---|---|---|
| 1 | For Those About To Rock (We Salute You) | Angus Young, Malcolm Young, Brian Johnson | 343719 |
| 2 | Balls to the Wall | NA | 342562 |
| 3 | Fast As a Shark | F. Baltes, S. Kaufman, U. Dirkscneider & W. Hoffman | 230619 |
| 4 | Restless and Wild | F. Baltes, R.A. Smith-Diesel, S. Kaufman, U. Dirkscneider & W. Hoffman | 252051 |
| 5 | Princess of the Dawn | Deaffy & R.A. Smith-Diesel | 375418 |
| 6 | Put The Finger On You | Angus Young, Malcolm Young, Brian Johnson | 205662 |
| 7 | Let’s Get It Up | Angus Young, Malcolm Young, Brian Johnson | 233926 |
| 8 | Inject The Venom | Angus Young, Malcolm Young, Brian Johnson | 210834 |
| 9 | Snowballed | Angus Young, Malcolm Young, Brian Johnson | 203102 |
| 10 | Evil Walks | Angus Young, Malcolm Young, Brian Johnson | 263497 |
| PlaylistId | Name |
|---|---|
| 1 | Music |
| 2 | Movies |
| 3 | TV Shows |
| 4 | Audiobooks |
| 5 | 90’s Music |
| 6 | Audiobooks |
| 7 | Movies |
| 8 | Music |
| 9 | Music Videos |
| 10 | TV Shows |
9.2.2 Count()
COUNT() is used to return all values in a field. using the * you can count all records(rows)
| count_playlists |
|---|
| 18 |
9.2.3 Distinct
DISTINCT can be used to remove all unique values in a field as their often might be duplicates.
It is often used in combination with COUNT.
The below example returns a count of all the unique playlists types, removing all duplicates.
| playlist_types |
|---|
| 14 |
9.2.4 Where
WHERE can be used to filter. With numbers we can use less than <,greater than >, equal to =, less than or equal to <=, greater than or equal to >=, and not equal to<>.
It can also be used for characters then the character needs to be in single quotes ‘example’
-- this all songs over 300,000 milliseconds
SELECT Name, Milliseconds AS over_300K
FROM tracks
WHERE Milliseconds >300000| Name | over_300K |
|---|---|
| For Those About To Rock (We Salute You) | 343719 |
| Balls to the Wall | 342562 |
| Princess of the Dawn | 375418 |
| Go Down | 331180 |
| Let There Be Rock | 366654 |
| Problem Child | 325041 |
| Overdose | 369319 |
| Whole Lotta Rosie | 323761 |
| Love In An Elevator | 321828 |
| What It Takes | 310622 |
| Name |
|---|
| Music |
| Music |
9.2.4.1 And, Or
AND and OR can be used with WHERE.
OR only needs one of the criteria in the filter to be valid, while AND needs all of them to be.
The below example returns all tracks that have one of two specific composers and are over 350,000 millisecond or under 200,000 milliseconds.
SELECT Name, Composer, Milliseconds
FROM tracks
WHERE (Composer = 'Angus Young, Malcolm Young, Brian Johnson' OR Composer = 'Alanis Morissette & Glenn Ballard')
AND (Milliseconds > 350000 OR Milliseconds < 200000)| Name | Composer | Milliseconds |
|---|---|---|
| C.O.D. | Angus Young, Malcolm Young, Brian Johnson | 199836 |
| Perfect | Alanis Morissette & Glenn Ballard | 188133 |
| Right Through You | Alanis Morissette & Glenn Ballard | 176117 |
| You Oughta Know (Alternate) | Alanis Morissette & Glenn Ballard | 491885 |
9.2.5 Order By (Sorting)
ORDER BY is used to sort results. Ascending is the automatic orientation, we can use ASC to validate that or use DESC to sort descending.
It can also be used by multiple fields.
The below example sorts by GenreId and then by Bytes within each GenreId.
| Name | GenreId | Bytes |
|---|---|---|
| Dazed And Confused | 1 | 52490554 |
| Space Truckin’ | 1 | 39267613 |
| Dazed And Confused | 1 | 36052247 |
| We’ve Got To Get Together/Jingo | 1 | 34618222 |
| Funky Piano | 1 | 30200730 |
| Going Down / Highway Star | 1 | 29846063 |
| Santana Jam | 1 | 29207100 |
| The Sun Road | 1 | 29008407 |
| Whole Lotta Love | 1 | 28191437 |
| Mistreated (Alternate Version) | 1 | 27775442 |
9.3 Grouping and aggregating
9.3.1 Group By
GROUP BY allows us to provide summary statistics for certain subsections of the data.
We can also use it on multiple fields.
The below example returns the average number of bytes for each GenreId and sorts them in descending order.
| GenreId | avg_bytes |
|---|---|
| 20 | 532930426 |
| 18 | 507078984 |
| 21 | 506946967 |
| 19 | 340261678 |
| 22 | 316904466 |
| 15 | 10691926 |
| 2 | 9488137 |
| 13 | 9474752 |
| 3 | 9234573 |
| 1 | 9007374 |
9.3.2 Aggregate Functions
Aggregate functions can be utilized to gain a better understanding about the data you are working with.
They all work with numerical data, while COUNT(), MIN(), and MAX() also work with strings.
9.3.2.1 Avg()
AVG() returns the average value of a specified field.
The below example shows the average number of bytes of a track.
| AVG(Bytes) |
|---|
| 33510207 |
9.3.2.2 SUM()
SUM() returns the sum of all the values of a specified field.
The below example shows the total number of bytes of all tracks in the database.
| SUM(Bytes) |
|---|
| 117386255350 |
9.3.2.3 MIN() and MAX()
MIN() returns the lowest value of a specified field and MAX() returns the highest value of a specified field.
These functions also works with strings, unlike AVG() and SUM().
The below example shows the minimum and maximum number of bytes of a track in the database.
| MIN(Bytes) | MAX(Bytes) |
|---|---|
| 38747 | 1059546140 |
Here we also see the functionality with non-numerical data.
| MIN(Name) | MAX(Name) |
|---|---|
| “40” | Último Pau-De-Arara |
9.3.3 Having (where for groups)
In SQL we cannot filter aggregate functions with WHERE, so we utilize HAVING.
HAVING filters grouped records while WHERE filters individual records.
The below example builds off the earlier GROUP BY example, but now only returning those records where the avg_bytes is greater than 10,000,000.
SELECT GenreId, AVG(Bytes) AS avg_bytes
FROM tracks
GROUP BY GenreId
HAVING AVG(Bytes) > 10000000
ORDER BY avg_bytes DESC| GenreId | avg_bytes |
|---|---|
| 20 | 532930426 |
| 18 | 507078984 |
| 21 | 506946967 |
| 19 | 340261678 |
| 22 | 316904466 |
| 15 | 10691926 |
9.4 Joining tables
9.4.1 Inner join
Just like in R INNER JOIN returns values where there is records in both tables.
SELECT TrackId, tracks.Name AS Name, tracks.AlbumId, albums.ArtistId
FROM tracks
INNER JOIN albums
ON tracks.AlbumId = albums.AlbumID| TrackId | Name | AlbumId | ArtistId |
|---|---|---|---|
| 1 | For Those About To Rock (We Salute You) | 1 | 1 |
| 6 | Put The Finger On You | 1 | 1 |
| 7 | Let’s Get It Up | 1 | 1 |
| 8 | Inject The Venom | 1 | 1 |
| 9 | Snowballed | 1 | 1 |
| 10 | Evil Walks | 1 | 1 |
| 11 | C.O.D. | 1 | 1 |
| 12 | Breaking The Rules | 1 | 1 |
| 13 | Night Of The Long Knives | 1 | 1 |
| 14 | Spellbound | 1 | 1 |
9.4.2 Left join (or right join)
LEFT JOIN returns all values in the left table and the corresponding ones in the right table.
It’s functionality is very similar to in R.
SELECT
e.FirstName AS first, e.LastName AS last,
c.CustomerId
FROM employees AS e
LEFT JOIN customers AS c
ON e.EmployeeId = c.SupportRepId;| first | last | CustomerId |
|---|---|---|
| Andrew | Adams | NA |
| Nancy | Edwards | NA |
| Jane | Peacock | 1 |
| Jane | Peacock | 3 |
| Jane | Peacock | 12 |
| Jane | Peacock | 15 |
| Jane | Peacock | 18 |
| Jane | Peacock | 19 |
| Jane | Peacock | 24 |
| Jane | Peacock | 29 |
SELECT
e.FirstName AS first, e.LastName AS last,
c.CustomerId
FROM employees AS e
RIGHT JOIN customers AS c
ON e.EmployeeId = c.SupportRepId;| first | last | CustomerId |
|---|---|---|
| Jane | Peacock | 1 |
| Jane | Peacock | 3 |
| Jane | Peacock | 12 |
| Jane | Peacock | 15 |
| Jane | Peacock | 18 |
| Jane | Peacock | 19 |
| Jane | Peacock | 24 |
| Jane | Peacock | 29 |
| Jane | Peacock | 30 |
| Jane | Peacock | 33 |
9.4.3 Other types of joins
9.4.3.1 Full Join
FULL JOIN returns all values from both tables.
SELECT e.FirstName, e.LastName, e.Title, c.CustomerId
FROM employees AS e
FULL JOIN customers AS c
ON e.EmployeeId = c.SupportRepId;| FirstName | LastName | Title | CustomerId |
|---|---|---|---|
| Andrew | Adams | General Manager | NA |
| Nancy | Edwards | Sales Manager | NA |
| Jane | Peacock | Sales Support Agent | 1 |
| Jane | Peacock | Sales Support Agent | 3 |
| Jane | Peacock | Sales Support Agent | 12 |
| Jane | Peacock | Sales Support Agent | 15 |
| Jane | Peacock | Sales Support Agent | 18 |
| Jane | Peacock | Sales Support Agent | 19 |
| Jane | Peacock | Sales Support Agent | 24 |
| Jane | Peacock | Sales Support Agent | 29 |
9.4.3.2 Cross Join
This returns all possible combinations of two tables. This matches every row in artist with every row in genre, without any matching term.
| artist | genre |
|---|---|
| AC/DC | Rock |
| AC/DC | Jazz |
| AC/DC | Metal |
| AC/DC | Alternative & Punk |
| AC/DC | Rock And Roll |
| AC/DC | Blues |
| AC/DC | Latin |
| AC/DC | Reggae |
| AC/DC | Pop |
| AC/DC | Soundtrack |
9.4.3.3 Self Join
This is for joining a table with itself. It can be useful for comparing different values in the same table. While self join is not a verb itself, it is a concept that can be very useful.
SELECT
e.EmployeeId,
e.LastName AS employee_last_name,
m.LastName AS manager_last_name
FROM employees AS e
LEFT JOIN employees AS m
ON e.ReportsTo = m.EmployeeId;| EmployeeId | employee_last_name | manager_last_name |
|---|---|---|
| 1 | Adams | NA |
| 2 | Edwards | Adams |
| 3 | Peacock | Edwards |
| 4 | Park | Edwards |
| 5 | Johnson | Edwards |
| 6 | Mitchell | Adams |
| 7 | King | Mitchell |
| 8 | Callahan | Mitchell |