Contains flight info for NYC departures to various US destinations in 2013
flights: all NYC departures in 2013
weather: hourly data for each airport
planes: construction info for each plane
airports: airport names and locations
airlines: two letter carrier codes/names
Establishing a SQL connection within R
We use SQLite: “small, fast, self-contained, high-reliability, full-featured, SQL database engine”
#install.packages(c("DBI", "dittodb", "RSQLite", "nycflights13")) need to install all theselibrary(DBI)library(dittodb)library(RSQLite)# establishing connection via sqlite# ":memory:" is a special path that creates an in-memory database# other database drivers require more details (like user, password, host, port, etc.)con_air <- DBI::dbConnect(RSQLite::SQLite(), ":memory:") # creates a database including a few tables from the nycflights13 dataset with con_air connectiondittodb::nycflights13_create_sql(con_air)
The function dbListTables() tells us what tables exist in the airlines database
Subqueries are queries that are nested inside another SQL query
Help us get more automation over filtering
Suppose we want to count total flights with destination codes starting with “M”, grouped by code
We could first get all destination codes starting with “M” (LIKE "M%" does wildcard matching)
SELECTDISTINCT dest FROM flights WHERE dest LIKE"M%"
Then, we could manually type out this list in are query but this is tedious are prone to error
SELECT dest, COUNT(*) as tot_flightsFROM flightsWHERE dest IN ("MIA", "MCO", "MSP", "MSY", "MKE", "MEM", "MYR", "MDW","MHT", "MSN", "MCI", "MTJ", "MVY")GROUPBY dest
Additional worry: what if a new destination code is added that starts with “M”?
Subqueries in SQL
SELECT dest, COUNT(*) as tot_flightsFROM flightsWHERE dest IN (SELECTDISTINCT dest FROM flights WHERE dest LIKE"M%")GROUPBY dest
Takeaway: subqueries result in more automated, scalable, and expressive code
Relating information between tables in SQL
How to get the count of total flights in Jun/Jul by plane carrier name?
SELECT carrier, COUNT(*) as tot_flightsFROM flightsWHEREmonthIN (6, 7)GROUPBY carrier
Almost there…but we want the carrier name, not carrier code
How can we modify our query to obtain and use this carrier name information?
We can use joins!
Joining in SQL
Recall that SQL is a query language that works on relational databases
The syntax for tying together information from multiple tables is done with a JOIN clause
A JOIN clause requires four pieces of information:
The name of the first table you want to JOIN
The type of JOIN being used
The name of the second table you want to JOIN
The condition(s) under which the records in the first table should match records in the second table
Venn diagrams describing different JOINs, image credit: phoenixNAP
Interlude: keys in SQL
Keys are useful for the following:
Creating relationships between two tables
Keeping data consistent and valid in the database
Helping in fast retrieval of data
Maintaining uniqueness in a table
Primary key: constraint that uniquely identifies each record in a table (e.g., studentID)
Must be unique and not null
Only one allowed per table
Foreign key: constraint that establishes a link between two tables
Refers to the primary key in another table
These are extremely useful for joining/relating two tables in a SQL database!
Joining in SQL
airlines: carrier code is a PRIMARY KEY since it uniquely identifies each observation
flights carrier code is a FOREIGN KEY that corresponds to the carrier code PRIMARY KEY in airlines — it is not a unique identifier of observations in flights
We can use the idea of a LEFT JOIN to link the keys:
SELECT al.name AS airline, COUNT(*) as tot_flightsFROM flights AS flLEFTJOIN airlines AS alON al.carrier = fl.carrierWHEREmonthIN (6, 7)GROUPBY al.name
Note: we use table aliases (e.g., “al” for “airlines”) to avoid reference ambiguities
This gives us the airline names based on the carrier code column!
Saving SQL query outputs as R objects
We can use --|output.var: "name_of_variable" inside the {sql} chunk to save query output to the R environment
SELECT al.name AS airline, COUNT(*) as tot_flightsFROM flights AS flLEFTJOIN airlines AS alON al.carrier = fl.carrierWHEREmonthIN (6, 7)GROUPBY al.name
This allows us to use query output for further analysis or visualizations in R
head(tot_airline_flights, n =3)
airline tot_flights
1 AirTran Airways Corporation 515
2 Alaska Airlines Inc. 122
3 American Airlines Inc. 5639
Destructive actions in SQL
In SQL, you can run commands that alter database schemas or permanently erase and modify data without automatically generating backups.
These are called destructive actions. Examples include:
DROP: deletes entire database objects (like tables, views, indexes, or the entire database) along with their data and structural definitions
TRUNCATE: instantly removes all rows from a table, resetting the table to its initial empty state
DELETE: removes rows from a table
UPDATE: modifies existing data in the database
Running DELETE (or UPDATE) without a WHERE clause will delete (or overwrite) every single row in the table
SQL best practices
Before running any destructive action, you should: