SQLite e bancos relacionais
Módulo 2 · Dados
Você tem três planilhas separadas: pacientes.csv (uma linha por paciente, dados demográficos), internacoes.csv (uma linha por internação, várias por paciente), prescricoes.csv (uma linha por medicamento prescrito, várias por internação). A pergunta que você quer responder é: “qual a duração média de internação dos pacientes que receberam Anticoagulante X em internações posteriores a 2024?” Resposta exige cruzar as três tabelas pelas chaves de paciente e internação. Em R com dplyr dá pra fazer; em Python com pandas também. Mas se isso vira workflow recorrente, com tabelas grandes e queries complexas, há uma ferramenta melhor: o banco relacional.
E quando “banco relacional” entra na conversa, a primeira opção que vale considerar — antes de instalar Postgres ou MySQL — é o SQLite. Este capítulo é sobre por que SQLite cabe em pesquisa científica.
O banco mais ubíquo da história
A primeira coisa que choca quem aprende sobre SQLite é a escala da adoção. O SQLite é, com larga vantagem, o banco de dados mais usado do mundo (SQLite Consortium, 2024). Não está em segundo lugar competindo com MySQL ou Postgres — está em primeiro lugar absoluto, e a distância para o segundo é enorme. O motivo: SQLite roda em todo lugar:
- Todo iPhone e iPad. O sistema iOS usa SQLite para mensagens, contatos, fotos, histórico do Safari, e dezenas de outros sistemas internos.
- Todo Android. Idem — o sistema operacional inteiro depende de SQLite.
- Todo navegador moderno. Chrome, Firefox e Safari usam SQLite para histórico, favoritos, cache, armazenamento local de aplicações web.
- Aviões e dispositivos embarcados. Sistemas críticos de aviação Airbus usam SQLite. Carros, dispositivos médicos, set-top boxes, leitores de e-book — qualquer aparelho minimamente complexo provavelmente tem SQLite dentro.
A estimativa do projeto SQLite é de mais de 1 trilhão de bancos SQLite ativos no mundo, em 2024. Para comparação, isso é mais que todos os outros bancos de dados somados, em ordem de magnitude.
O que é um banco relacional, em poucas linhas
Para quem nunca usou banco relacional formalmente, vale o resumo. A ideia central é três:
1. Múltiplas tabelas relacionadas. Em vez de uma tabela única, você tem várias tabelas pequenas com relacionamentos explícitos. Pacientes tem uma chave única (id); cada internação tem paciente_id apontando para a tabela de pacientes; cada prescrição tem internacao_id. As tabelas se conectam por essas chaves.
2. SQL para consultar. SQL (Structured Query Language) é uma linguagem declarativa para descrever o que você quer, sem dizer como o banco deve buscar. O banco se encarrega de planejar a busca eficiente:
SELECT p.id, AVG(i.duracao_dias) AS media_internacao
FROM pacientes p
JOIN internacoes i ON i.paciente_id = p.id
JOIN prescricoes pr ON pr.internacao_id = i.id
WHERE pr.medicamento = 'Anticoagulante X'
AND i.data_admissao > '2024-01-01'
GROUP BY p.id;Essa query cruza três tabelas, filtra por dois critérios, e calcula uma agregação — tudo numa única expressão declarativa.
3. Garantias de consistência (ACID). Bancos relacionais oferecem garantias formais: ou uma operação completa por inteiro, ou não acontece nada (atomicidade); o banco nunca fica num estado inválido (consistência); operações concorrentes não interferem entre si (isolamento); uma vez commitada, a operação persiste mesmo após queda de energia (durabilidade). Essas garantias importam quando você está coletando dados em tempo real ou em ambiente colaborativo.
SQLite vs Postgres/MySQL: quando vale qual
SQLite é diferente dos bancos “tradicionais” em uma característica central: não é cliente-servidor. Não há processo sqlite-server rodando no fundo. SQLite é uma biblioteca — libsqlite3.dylib em Mac, sqlite3.dll em Windows — que seu programa carrega e usa direto. O “banco” é um único arquivo (.sqlite ou .db) no disco. Programas diferentes podem abrir o mesmo arquivo (com cuidado para concorrência), mas não há servidor mediando.
Isso muda o trade-off:
| Característica | SQLite | Postgres / MySQL |
|---|---|---|
| Setup | Zero (arquivo .sqlite é o banco) |
Instalar servidor, configurar usuários, abrir porta |
| Concorrência | Limitada (multi-leitura ok, multi-escrita serializada) | Plena (milhares de conexões simultâneas) |
| Tamanho | Bom até dezenas de GB; viável até centenas | Sem limite prático |
| Distribuído | Não — um arquivo numa máquina | Sim — replicação, sharding, cluster |
| Backup | cp banco.sqlite backup.sqlite |
Ferramentas dedicadas, dump SQL, replicação |
| Compartilhamento | Mandar o .sqlite por email funciona |
Exige acesso a servidor |
Para pesquisa típica (cohort de centenas a milhares de pacientes, datasets que cabem em uma máquina, projeto colaborativo de poucas pessoas), SQLite é absolutamente adequado e mais simples de gerenciar. Para registros nacionais multi-instituição com escrita concorrente massiva, faz sentido um Postgres dedicado.
Se a pergunta começa com “qual banco devo usar para…”, e o projeto cabe numa máquina, a resposta default é SQLite. Você só sai dele quando descobre uma necessidade específica que ele não atende — não antes.
SQLite em pesquisa: três casos típicos
1. Dados que ultrapassam memória RAM. Você tem um dataset de 30 GB e sua máquina tem 16 GB de RAM. Carregar tudo no R é impossível. Mas SQLite consegue ler/escrever sem precisar carregar tudo — o arquivo fica no disco, e queries leem só os pedaços necessários:
library(DBI)
library(RSQLite)
con <- dbConnect(RSQLite::SQLite(), "dados/coorte.sqlite")
# Query opera no banco, retorna só o resultado pequeno
resultado <- dbGetQuery(con, "
SELECT idade_grupo, AVG(pas_sistolica) as media_pas
FROM pacientes
WHERE ano_inclusao = 2024
GROUP BY idade_grupo
")
dbDisconnect(con)2. Dados estruturados em múltiplas tabelas relacionadas. Coorte com pacientes, exames, internações, prescrições — naturalmente relacionais. Modelar como banco SQLite preserva a estrutura e permite consultas que envolvem cruzamento.
3. Dados que se acumulam ao longo do tempo. Um sistema de coleta diária de dados clínicos. Em vez de criar dados_2026-05-04.csv, dados_2026-05-05.csv, etc. e ter que concatenar tudo na análise, mantém um único coorte.sqlite que cresce — INSERT novos registros sem precisar reescrever o arquivo inteiro.
Lendo SQLite em R e Python
Em R, via DBI + RSQLite:
library(DBI)
library(RSQLite)
library(dplyr)
con <- dbConnect(RSQLite::SQLite(), "dados/coorte.sqlite")
# Listar tabelas existentes
dbListTables(con)
# Query SQL direta
dbGetQuery(con, "SELECT * FROM pacientes WHERE idade > 65 LIMIT 10")
# Ou com dplyr (escreve dplyr, gera SQL automaticamente)
pacientes <- tbl(con, "pacientes")
pacientes |>
filter(idade > 65) |>
count(sexo) |>
collect()
dbDisconnect(con)Em Python, via sqlite3 (biblioteca-padrão) ou pandas:
import pandas as pd
import sqlite3
con = sqlite3.connect("dados/coorte.sqlite")
# Query SQL direta retorna DataFrame
dados = pd.read_sql("SELECT * FROM pacientes WHERE idade > 65 LIMIT 10", con)
# Lista tabelas
tabelas = pd.read_sql("SELECT name FROM sqlite_master WHERE type='table'", con)
con.close()A interface dplyr + dbplyr em R é especialmente elegante: você escreve a análise em sintaxe dplyr familiar, e ela é traduzida em SQL e executada no banco. Você ganha SQL sem precisar escrever SQL.
Conexão com IA
Agentes são particularmente úteis com SQL/SQLite por dois motivos:
1. Tradução de pergunta em linguagem natural para SQL. “Quero a média de pressão arterial sistólica por grupo etário em pacientes incluídos depois de 2024, ordenado do mais alto para o mais baixo, mas só grupos com pelo menos 30 pacientes.” O agente devolve a query SQL pronta. Para quem não escreve SQL com fluência, isso é transformador.
2. Modelagem inicial do banco. “Tenho dados de pacientes, internações e prescrições — me proponha o esquema relacional inicial em SQLite, com chaves primárias e estrangeiras.” O agente devolve CREATE TABLE statements consistentes, com chaves bem desenhadas.
SQL é fácil de gerar errado de jeitos sutis. Um JOIN no lugar errado pode multiplicar linhas. Um WHERE antes de um GROUP BY filtra coisa diferente de um HAVING depois. Para queries que vão alimentar análise científica, rode primeiro num subset pequeno, confira contagens, valide manualmente o resultado de algumas linhas, antes de aplicar no banco completo. Especialmente com queries geradas por IA — agente raramente avisa quando a query está sintaticamente válida mas semanticamente errada.
O que vem a seguir
Os cinco capítulos anteriores cobriram formatos — como armazenar dados. Os dois capítulos finais do bloco mudam o foco para estrutura — como organizar os dados (sejam eles em qualquer formato) de modo que a análise seja eficiente e reprodutível. O próximo trata do conceito que organiza tudo: tidy data, ou dados arrumados — o paper de 2014 de Hadley Wickham que mudou como ciência de dados é praticada em R e Python.