メインコンテンツにスキップ

SQLite in R

In this tutorial, you will learn about using SQLite, an extremely light-weight relational database management system (RDBMS) in R.
· 12 分 読む

AIで探求

As mentioned at the end of my previous tutorial, Beginners Guide to SQLite, SQLite databases are most powerful when used along R and Python. So far in DataCamp, you have been exposed to how to operate with SQLite databases from Python (see the SQLite in Python tutorial by Sayak Paul to learn how to manipulate SQLite databases through the sqlite3 package in Python). However, in this tutorial, we will focus on how to use SQLite databases in R using the RSQLite package.

We will go over the basics of how to perform essential tasks such as sending queries to a SQLite database or creating tables using RSQLite. Furthermore, I will cover how to use parameterized queries and operations like INSERT or DELETE that do not return tabular results.

Creating Databases and Tables

The first step as you may have guessed is to create a database. RSQLite can create ephemeral in-memory transient SQLite databases just as it happens when you open the SQLite command line. However, this is not usually what you want, so let's create a proper database for the mtcars dataset using the function dbConnect(), which takes the following arguments:

  • drv: A database driver
  • path: The path to a SQLite database. If you are creating a new one, simply give it a name of your choice as I do below. If you want to operate with a transient in-memory database; however, you can omit the path argument or type ":memory:").
# Load the RSQLite Library
library(RSQLite)
# Load the mtcars as an R data frame put the row names as a column, and print the header.
data("mtcars")
mtcars$car_names <- rownames(mtcars)
rownames(mtcars) <- c()
head(mtcars)
# Create a connection to our new database, CarsDB.db
# you can check that the .db file has been created on your working directory
conn <- dbConnect(RSQLite::SQLite(), "CarsDB.db")
mpgcyldisphpdratwtqsecvsamgearcarbcar_names
21.061601103.902.62016.460144Mazda RX4
21.061601103.902.87517.020144Mazda RX4 Wag
22.84108933.852.32018.611141Datsun 710
21.462581103.083.21519.441031Hornet 4 Drive
18.783601753.153.44017.020032Hornet Sportabout
18.162251052.763.46020.221031Valiant

Once you have the database created and your data in proper shape, you can go ahead and create a table inside the database using the dbWriteTable() function. This function can take multiple arguments, but, for now, let's focus on the following:

  • conn: The connection to your SQLite database
  • name: The name you want to use for your table
  • value: The data that you want to insert. It should be an R data frame or an object coercible to an R data frame.

After that, you can use the function dbListTables() with the SQLite database connection as an argument to check that you have successfully created the table.

# Write the mtcars dataset into a table names mtcars_data
dbWriteTable(conn, "cars_data", mtcars)
# List all the tables available in the database
dbListTables(conn)

'cars_data'

An extremely useful feature when creating tables using RSQLite, is that you can append more data to an existing table using a loop in case that you have several data frames by setting the optional argument append = TRUE inside dbWriteTable(). For instance, let us create a new toy table with some cars and the manufacturers by appending two different data frames:

# Create toy data frames
car <- c('Camaro', 'California', 'Mustang', 'Explorer')
make <- c('Chevrolet','Ferrari','Ford','Ford')
df1 <- data.frame(car,make)
car <- c('Corolla', 'Lancer', 'Sportage', 'XE')
make <- c('Toyota','Mitsubishi','Kia','Jaguar')
df2 <- data.frame(car,make)
# Add them to a list
dfList <- list(df1,df2)
# Write a table by appending the data frames inside the list
for(k in 1:length(dfList)){
    dbWriteTable(conn,"Cars_and_Makes", dfList[[k]], append = TRUE)
}
# List all the Tables
dbListTables(conn)
  1. 'Cars_and_Makes'
  2. 'cars_data'

Let us now make sure that all the data is in the new table:

dbGetQuery(conn, "SELECT * FROM Cars_and_Makes")
carmake
CamaroChevrolet
CaliforniaFerrari
MustangFord
ExplorerFord
CorollaToyota
LancerMitsubishi
SportageKia
XEJaguar

Executing SQL Queries

As you have seen above, it is possible to execute valid SQL queries through RSQLite using the dbGetQuery() function, which has the following arguments:

  • conn: The connection to the SQLite Database
  • query: The SQL query you want to execute as a string

To further showcase the capability to execute SQL queries with RSQLite, let's go over some more query examples on the cars_data table:

Note: It is important to keep in mind that, through RSQLIte, you can execute any query that is valid for a SQLite database from simple SELECT statements to JOINS (Except RIGHT OUTER JOINS and FULL OUTER JOINS, which are not allowed in SQLite).

# Gather the first 10 rows in the cars_data table
dbGetQuery(conn, "SELECT * FROM cars_data LIMIT 10")
mpgcyldisphpdratwtqsecvsamgearcarbcar_names
21.06160.01103.902.62016.460144Mazda RX4
21.06160.01103.902.87517.020144Mazda RX4 Wag
22.84108.0933.852.32018.611141Datsun 710
21.46258.01103.083.21519.441031Hornet 4 Drive
18.78360.01753.153.44017.020032Hornet Sportabout
18.16225.01052.763.46020.221031Valiant
14.38360.02453.213.57015.840034Duster 360
24.44146.7623.693.19020.001042Merc 240D
22.84140.8953.923.15022.901042Merc 230
19.26167.61233.923.44018.301044Merc 280
# Get the car names and horsepower of the cars with 8 cylinders
dbGetQuery(conn,"SELECT car_names, hp, cyl FROM cars_data
                 WHERE cyl = 8")
car_nameshpcyl
Hornet Sportabout1758
Duster 3602458
Merc 450SE1808
Merc 450SL1808
Merc 450SLC1808
Cadillac Fleetwood2058
Lincoln Continental2158
Chrysler Imperial2308
Dodge Challenger1508
AMC Javelin1508
Camaro Z282458
Pontiac Firebird1758
Ford Pantera L2648
Maserati Bora3358
# Get the car names and horsepower starting with M that have 6 or 8 cylinders
dbGetQuery(conn,"SELECT car_names, hp, cyl FROM cars_data
                 WHERE car_names LIKE 'M%' AND cyl IN (6,8)")
car_nameshpcyl
Mazda RX41106
Mazda RX4 Wag1106
Merc 2801236
Merc 280C1236
Merc 450SE1808
Merc 450SL1808
Merc 450SLC1808
Maserati Bora3358
# Get the average horsepower and mpg by number of cylinder groups
dbGetQuery(conn,"SELECT cyl, AVG(hp) AS 'average_hp', AVG(mpg) AS 'average_mpg' FROM cars_data
                 GROUP BY cyl
                 ORDER BY average_hp")
cylaverage_hpaverage_mpg
482.6363626.66364
6122.2857119.74286
8209.2142915.10000

To store the results of your queries to do further operations in R down the line as a data frame, it is as simple as assigning the result of your query to a variable.

avg_HpCyl <- dbGetQuery(conn,"SELECT cyl, AVG(hp) AS 'average_hp'FROM cars_data
                 GROUP BY cyl
                 ORDER BY average_hp")
avg_HpCyl
class(avg_HpCyl)
cylaverage_hp
482.63636
6122.28571
8209.21429

'data.frame'

Insert Variables into Queries (Parameterised Queries)

One of the greatest advantages of operating with SQLite databases from R is the ability to use parameterized queries. That is, the ability to gather variables available in your R workspace and use them to query your SQLite database. Let me show an example of how to use a variable inside a SQLite query:

# Lets assume that there is some user input that asks us to look only into cars that have over 18 miles per gallon (mpg)
# and more than 6 cylinders
mpg <-  18
cyl <- 6
Result <- dbGetQuery(conn, 'SELECT car_names, mpg, cyl FROM cars_data WHERE mpg >= ? AND cyl >= ?', params = c(mpg,cyl))
Result
car_namesmpgcyl
Mazda RX421.06
Mazda RX4 Wag21.06
Hornet 4 Drive21.46
Hornet Sportabout18.78
Valiant18.16
Merc 28019.26
Pontiac Firebird19.28
Ferrari Dino19.76

As you can see the only difference between sending a normal query and a parameterized query lies in the placeholder value in the query (>= ?) and the params argument of dbGetQuery(), which takes a list or a vector with the values you want to assign to the placeholders (in this case we have a vector containing the mpg and cyl variables).

Now, what happens when you want to execute a different query? In the example above, as you can see, I pretty much hand-crafted the query. It just allows the input of mpg and cyl values, and the query will only retrieve the cars with higher or equal mpg and cyl. However, there may be situations where you may want to be more flexible. What happens if a user wants to look at the cars that have the higher or equal horsepower and weight than a specific value as well? With the query above, you would have to go back and rewrite it, but you could write a function that would make that step unnecessary. Let's take a look at one:

# Assemble an example function that takes the SQLite database connection, a base query,
# and the parameters you want to use in the WHERE clause as a list
assembleQuery <- function(conn, base, search_parameters){
    parameter_names <- names(search_parameters)
    partial_queries <- ""
    # Iterate over all the parameters to assemble the query
    for(k in 1:length(parameter_names)){
        filter_k <- paste(parameter_names[k], " >= ? ")
        # If there is more than 1 parameter, add an AND statement before the parameter name and placeholder
        if(k > 1){
            filter_k <- paste("AND ", parameter_names[k], " >= ?")
        }
        partial_queries <- paste(partial_queries, filter_k)
    }
    # Paste all together into a single query using a WHERE statement
    final_paste <- paste(base, " WHERE", partial_queries)
    # Print the assembled query to show how it looks like
    print(final_paste)
    # Run the final query. I unlist the values from the search_parameters list into a vector since it is needed
    # when using various anonymous placeholders (i.e. >= ?)
    values <- unlist(search_parameters, use.names = FALSE)
    result <- dbGetQuery(conn, final_paste, params = values)
    # return the executed query
    return(result)
}

base <- "SELECT car_names, mpg, hp, wt FROM cars_data"
search_parameters <- list("mpg" = 16, "hp" = 150, "wt" = 2.1)
result <- assembleQuery(conn, base, search_parameters)
result
[1] "SELECT car_names, mpg, hp, wt FROM cars_data  WHERE  mpg  >= ?  AND  hp  >= ? AND  wt  >= ?"
car_namesmpghpwt
Hornet Sportabout18.71753.440
Merc 450SE16.41804.070
Merc 450SL17.31803.730
Pontiac Firebird19.21753.845
Ferrari Dino19.71752.770

The function above may be basic, but it illustrates how it is possible to write R code that generates SQL queries that can be executed on a SQLite database. I encourage you to keep exploring if you are interested in this use case. The function above can be enhanced in several ways starting with the removal of the assumption that the user providing the parameters will want to see the values that are higher or equal than those inputted.

Statements that do not Return Tabular Results

Sometimes, you may want to execute SQL queries that do not necessarily return tabular data. Examples of these operations are inserting, updating or deleting table records. For this, we can use the function dbExecute(), which takes as arguments a SQLite database connection and a SQL query. A couple of examples can be seen below:

# Visualize the table before deletion
dbGetQuery(conn, "SELECT * FROM cars_data LIMIT 10")
# Delete the column belonging to the Mazda RX4. You will see a 1 as the output.
dbExecute(conn, "DELETE FROM cars_data WHERE car_names = 'Mazda RX4'")
# Visualize the new table after deletion
dbGetQuery(conn, "SELECT * FROM cars_data LIMIT 10")
mpgcyldisphpdratwtqsecvsamgearcarbcar_names
21.06160.01103.902.62016.460144Mazda RX4
21.06160.01103.902.87517.020144Mazda RX4 Wag
22.84108.0933.852.32018.611141Datsun 710
21.46258.01103.083.21519.441031Hornet 4 Drive
18.78360.01753.153.44017.020032Hornet Sportabout
18.16225.01052.763.46020.221031Valiant
14.38360.02453.213.57015.840034Duster 360
24.44146.7623.693.19020.001042Merc 240D
22.84140.8953.923.15022.901042Merc 230
19.26167.61233.923.44018.301044Merc 280

1

mpgcyldisphpdratwtqsecvsamgearcarbcar_names
21.06160.01103.902.87517.020144Mazda RX4 Wag
22.84108.0933.852.32018.611141Datsun 710
21.46258.01103.083.21519.441031Hornet 4 Drive
18.78360.01753.153.44017.020032Hornet Sportabout
18.16225.01052.763.46020.221031Valiant
14.38360.02453.213.57015.840034Duster 360
24.44146.7623.693.19020.001042Merc 240D
22.84140.8953.923.15022.901042Merc 230
19.26167.61233.923.44018.301044Merc 280
17.86167.61233.923.44018.901044Merc 280C

Now lets try inserting the data of the Mazda RX4 back:

# Insert the data for the Mazda RX4. This will also ouput a 1
dbExecute(conn, "INSERT INTO cars_data VALUES (21.0,6,160.0,110,3.90,2.620,16.46,0,1,4,4,'Mazda RX4')")
# See that we re-introduced the Mazda RX4 succesfully at the end
dbGetQuery(conn, "SELECT * FROM cars_data")

1

mpgcyldisphpdratwtqsecvsamgearcarbcar_names
21.06160.01103.902.87517.020144Mazda RX4 Wag
22.84108.0933.852.32018.611141Datsun 710
21.46258.01103.083.21519.441031Hornet 4 Drive
18.78360.01753.153.44017.020032Hornet Sportabout
18.16225.01052.763.46020.221031Valiant
14.38360.02453.213.57015.840034Duster 360
24.44146.7623.693.19020.001042Merc 240D
22.84140.8953.923.15022.901042Merc 230
19.26167.61233.923.44018.301044Merc 280
17.86167.61233.923.44018.901044Merc 280C
16.48275.81803.074.07017.400033Merc 450SE
17.38275.81803.073.73017.600033Merc 450SL
15.28275.81803.073.78018.000033Merc 450SLC
10.48472.02052.935.25017.980034Cadillac Fleetwood
10.48460.02153.005.42417.820034Lincoln Continental
14.78440.02303.235.34517.420034Chrysler Imperial
32.4478.7664.082.20019.471141Fiat 128
30.4475.7524.931.61518.521142Honda Civic
33.9471.1654.221.83519.901141Toyota Corolla
21.54120.1973.702.46520.011031Toyota Corona
15.58318.01502.763.52016.870032Dodge Challenger
15.28304.01503.153.43517.300032AMC Javelin
13.38350.02453.733.84015.410034Camaro Z28
19.28400.01753.083.84517.050032Pontiac Firebird
27.3479.0664.081.93518.901141Fiat X1-9
26.04120.3914.432.14016.700152Porsche 914-2
30.4495.11133.771.51316.901152Lotus Europa
15.88351.02644.223.17014.500154Ford Pantera L
19.76145.01753.622.77015.500156Ferrari Dino
15.08301.03353.543.57014.600158Maserati Bora
21.44121.01094.112.78018.601142Volvo 142E
21.06160.01103.902.62016.460144Mazda RX4

As you can see, the last line of code added the Mazda RX4 to the end of the table. Once you are done operating with your SQLite database within R, it is important to call the function dbDisconnect(). This ensures that we release any resources that the database connection has been using, which is always a good practice.

# Close the database connection to CarsDB
dbDisconnect(conn)

Conclusion

In this tutorial, we have covered the essential functions needed to manipulate SQLite databases in R using RSQLite. SQLite databases, if appropriately used, are an incredibly useful tool when combined inside your R scripts. An example of the power of the intersection between R and SQLite can be seen in parameterized queries, which could be used if you need to query a database in order to display information based on user input inside an R Shiny app. Another example use case or parameterized queries could be virtual assistants or chatbots. If you want to learn more about this, I encourage you to look into DataCamp's Building Chatbots with Python course.

Writing SQLite tables by appending data frames, can also be very powerful. As I noted in the Beginners Guide to SQLite tutorial, this capability allowed me to complete a social network analysis project by appending the followers of several Twitter users of interest to a table as I gathered them. I used a SQLite database for this, to save time by avoiding the need to start from scratch in the case of a power outage or Windows update, which could forcefully shut down my computer. If something like that happened, all that I had to do was restart gathering the followers of the user that was next to the last one that I had in the database. To put it into perspective, the computation time to gather the followers of all my Twitter users of interest, took nearly 4 weeks to complete due to the rate limits of the Twitter API. Therefore, starting from zero due to a power outage or Windows update would have been very undesirable.

As always, I encourage you to keep learning about SQLite databases and how to interact with them through R. I strongly encourage you to dive deeper an check the previous tutorials covering their usage with Python and the command line, which can further help you become a full SQLite wiz. Keep learning; the sky is the limit!

トピック

関連

チュートリアル

How to Use SQLite in Python

チュートリアル

Beginners Guide to SQLite