Curso
Como mencionei no fim do meu tutorial anterior, Beginners Guide to SQLite, bancos de dados SQLite ficam ainda mais poderosos quando usados junto com R e Python. No DataCamp, você já viu como operar bancos SQLite a partir do Python (confira o tutorial SQLite in Python do Sayak Paul para aprender a manipular bancos SQLite com o pacote sqlite3 no Python). Neste tutorial, porém, vamos focar em como usar bancos de dados SQLite no R com o pacote RSQLite.
Vamos passar pelo básico de como realizar tarefas essenciais, como enviar consultas para um banco SQLite ou criar tabelas usando o RSQLite. Além disso, vou mostrar como usar consultas parametrizadas e operações como INSERT ou DELETE, que não retornam resultados tabulares.
Criando bancos de dados e tabelas
O primeiro passo, como você deve imaginar, é criar um banco de dados. O RSQLite pode criar bancos SQLite efêmeros em memória, assim como quando você abre a linha de comando do SQLite. Porém, isso geralmente não é o que você quer. Então vamos criar um banco de dados de verdade para o conjunto de dados mtcars usando a função dbConnect(), que recebe os seguintes argumentos:
- drv: um driver de banco de dados
- path: o caminho para um banco de dados SQLite. Se for criar um novo, basta dar um nome de sua preferência, como faço abaixo. Se quiser operar com um banco transitório em memória, você pode omitir o argumento path ou usar ":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")
| mpg | cyl | disp | hp | drat | wt | qsec | vs | am | gear | carb | car_names |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 21.0 | 6 | 160 | 110 | 3.90 | 2.620 | 16.46 | 0 | 1 | 4 | 4 | Mazda RX4 |
| 21.0 | 6 | 160 | 110 | 3.90 | 2.875 | 17.02 | 0 | 1 | 4 | 4 | Mazda RX4 Wag |
| 22.8 | 4 | 108 | 93 | 3.85 | 2.320 | 18.61 | 1 | 1 | 4 | 1 | Datsun 710 |
| 21.4 | 6 | 258 | 110 | 3.08 | 3.215 | 19.44 | 1 | 0 | 3 | 1 | Hornet 4 Drive |
| 18.7 | 8 | 360 | 175 | 3.15 | 3.440 | 17.02 | 0 | 0 | 3 | 2 | Hornet Sportabout |
| 18.1 | 6 | 225 | 105 | 2.76 | 3.460 | 20.22 | 1 | 0 | 3 | 1 | Valiant |
Com o banco criado e os dados no formato certo, você pode criar uma tabela dentro do banco usando a função dbWriteTable(). Ela aceita vários argumentos, mas, por agora, vamos focar nos seguintes:
- conn: a conexão com seu banco SQLite
- name: o nome que você quer dar à sua tabela
- value: os dados que você quer inserir. Deve ser um data frame do R ou um objeto convertível em data frame.
Depois disso, você pode usar a função dbListTables() com a conexão do SQLite como argumento para verificar se a tabela foi criada com sucesso.
# 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'
Um recurso muito útil ao criar tabelas com o RSQLite é poder anexar mais dados a uma tabela existente usando um loop, caso você tenha vários data frames, definindo o argumento opcional append = TRUE dentro do dbWriteTable(). Por exemplo, vamos criar uma tabela simples com alguns carros e seus fabricantes anexando dois data frames diferentes:
# 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)
- 'Cars_and_Makes'
- 'cars_data'
Agora vamos conferir se todos os dados estão na nova tabela:
dbGetQuery(conn, "SELECT * FROM Cars_and_Makes")
| car | make |
|---|---|
| Camaro | Chevrolet |
| California | Ferrari |
| Mustang | Ford |
| Explorer | Ford |
| Corolla | Toyota |
| Lancer | Mitsubishi |
| Sportage | Kia |
| XE | Jaguar |
Executando consultas SQL
Como você viu acima, é possível executar consultas SQL válidas pelo RSQLite usando a função dbGetQuery(), que recebe os seguintes argumentos:
- conn: a conexão com o banco SQLite
- query: a consulta SQL que você quer executar como string
Para mostrar melhor a capacidade de executar consultas SQL com o RSQLite, vamos ver mais alguns exemplos na tabela cars_data:
Observação: é importante lembrar que, com o RSQLite, você pode executar qualquer consulta válida para um banco SQLite, desde SELECT simples até JOINS (exceto RIGHT OUTER JOIN e FULL OUTER JOIN, que não são permitidos no SQLite).
# Gather the first 10 rows in the cars_data table
dbGetQuery(conn, "SELECT * FROM cars_data LIMIT 10")
| mpg | cyl | disp | hp | drat | wt | qsec | vs | am | gear | carb | car_names |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 21.0 | 6 | 160.0 | 110 | 3.90 | 2.620 | 16.46 | 0 | 1 | 4 | 4 | Mazda RX4 |
| 21.0 | 6 | 160.0 | 110 | 3.90 | 2.875 | 17.02 | 0 | 1 | 4 | 4 | Mazda RX4 Wag |
| 22.8 | 4 | 108.0 | 93 | 3.85 | 2.320 | 18.61 | 1 | 1 | 4 | 1 | Datsun 710 |
| 21.4 | 6 | 258.0 | 110 | 3.08 | 3.215 | 19.44 | 1 | 0 | 3 | 1 | Hornet 4 Drive |
| 18.7 | 8 | 360.0 | 175 | 3.15 | 3.440 | 17.02 | 0 | 0 | 3 | 2 | Hornet Sportabout |
| 18.1 | 6 | 225.0 | 105 | 2.76 | 3.460 | 20.22 | 1 | 0 | 3 | 1 | Valiant |
| 14.3 | 8 | 360.0 | 245 | 3.21 | 3.570 | 15.84 | 0 | 0 | 3 | 4 | Duster 360 |
| 24.4 | 4 | 146.7 | 62 | 3.69 | 3.190 | 20.00 | 1 | 0 | 4 | 2 | Merc 240D |
| 22.8 | 4 | 140.8 | 95 | 3.92 | 3.150 | 22.90 | 1 | 0 | 4 | 2 | Merc 230 |
| 19.2 | 6 | 167.6 | 123 | 3.92 | 3.440 | 18.30 | 1 | 0 | 4 | 4 | Merc 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_names | hp | cyl |
|---|---|---|
| Hornet Sportabout | 175 | 8 |
| Duster 360 | 245 | 8 |
| Merc 450SE | 180 | 8 |
| Merc 450SL | 180 | 8 |
| Merc 450SLC | 180 | 8 |
| Cadillac Fleetwood | 205 | 8 |
| Lincoln Continental | 215 | 8 |
| Chrysler Imperial | 230 | 8 |
| Dodge Challenger | 150 | 8 |
| AMC Javelin | 150 | 8 |
| Camaro Z28 | 245 | 8 |
| Pontiac Firebird | 175 | 8 |
| Ford Pantera L | 264 | 8 |
| Maserati Bora | 335 | 8 |
# 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_names | hp | cyl |
|---|---|---|
| Mazda RX4 | 110 | 6 |
| Mazda RX4 Wag | 110 | 6 |
| Merc 280 | 123 | 6 |
| Merc 280C | 123 | 6 |
| Merc 450SE | 180 | 8 |
| Merc 450SL | 180 | 8 |
| Merc 450SLC | 180 | 8 |
| Maserati Bora | 335 | 8 |
# 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")
| cyl | average_hp | average_mpg |
|---|---|---|
| 4 | 82.63636 | 26.66364 |
| 6 | 122.28571 | 19.74286 |
| 8 | 209.21429 | 15.10000 |
Para guardar os resultados das suas consultas e fazer outras operações no R depois, como um data frame, basta atribuir o resultado da consulta a uma variável.
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)
| cyl | average_hp |
|---|---|
| 4 | 82.63636 |
| 6 | 122.28571 |
| 8 | 209.21429 |
'data.frame'
Inserindo variáveis em consultas (consultas parametrizadas)
Uma das maiores vantagens de operar bancos SQLite a partir do R é a possibilidade de usar consultas parametrizadas. Ou seja, usar variáveis disponíveis no seu ambiente do R para consultar seu banco SQLite. Veja um exemplo de como usar uma variável dentro de uma consulta SQLite:
# 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_names | mpg | cyl |
|---|---|---|
| Mazda RX4 | 21.0 | 6 |
| Mazda RX4 Wag | 21.0 | 6 |
| Hornet 4 Drive | 21.4 | 6 |
| Hornet Sportabout | 18.7 | 8 |
| Valiant | 18.1 | 6 |
| Merc 280 | 19.2 | 6 |
| Pontiac Firebird | 19.2 | 8 |
| Ferrari Dino | 19.7 | 6 |
Como você pode ver, a única diferença entre enviar uma consulta normal e uma consulta parametrizada está no uso do placeholder na consulta (>= ?) e no argumento params de dbGetQuery(), que recebe uma lista ou um vetor com os valores que você quer atribuir aos placeholders (neste caso, um vetor contendo as variáveis mpg e cyl).
Agora, o que acontece quando você quer executar uma consulta diferente? No exemplo acima, como dá para ver, eu praticamente escrevi a consulta à mão. Ela só permite informar valores de mpg e cyl e a consulta apenas retorna os carros com mpg e cyl maiores ou iguais. Mas pode haver situações em que você queira ser mais flexível. E se um usuário quiser ver carros com potência e peso maiores ou iguais a um certo valor também? Com a consulta acima, você teria que reescrevê-la. Em vez disso, dá para escrever uma função que evita esse retrabalho. Veja um exemplo:
# 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_names | mpg | hp | wt |
|---|---|---|---|
| Hornet Sportabout | 18.7 | 175 | 3.440 |
| Merc 450SE | 16.4 | 180 | 4.070 |
| Merc 450SL | 17.3 | 180 | 3.730 |
| Pontiac Firebird | 19.2 | 175 | 3.845 |
| Ferrari Dino | 19.7 | 175 | 2.770 |
A função acima é simples, mas mostra como é possível escrever código em R que gera consultas SQL executáveis em um banco SQLite. Se esse caso de uso te interessa, vale explorar mais. Dá para melhorar a função de várias formas, começando por remover a suposição de que quem fornece os parâmetros sempre quer ver valores maiores ou iguais aos informados.
Instruções que não retornam resultados tabulares
Às vezes, você pode querer executar consultas SQL que não retornam dados em forma de tabela. Exemplos: inserir, atualizar ou excluir registros. Para isso, usamos a função dbExecute(), que recebe como argumentos uma conexão SQLite e uma consulta SQL. Veja alguns exemplos:
# 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")
| mpg | cyl | disp | hp | drat | wt | qsec | vs | am | gear | carb | car_names |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 21.0 | 6 | 160.0 | 110 | 3.90 | 2.620 | 16.46 | 0 | 1 | 4 | 4 | Mazda RX4 |
| 21.0 | 6 | 160.0 | 110 | 3.90 | 2.875 | 17.02 | 0 | 1 | 4 | 4 | Mazda RX4 Wag |
| 22.8 | 4 | 108.0 | 93 | 3.85 | 2.320 | 18.61 | 1 | 1 | 4 | 1 | Datsun 710 |
| 21.4 | 6 | 258.0 | 110 | 3.08 | 3.215 | 19.44 | 1 | 0 | 3 | 1 | Hornet 4 Drive |
| 18.7 | 8 | 360.0 | 175 | 3.15 | 3.440 | 17.02 | 0 | 0 | 3 | 2 | Hornet Sportabout |
| 18.1 | 6 | 225.0 | 105 | 2.76 | 3.460 | 20.22 | 1 | 0 | 3 | 1 | Valiant |
| 14.3 | 8 | 360.0 | 245 | 3.21 | 3.570 | 15.84 | 0 | 0 | 3 | 4 | Duster 360 |
| 24.4 | 4 | 146.7 | 62 | 3.69 | 3.190 | 20.00 | 1 | 0 | 4 | 2 | Merc 240D |
| 22.8 | 4 | 140.8 | 95 | 3.92 | 3.150 | 22.90 | 1 | 0 | 4 | 2 | Merc 230 |
| 19.2 | 6 | 167.6 | 123 | 3.92 | 3.440 | 18.30 | 1 | 0 | 4 | 4 | Merc 280 |
1
| mpg | cyl | disp | hp | drat | wt | qsec | vs | am | gear | carb | car_names |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 21.0 | 6 | 160.0 | 110 | 3.90 | 2.875 | 17.02 | 0 | 1 | 4 | 4 | Mazda RX4 Wag |
| 22.8 | 4 | 108.0 | 93 | 3.85 | 2.320 | 18.61 | 1 | 1 | 4 | 1 | Datsun 710 |
| 21.4 | 6 | 258.0 | 110 | 3.08 | 3.215 | 19.44 | 1 | 0 | 3 | 1 | Hornet 4 Drive |
| 18.7 | 8 | 360.0 | 175 | 3.15 | 3.440 | 17.02 | 0 | 0 | 3 | 2 | Hornet Sportabout |
| 18.1 | 6 | 225.0 | 105 | 2.76 | 3.460 | 20.22 | 1 | 0 | 3 | 1 | Valiant |
| 14.3 | 8 | 360.0 | 245 | 3.21 | 3.570 | 15.84 | 0 | 0 | 3 | 4 | Duster 360 |
| 24.4 | 4 | 146.7 | 62 | 3.69 | 3.190 | 20.00 | 1 | 0 | 4 | 2 | Merc 240D |
| 22.8 | 4 | 140.8 | 95 | 3.92 | 3.150 | 22.90 | 1 | 0 | 4 | 2 | Merc 230 |
| 19.2 | 6 | 167.6 | 123 | 3.92 | 3.440 | 18.30 | 1 | 0 | 4 | 4 | Merc 280 |
| 17.8 | 6 | 167.6 | 123 | 3.92 | 3.440 | 18.90 | 1 | 0 | 4 | 4 | Merc 280C |
Agora vamos inserir novamente os dados do Mazda RX4:
# 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
| mpg | cyl | disp | hp | drat | wt | qsec | vs | am | gear | carb | car_names |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 21.0 | 6 | 160.0 | 110 | 3.90 | 2.875 | 17.02 | 0 | 1 | 4 | 4 | Mazda RX4 Wag |
| 22.8 | 4 | 108.0 | 93 | 3.85 | 2.320 | 18.61 | 1 | 1 | 4 | 1 | Datsun 710 |
| 21.4 | 6 | 258.0 | 110 | 3.08 | 3.215 | 19.44 | 1 | 0 | 3 | 1 | Hornet 4 Drive |
| 18.7 | 8 | 360.0 | 175 | 3.15 | 3.440 | 17.02 | 0 | 0 | 3 | 2 | Hornet Sportabout |
| 18.1 | 6 | 225.0 | 105 | 2.76 | 3.460 | 20.22 | 1 | 0 | 3 | 1 | Valiant |
| 14.3 | 8 | 360.0 | 245 | 3.21 | 3.570 | 15.84 | 0 | 0 | 3 | 4 | Duster 360 |
| 24.4 | 4 | 146.7 | 62 | 3.69 | 3.190 | 20.00 | 1 | 0 | 4 | 2 | Merc 240D |
| 22.8 | 4 | 140.8 | 95 | 3.92 | 3.150 | 22.90 | 1 | 0 | 4 | 2 | Merc 230 |
| 19.2 | 6 | 167.6 | 123 | 3.92 | 3.440 | 18.30 | 1 | 0 | 4 | 4 | Merc 280 |
| 17.8 | 6 | 167.6 | 123 | 3.92 | 3.440 | 18.90 | 1 | 0 | 4 | 4 | Merc 280C |
| 16.4 | 8 | 275.8 | 180 | 3.07 | 4.070 | 17.40 | 0 | 0 | 3 | 3 | Merc 450SE |
| 17.3 | 8 | 275.8 | 180 | 3.07 | 3.730 | 17.60 | 0 | 0 | 3 | 3 | Merc 450SL |
| 15.2 | 8 | 275.8 | 180 | 3.07 | 3.780 | 18.00 | 0 | 0 | 3 | 3 | Merc 450SLC |
| 10.4 | 8 | 472.0 | 205 | 2.93 | 5.250 | 17.98 | 0 | 0 | 3 | 4 | Cadillac Fleetwood |
| 10.4 | 8 | 460.0 | 215 | 3.00 | 5.424 | 17.82 | 0 | 0 | 3 | 4 | Lincoln Continental |
| 14.7 | 8 | 440.0 | 230 | 3.23 | 5.345 | 17.42 | 0 | 0 | 3 | 4 | Chrysler Imperial |
| 32.4 | 4 | 78.7 | 66 | 4.08 | 2.200 | 19.47 | 1 | 1 | 4 | 1 | Fiat 128 |
| 30.4 | 4 | 75.7 | 52 | 4.93 | 1.615 | 18.52 | 1 | 1 | 4 | 2 | Honda Civic |
| 33.9 | 4 | 71.1 | 65 | 4.22 | 1.835 | 19.90 | 1 | 1 | 4 | 1 | Toyota Corolla |
| 21.5 | 4 | 120.1 | 97 | 3.70 | 2.465 | 20.01 | 1 | 0 | 3 | 1 | Toyota Corona |
| 15.5 | 8 | 318.0 | 150 | 2.76 | 3.520 | 16.87 | 0 | 0 | 3 | 2 | Dodge Challenger |
| 15.2 | 8 | 304.0 | 150 | 3.15 | 3.435 | 17.30 | 0 | 0 | 3 | 2 | AMC Javelin |
| 13.3 | 8 | 350.0 | 245 | 3.73 | 3.840 | 15.41 | 0 | 0 | 3 | 4 | Camaro Z28 |
| 19.2 | 8 | 400.0 | 175 | 3.08 | 3.845 | 17.05 | 0 | 0 | 3 | 2 | Pontiac Firebird |
| 27.3 | 4 | 79.0 | 66 | 4.08 | 1.935 | 18.90 | 1 | 1 | 4 | 1 | Fiat X1-9 |
| 26.0 | 4 | 120.3 | 91 | 4.43 | 2.140 | 16.70 | 0 | 1 | 5 | 2 | Porsche 914-2 |
| 30.4 | 4 | 95.1 | 113 | 3.77 | 1.513 | 16.90 | 1 | 1 | 5 | 2 | Lotus Europa |
| 15.8 | 8 | 351.0 | 264 | 4.22 | 3.170 | 14.50 | 0 | 1 | 5 | 4 | Ford Pantera L |
| 19.7 | 6 | 145.0 | 175 | 3.62 | 2.770 | 15.50 | 0 | 1 | 5 | 6 | Ferrari Dino |
| 15.0 | 8 | 301.0 | 335 | 3.54 | 3.570 | 14.60 | 0 | 1 | 5 | 8 | Maserati Bora |
| 21.4 | 4 | 121.0 | 109 | 4.11 | 2.780 | 18.60 | 1 | 1 | 4 | 2 | Volvo 142E |
| 21.0 | 6 | 160.0 | 110 | 3.90 | 2.620 | 16.46 | 0 | 1 | 4 | 4 | Mazda RX4 |
Como dá para ver, a última linha de código adicionou o Mazda RX4 ao fim da tabela. Quando terminar de operar com seu banco SQLite no R, é importante chamar a função dbDisconnect(). Isso garante que os recursos usados pela conexão com o banco sejam liberados — uma boa prática sempre.
# Close the database connection to CarsDB
dbDisconnect(conn)
Conclusão
Neste tutorial, cobrimos as funções essenciais para manipular bancos SQLite no R usando o RSQLite. Bancos SQLite, quando bem utilizados, são ferramentas incrivelmente úteis dentro dos seus scripts em R. Um exemplo do poder da interseção entre R e SQLite aparece nas consultas parametrizadas, que você pode usar para consultar um banco e exibir informações com base em entradas do usuário em um app R Shiny. Outro uso possível para consultas parametrizadas são assistentes virtuais ou chatbots. Se quiser saber mais, recomendo o curso Building Chatbots with Python do DataCamp.
Escrever tabelas no SQLite anexando data frames também é muito poderoso. Como destaquei no tutorial Beginners Guide to SQLite, esse recurso me permitiu concluir um projeto de análise de redes sociais anexando os seguidores de vários usuários do Twitter de interesse a uma tabela conforme eu os coletava. Usei um banco SQLite para ganhar tempo e evitar ter que começar do zero em caso de queda de energia ou atualização do Windows que desligasse o computador à força. Se algo assim acontecesse, tudo que eu precisava fazer era retomar a coleta dos seguidores a partir do próximo usuário após o último salvo no banco. Para você ter uma ideia, o tempo de processamento para coletar os seguidores de todos os meus usuários de interesse levou quase 4 semanas por causa dos limites de taxa da API do Twitter. Portanto, recomeçar do zero por conta de uma queda de energia ou atualização seria péssimo.
Como sempre, incentivo você a continuar aprendendo sobre bancos de dados SQLite e como interagir com eles via R. Vale muito a pena mergulhar e conferir os tutoriais anteriores que cobrem o uso com Python e a linha de comando — isso vai te ajudar a se tornar fera em SQLite. Continue aprendendo: o céu é o limite!

