Lecture 14: SQL Queries
September 1, 2026
π Last lecture we looked at databases, specifically:
Also recall the following topics:
tidyverse library|> operatorπ Today we will cover the following material:
Last lecture we discussed the relational model.
Data manipulation is a slight misnomer β manipulating data could mean many things.

SQL is the most common language used in storing, querying and manipulating structured data.

Note that in PSTAT 10 we will not cover:
GRANT, REVOKE).COMMIT, ROLLBACK).π These matter for multi-user, production databases β beyond our scope, where a single analyst queries a local database.

The database engine is the software a DBMS uses to interface with its contents.
SQLite database engine.SQLite is the most widely used database engine worldwide.SQLite.To interface with databases using R we will use RSQLite.
RSQLite embeds SQLite in R.SQLite functionality with an R interface.RSQLite allows us to interface with DBs using SQL code in R.DBI constructs the database interface.sqldf allows us to manipulate R data frames using SQL.π If you do not have all three libraries loaded, code appearing later in the lecture will break.
02:00
Our initial database work in this course uses the TinyClothesDB.
Download the tc_clean.sqlite file from Canvas and put it in your current working directory.
To access the database, we need to create a connection to the database file. We do so using the dbConnect() function:
Since we store our data in a separate data folder, our function is:
We cannot interface with the database using the <SQLiteConnection> directly. We must save it as an object.
We now have all the pieces we need:
tc_dbDBI, RSQLite, sqldf)A dbConnect object only lets us interface with the database.
The dbListTables() function returns the relation names in the remote database.
dbListTables()We apply the dbListTables() and dbListFields() functions to our tc_db connection object.
We see our database has four tables:
CustomerProductSalesOrderSalesOrderLinedbListFields()The attributes of the relation of interest are:
CustomerIdNameAddressπ In general these functions become less useful as database and relation size increases!
03:00
Use the dbListFields() command to determine the attributes of the Product, SalesOrder and SalesOrderLine tables.
[1] "ProductId" "Name" "Color"
[1] "CustomerId" "OrderId" "Date"
[1] "OrderId" "ProductId" "Quantity"
Likely primary keys β the Id attribute that uniquely identifies each row:
ProductId for Product, OrderId for SalesOrder, SalesOrderLineId for SalesOrderLine.Likely foreign keys β attributes that point to another tableβs primary key:
SalesOrder holds a CustomerId β links each order to the Customer who placed it.SalesOrderLine holds an OrderId and a ProductId β links each line to its SalesOrder and to the Product sold.So far, we have functions to:
The next step is actually accessing table data, which will require us to write our first SQL query:
dbListTables() and dbListFields() are just R functions.Basic SQL query structure is detailed below:
For example, the following query would return the ProductId and Name columns from all rows of Product where ProductId > 1:
π R cannot interpret SQL code directly.
To work with SQL queries in R, we can use the functions dbGetQuery() and dbExecute() from the DBI library.
dbGetQuery() for SELECT queries only.dbGetQuery() returns the query result as an R dataframe.dbExecute().π dbGetQuery() may ostensibly work with commands that should use dbExecute(), but you can very quickly run into errors and compatibility issues.
Letβs consider our previous query, which returned the ProductId and Name columns from all rows of Product where ProductId > 1.
05:00
SELECT queries are the bread and butter of SQL! Youβre going to be writing several of them in the next few assignments, so write two for practice now.
CustomerId, Name and Address values of all customers whose addresses are either "State" or "Ocean".Date values of all sales orders where OrderId is greater than 5. CustomerId Name Address
1 1 Alex State
2 3 Carol Ocean
Date
1 8/16/19
2 10/12/19
Reading each query as SELECT (columns) FROM (table) WHERE (condition):
Customer; the WHERE uses or so a row qualifies if Address is 'State' or 'Ocean'.Date from SalesOrder; the WHERE keeps rows where the OrderId > 5 comparison is true.Our queries so far have been very basic β we are working with a very basic database.
Letβs see how queries translate to a much more complex database.
For the remaining exercises we will use the Chinook database. This database is a useful training tool for several reasons:
Exploring tables is usually an appropriate first step.
Running a SQL query through dbGetQuery() returns a dataframe:
π The LIMIT argument functions similarly to the second argument of head() in R.
The PRAGMA command is a special tool for SQLite.
It is an SQL extension specific to SQLite and used to:
π Queries with PRAGMA can get quite complex!
We will mainly work with:
PRAGMA table_info(table)PRAGMA foreign_key_list(table) cid name type notnull dflt_value pk
1 0 CustomerId INTEGER 1 NA 1
2 1 FirstName NVARCHAR(40) 1 NA 0
3 2 LastName NVARCHAR(20) 1 NA 0
4 3 Company NVARCHAR(80) 0 NA 0
5 4 Address NVARCHAR(70) 0 NA 0
6 5 City NVARCHAR(40) 0 NA 0
7 6 State NVARCHAR(40) 0 NA 0
8 7 Country NVARCHAR(40) 0 NA 0
9 8 PostalCode NVARCHAR(10) 0 NA 0
10 9 Phone NVARCHAR(24) 0 NA 0
11 10 Fax NVARCHAR(24) 0 NA 0
12 11 Email NVARCHAR(60) 1 NA 0
13 12 SupportRepId INTEGER 0 NA 0
id seq table from to on_update on_delete match
1 0 0 Employee SupportRepId EmployeeId NO ACTION NO ACTION NONE
π Remember that a foreign key points to the primary key of another table.
Here SupportRepId in Customer points to EmployeeId in Employee.
foreign_key_list()PRAGMA foreign_key_list(table) lists every foreign key defined on a relation.
from is the attribute in this table that acts as the foreign key.table and to name the relation and attribute it points to.
This diagram shows the same relationships that foreign_key_list() reported, but for the whole database at once.
So far we have only worked with SELECT (i.e. DQL queries). We can use INSERT to add to a table. This is a modification to the table, so we use dbExecute().
[1] 1
CustomerId FirstName LastName Email
1 97 John Inston johninston@ucsb.edu
We must satisfy entity integrity when modifying tables.
We must also satisfy referential integrity when modifying tables.
NULL.SupportRepId is the Customer foreign key for EmployeeId.dbExecute(chinook_db,
"INSERT INTO Customer (CustomerId, FirstName, LastName, Email, SupportRepId)
VALUES (92, 'Blythe', 'King', 'blytheking@pstat.ucsb.edu', 16384)")
dbExecute(chinook_db,
"INSERT INTO Customer (CustomerId, FirstName, LastName, Email, SupportRepId)
VALUES (90, 'Blythe', 'King', 'blytheking@pstat.ucsb.edu', 16384)")[1] 1
[1] 1
Letβs look at what we have added to our database.
DELETE Syntaxπ Be very careful if you decide to use the DELETE syntax, as it does not allow you to undo mistakes and you will have to re-download the database.
In our case we delete the individuals that we added using the following code (you do not need to run this):
For the next few lectures, you should be thinking about organization.
π€ Today we looked at:
π€© Next class we will continue our exploration of databases and SQL with:
π Next lecture is code / syntax heavy, similar to early R lectures!