mygems

Como executar consultas SQL em um arquivo CSV sem banco de dados (2026 guide)

Você não precisa de Postgres para um GROUP BY. Consulte um CSV direto com DuckDB, SQLite, q ou o celular e veja qual ferramenta serve para cada caso.

Por uos ·

A versão curta: aponte o DuckDB para o arquivo.

duckdb -c "SELECT region, SUM(amount) FROM 'sales.csv' GROUP BY region"

Sem esquema, sem importação, sem servidor, sem CREATE TABLE. O CSV fica exatamente onde está e a linha de cabeçalho vira os nomes das colunas. Se você for instalar só uma coisa hoje, instale esta.

O resto desta página explica por que isso funciona, o que fazer quando você não pode usar o DuckDB e como lidar com as duas coisas que pegam todo mundo: as aspas no nome do arquivo e as colunas cujos nomes têm espaços.

Por que você não precisa carregar o arquivo em lugar nenhum

O instinto de «primeiro colocar os dados num banco» é um resquício do tempo em que os motores SQL só sabiam ler o próprio armazenamento. Os motores modernos não têm mais essa limitação. DuckDB, SQLite e várias ferramentas pequenas de linha de comando tratam um CSV como uma tabela durante uma única consulta, inferem os tipos por amostragem, respondem e esquecem tudo.

Isso muda a conta por completo. Uma pergunta pontual sobre um arquivo que chegou por e-mail não vale mais um esquema, um script de carga e uma etapa de limpeza. Vale uma linha.

Carregar num banco de verdade continua sendo a escolha certa em exatamente dois casos: os dados vão ser consultados repetidamente por mais de uma pessoa, ou precisam de restrições e relacionamentos que um único arquivo plano não consegue expressar. Nenhum dos dois descreve «qual é o total por região nesta exportação».

As opções, com honestidade

FerramentaInstalaçãoMelhor paraFraca em
DuckDBUm binário, sem dependênciasTudo até dezenas de GBQuase nada nesta tarefa
SQLite + .importJá vem no macOS e no LinuxGuardar a tabela para depoisTipos: tudo entra como TEXT
qpip install q-text-as-dataOne-liners rápidos num pipeline de shellArquivos grandes; é mais lento
csvsql (csvkit)pip install csvkitGerar um CREATE TABLE para um banco de verdadeVelocidade: é o mais lento daqui
Pandas + pandasqlVocê já tem o pandasQuando a resposta alimenta mais código PythonO dialeto SQL é o do SQLite, não o do Postgres
App de celular com SQLApp Store / PlayO arquivo chegou no seu e-mail, no celularQualquer coisa que você queira automatizar

O ranking honesto é curto: DuckDB para quase tudo, SQLite quando você não pode instalar nada e um app de celular quando você não está num computador. Os outros são bons e têm seus momentos, mas nenhum supera o DuckDB nesta tarefa específica.

DuckDB, direito

A instalação é um único binário: brew install duckdb, ou baixe e coloque no seu path. Depois:

duckdb
D SELECT * FROM 'orders.csv' LIMIT 10;

Duas coisas para saber logo de cara:

O nome do arquivo precisa de aspas simples. FROM orders.csv é interpretado como um esquema chamado orders e uma tabela chamada csv, e a mensagem de erro não deixa isso claro. FROM 'orders.csv' é o certo.

Nomes de coluna com espaços precisam de aspas duplas. SELECT "Order Date" FROM …. Com aspas simples você recebe o texto literal, em silêncio, em todas as linhas, o que faz parecer que a consulta funcionou.

Use um glob para ler um diretório inteiro quando a exportação veio em partes mensais:

SELECT * FROM 'exports/*.csv'

Quando a inferência de tipos erra, geralmente numa coluna de IDs com zeros à esquerda ou em datas num formato que ela não reconhece, assuma o controle da leitura em vez de brigar com ela:

SELECT * FROM read_csv('orders.csv',
  header = true,
  columns = {'id': 'VARCHAR', 'ordered_at': 'DATE', 'amount': 'DOUBLE'},
  dateformat = '%d/%m/%Y'
)

O read_csv também aceita delim, quote, escape, ignore_errors = true e sample_size = -1 (varrer o arquivo inteiro antes de decidir os tipos, em vez das primeiras 20.480 linhas). Este último é a solução quando uma coluna parece numérica por dez mil linhas e depois encontra N/A.

Grave a resposta em arquivo sem sair do SQL:

COPY (SELECT * FROM 'orders.csv' WHERE amount > 1000)
TO 'large-orders.csv' (HEADER, DELIMITER ',');

E se o arquivo for consultado mais do que meia dúzia de vezes, converta-o uma vez com COPY (SELECT * FROM 'orders.csv') TO 'orders.parquet' e consulte o Parquet. Ele tem tipos, é comprimido e costuma ser de cinco a vinte vezes mais rápido de varrer.

SQLite, quando você não pode instalar nada

O sqlite3 já vem no macOS e na maioria das distribuições Linux, então esta é a opção que funciona numa máquina bloqueada.

sqlite3 shop.db
sqlite> .mode csv
sqlite> .import orders.csv orders
sqlite> SELECT region, COUNT(*) FROM orders GROUP BY region;

A pegadinha, e ela importa: o .import transforma toda coluna em TEXT. A ordenação passa a ser lexicográfica, então 100 vem antes de 20, e o SUM() dá surpresas. Faça o cast na consulta (CAST(amount AS REAL)) ou crie a tabela com tipos reais antes de importar.

Ao contrário do DuckDB, isso deixa um shop.db para trás, o que é bom se você quiser a tabela de novo amanhã e bagunça se não quiser.

q, para um pipeline de shell

Quando a consulta é um passo numa cadeia de comandos de shell, o q lê a entrada padrão como qualquer outra ferramenta Unix:

q -H -d, "SELECT region, SUM(amount) FROM - GROUP BY region" < orders.csv

-H indica que a primeira linha é cabeçalho, -d, define o delimitador e - é o stdin. Por baixo é SQLite, então o dialeto é o do SQLite. É também a opção razoável mais lenta num arquivo grande: tudo bem para algumas centenas de milhares de linhas, penoso além disso.

Quando o arquivo está no seu celular

Nada disso ajuda quando a exportação é um anexo no seu e-mail e você não está numa mesa. Isso acontece com frequência, e a resposta de sempre, «mande para você mesmo e olhe depois», é ruim.

CSV File Viewer - Smart CSViPhone e iPad · Android

Executa SQL de verdade no arquivo, no iOS e no Android, com um construtor visual de filtros para quando você prefere não digitar uma consulta no teclado do celular e um assistente em linguagem simples para quando você ainda não sabe os nomes das colunas.

O que ele faz

O dialeto é o do SQLite, então as consultas deste artigo se aplicam com as ressalvas de sempre: sem as funções de janela dos exemplos do DuckDB, e valem os mesmos hábitos com CAST.

Consultas para deixar à mão

O que tem neste arquivo:

SELECT * FROM 'file.csv' LIMIT 20;
DESCRIBE SELECT * FROM 'file.csv';
SELECT COUNT(*) FROM 'file.csv';

Onde estão os nulos, já que ir coluna por coluna é tedioso:

SELECT COUNT(*) - COUNT(email) AS missing_email,
       COUNT(*) - COUNT(phone) AS missing_phone
FROM 'contacts.csv';

Duplicatas numa chave:

SELECT id, COUNT(*) AS n
FROM 'orders.csv'
GROUP BY id HAVING n > 1
ORDER BY n DESC;

Juntar duas exportações é a consulta que mais justifica todo o exercício, porque fazer isso com PROCV entre duas planilhas é bem pior:

SELECT o.id, o.amount, c.name
FROM 'orders.csv' o
JOIN 'customers.csv' c ON c.id = o.customer_id;

Um total mensal a partir de uma coluna de datas:

SELECT date_trunc('month', ordered_at) AS month, SUM(amount)
FROM 'orders.csv'
GROUP BY month ORDER BY month;

Quando algo dá errado

Tudo veio numa coluna só. O delimitador não é vírgula; ponto e vírgula é o padrão em boa parte da Europa. read_csv('f.csv', delim = ';'). Se o arquivo for mais estranho que isso, ele pode estar realmente malformado e não apenas incomum.

Uma coluna numérica não soma. Ela foi inferida como texto porque aparece algo não numérico nela. Encontre o culpado com SELECT amount FROM 'f.csv' WHERE TRY_CAST(amount AS DOUBLE) IS NULL LIMIT 10;. Geralmente é um símbolo de moeda, um separador de milhar ou a string N/A.

Os zeros à esquerda sumiram. A coluna foi lida como número. Declare-a como VARCHAR em columns e não deixe uma planilha chegar perto dela depois.

A linha de cabeçalho se repete pelo arquivo. Várias exportações foram concatenadas. WHERE id <> 'id' é a correção grosseira e geralmente a correta.

Acabou a memória. O DuckDB processa em fluxo, então isso normalmente significa um ORDER BY ou um DISTINCT sobre o arquivo inteiro. Dê a ele um diretório temporário (SET temp_directory = '/tmp/duckdb') e ele passa a gravar em disco. Ou leia como abrir um arquivo CSV de 1 GB para a questão mais ampla do que dá conta de quê em grande escala.

Perguntas

Dá para executar SQL num arquivo CSV sem importá-lo?
Dá. O DuckDB consulta um CSV no lugar com `SELECT * FROM 'file.csv'`, inferindo os nomes das colunas pela linha de cabeçalho e os tipos por amostragem. Nada é importado, nenhuma tabela é criada e o arquivo não muda. O SQLite também consegue, mas a etapa .import cria uma tabela persistente.
Qual é a forma mais rápida de consultar um CSV grande com SQL?
O DuckDB, com ampla vantagem entre as opções sem configuração. Ele lê o arquivo em paralelo e processa em fluxo em vez de carregar tudo na memória, então lida com arquivos muito maiores que a RAM. Se você vai consultar o mesmo arquivo várias vezes, converta-o uma vez para Parquet com COPY … TO 'file.parquet' e as varreduras ficam de cinco a vinte vezes mais rápidas.
Como consulto uma coluna de CSV cujo nome tem espaço?
Coloque-a entre aspas duplas: SELECT "Order Date" FROM 'orders.csv'. Aspas simples produzem um literal de string, que devolve o mesmo texto em todas as linhas em vez de um erro, então a consulta parece ter funcionado.
Posso executar consultas SQL num arquivo CSV no meu celular?
Pode. O Smart CSV Viewer executa SQL num CSV no iOS e no Android, com um construtor visual de filtros como alternativa a digitar consultas no teclado do celular. O motor é o SQLite, então o dialeto é o do SQLite e não o do DuckDB.
Por que minha coluna numérica não soma?
Ela foi inferida como texto porque aparece algo não numérico em algum lugar: um símbolo de moeda, um separador de milhar ou um marcador como N/A. Encontre as linhas problemáticas com TRY_CAST(col AS DOUBLE) IS NULL e depois limpe-as, ou leia a coluna de forma explícita com o argumento `columns` do read_csv.
Quando devo carregar um CSV num banco de dados de verdade?
Em dois casos: os dados vão ser consultados repetidamente por mais de uma pessoa, ou precisam de restrições e relacionamentos que um único arquivo plano não consegue expressar. Uma pergunta pontual sobre um arquivo que chegou por e-mail não precisa de esquema nem de script de carga.

uos — Eu desenvolvo o Smart CSV Viewer, que coloca um motor SQL no celular, então a pergunta sobre o que merece um banco de dados aparece com frequência.