Lectura de CSV, JSON y Parquet
DuckDB brilla leyendo archivos directamente en SQL, sin etapa previa de "cargar a base". Este capitulo cubre read_csv, read_json, read_parquet, opciones de tipado, globbing y escritura.
Principio: consulta el archivo
SELECT * FROM read_csv_auto('datos/pedidos.csv') LIMIT 5;
SELECT * FROM read_parquet('datos/pedidos.parquet') LIMIT 5;
SELECT * FROM read_json_auto('datos/eventos.json') LIMIT 5;Tambien puedes usar la sintaxis de reemplazo:
SELECT * FROM 'datos/pedidos.parquet' LIMIT 5;
SELECT * FROM 'datos/*.parquet' LIMIT 5;CSV
Auto vs explicito
read_csv_auto infiere delimitador, header y tipos. Util en exploracion.
SELECT * FROM read_csv_auto('pedidos.csv', sample_size = 20000);En pipelines, fija opciones:
SELECT *
FROM read_csv(
'pedidos.csv',
header = true,
delim = ',',
quote = '"',
escape = '"',
dateformat = '%Y-%m-%d',
timestampformat = '%Y-%m-%d %H:%M:%S',
columns = {
'pedido_id': 'INTEGER',
'pais': 'VARCHAR',
'importe': 'DECIMAL(18, 2)',
'fecha': 'DATE'
}
);Problemas tipicos de CSV
| Sintoma | Causa frecuente | Mitigacion |
|---|---|---|
Numeros como VARCHAR | Coma decimal, miles, espacios | decimal_separator, limpieza, cast |
| Fechas mal parseadas | Formato local dd/mm/yyyy | dateformat explicito |
| Filas rotas | Delimitadores dentro de campos sin quote | Revisar quote/escape |
| Encoding raro | Latin-1 / Windows-1252 | encoding = 'latin-1' u otro |
| Columnas de mas/menos | CSV inconsistente | ignore_errors, null_padding, validar |
Ejemplo con separador europeo y encoding:
SELECT *
FROM read_csv(
'ventas_eu.csv',
header = true,
delim = ';',
decimal_separator = ',',
encoding = 'utf-8',
columns = {
'importe': 'DECIMAL(18, 2)',
'fecha': 'DATE'
},
dateformat = '%d/%m/%Y'
);Materializar CSV limpio
CREATE OR REPLACE TABLE pedidos AS
SELECT
pedido_id,
upper(trim(pais)) AS pais,
CAST(importe AS DECIMAL(18, 2)) AS importe,
CAST(fecha AS DATE) AS fecha
FROM read_csv_auto('pedidos.csv');JSON
JSON Lines vs array
- JSONL / NDJSON: un objeto por linea. Ideal para logs y eventos.
- Array JSON: un unico
[ {...}, {...} ].
-- Auto detecta estructura
SELECT * FROM read_json_auto('eventos.jsonl');
-- Array en un archivo
SELECT * FROM read_json('eventos_array.json', format = 'array');Extraer campos
SELECT
json_extract_string(payload, '$.user.id') AS user_id,
json_extract(payload, '$.items') AS items,
CAST(json_extract_string(payload, '$.amount') AS DECIMAL(18, 2)) AS amount
FROM read_json_auto('raw_events.jsonl');Si read_json_auto ya aplana columnas:
SELECT
event_id,
user_id,
event_type,
CAST(ts AS TIMESTAMP) AS ts
FROM read_json_auto('eventos.jsonl');Listas anidadas con UNNEST
SELECT
o.order_id,
i.item_id,
i.qty
FROM read_json_auto('orders.json') AS o,
UNNEST(o.items) AS u(i);Parquet
Parquet es el formato por defecto recomendado: columnar, tipado, compresion, predicados y proyecciones eficientes.
SELECT pais, SUM(importe) AS total
FROM read_parquet('lake/pedidos/**/*.parquet')
WHERE fecha >= DATE '2026-01-01'
GROUP BY pais;DuckDB puede empujar filtros y columnas al lector Parquet (no lee columnas innecesarias).
Hive partitioning
Si el layout es .../fecha=2026-01-02/pais=ES/part.parquet:
SELECT *
FROM read_parquet(
'lake/pedidos/**/*.parquet',
hive_partitioning = true
)
WHERE fecha = DATE '2026-01-02' AND pais = 'ES';Las columnas de particion aparecen en el resultado y se usan para no abrir ficheros irrelevantes.
Schema y evolucion
-- Union de archivos con esquemas parecidos
SELECT * FROM read_parquet('lake/pedidos/*.parquet', union_by_name = true);union_by_name = true alinea por nombre de columna (no por posicion). Util cuando anadiste columnas nuevas en ficheros recientes.
Describir esquema:
DESCRIBE SELECT * FROM read_parquet('lake/pedidos/part-0.parquet');Globbing y union de fuentes
SELECT * FROM read_csv_auto('landing/2026-01-*.csv');
SELECT * FROM read_parquet(['a.parquet', 'b.parquet']);
SELECT *, 'csv' AS origen FROM read_csv_auto('a.csv')
UNION ALL BY NAME
SELECT *, 'parquet' AS origen FROM read_parquet('b.parquet');Escritura
COPY (
SELECT pais, SUM(importe) AS total
FROM pedidos
GROUP BY pais
) TO 'salida/resumen_pais.parquet' (FORMAT PARQUET);
COPY pedidos TO 'salida/pedidos.csv' (HEADER, DELIMITER ',');
-- Particionado
COPY pedidos TO 'lake/pedidos' (
FORMAT PARQUET,
PARTITION_BY (fecha, pais),
OVERWRITE_OR_IGNORE
);Desde SQL tambien:
CREATE OR REPLACE TABLE tmp AS SELECT * FROM read_csv_auto('pedidos.csv');
COPY tmp TO 'pedidos.parquet' (FORMAT PARQUET);Perfil rapido de un archivo
SUMMARIZE SELECT * FROM read_parquet('pedidos.parquet');
SELECT
COUNT(*) AS filas,
COUNT(DISTINCT pais) AS paises,
SUM(CASE WHEN importe IS NULL THEN 1 ELSE 0 END) AS nulos_importe
FROM read_csv_auto('pedidos.csv');Errores habituales
- Dejar
read_csv_autoen produccion sincolumnsni formatos de fecha. - Mezclar CSV con distinta cantidad de columnas sin validar.
- Leer JSON como texto y parsear a mano cuando
read_jsonbasta. - Escribir miles de Parquet minusculos (un archivo por fila o por micro-lote).
- Ignorar
hive_partitioningy filtrar solo en SQL tras abrir todo. - Asumir que el orden de columnas en Parquet viejo y nuevo coincide (
union_by_name).
Buenas practicas
- Exploracion:
*_auto. Produccion: opciones y tipos explicitos. - Normaliza a Parquet en cuanto el CSV/JSON este limpio.
- Particiona por columnas de filtro frecuente (
fecha,pais), no por alta cardinalidad (pedido_id). - Valida filas, nulos y rangos justo despues de leer.
- Usa
SUMMARIZE/DESCRIBEantes de disenar el modelo. - Preferir JSONL frente a un unico array gigante.
Ejercicios
- Lee un CSV con
read_csv_autoy vuelve a leerlo concolumnsexplicitas; comparaDESCRIBE. - Convierte ese CSV a Parquet con
COPYy mide (mentalmente o con timer) unGROUP BYsobre ambos. - Crea un JSONL de eventos con un campo anidado y extrae
user_ideamount. - Escribe Parquet particionado por
fechay consulta solo un dia conhive_partitioning = true. - Fuerza un CSV con una fila mal formada y prueba
ignore_errors = true; luego arregla el archivo en vez de silenciar el error.
Siguiente paso
Continua con Integracion con Python: API de conexiones, DataFrames, registro de tablas y patrones notebook/pipeline.
