Skip to Content
Course content

78: dbplyr for Remote Data Sources

Click on the "Edit" button in the top corner of the screen to edit your slide content.

I've run into this a lot in production: you're working with a dataset that's just too big for your laptop's RAM. Maybe it's a few hundred million rows of clickstream data or a massive retail ledger. Your first instinct is usually to pull the whole table into R and then filter it down, but that's a great way to freeze your entire system.

Let's look at how we handle this using dbplyr. I'm going to set up a quick SQLite database with a hypothetical store_sales table so we can walk through the logic together.

library(dplyr)
library(dbplyr)
library(RSQLite)

The RAM Wall

Usually, we're tempted to do something like dbReadTable(con, "store_sales"). I tried that on a project a few years back with a 50GB table, and R just gave up. The problem is that dbReadTable pulls the entire dataset into your local memory. We want the database to do the heavy lifting, not R.

Instead, let's try creating a reference to the table without actually loading the data:

# Connect to a dummy database
con <- dbConnect(RSQLite::SQLite(), ":memory:")
# (Assuming we've already populated 'store_sales' in the DB)

sales_remote <- tbl(con, "store_sales")

Now, if you print sales_remote to the console, you'll see something interesting. It looks like a tibble, but if you look closely at the header, it says # Source: dbplyr lazy table. This is the "magic" part. I haven't actually downloaded a single row of data yet. I've just told R, "Hey, there is a table over there called store_sales, and I want you to keep track of it."

Testing the Lazy Pipeline

Since it's a "lazy" table, I can start writing dplyr code as if the data were already in R. Let's say I only want the total revenue for the 'Electronics' department where the sale was over $100.

revenue_query <- sales_remote
%> filter(department == "Electronics", amount > 100)
%> summarise(total = sum(amount, na.rm = TRUE))

I'll run that, and... nothing happened. Well, not "nothing," but R didn't actually calculate the sum. It just updated the revenue_query object. If I print revenue_query now, it still says it's a lazy table. This is where a lot of people get confused; they think their code isn't working because they don't see the final number.

Peeking Under the Hood

I always do this when I'm debugging a dbplyr pipeline. I want to know exactly what SQL R is sending to the server. If the SQL is inefficient, the query will be slow, regardless of how clean my R code looks.

show_query(revenue_query)

When I run this, R spits out the actual SQL: SELECT sum("amount") AS "total" FROM "store_sales" WHERE ("department" = 'Electronics' AND "amount" > 100). That's the beauty of it. R isn't doing the math; it's translating my tidyverse verbs into a language the database understands. The database filters the millions of rows and only prepares the one single number I actually asked for.

The Moment of Truth: collect()

So, we have a query that is ready to go, but it's still "remote." To actually bring that result into my R session as a standard data frame, I need to use collect().

final_result <- revenue_query %> collect()

Now final_result is a regular R tibble. The crucial part here is that I called collect() at the very end of the chain. If I had called collect() right after sales_remote, I would have pulled the entire million-row table into memory, defeating the whole purpose of using dbplyr. Always filter and summarise as much as possible on the remote side before bringing the data home.




📋 Practical Task

Exercise: Optimizing the Regional Performance Report

You have been given access to a remote PostgreSQL database containing a table called web_logs. This table is massive (terabytes of data) and contains columns user_id, region, page_views, and session_duration.

Your goal is to calculate the average session_duration for users in the 'North America' region who had more than 5 page_views. You must do this without crashing the R session.

Requirements:

  • Use tbl() to create a remote reference to web_logs.
  • Use filter() to isolate 'North America' and page_views > 5.
  • Use summarise() to find the mean of session_duration.
  • Use show_query() to verify that the filtering is happening in SQL, not in R.
  • Use collect() as the final step to bring the single resulting average into your R environment.

Starter Code:

# Assume 'con' is already established
logs_remote <- tbl(con, "web_logs")

# Your code here...
Rating
0 0

There are no comments for now.

to be the first to leave a comment.