Consultas sobre data lakes
DuckDB puede consultar Parquet/CSV/JSON en object storage (S3, GCS, Azure, HTTP) con la extension httpfs. El patron tipico: el lake permanece en el bucket; DuckDB lee solo las columnas y particiones que la consulta necesita.
Extension httpfs
INSTALL httpfs;
LOAD httpfs;En Python suele bastar:
import duckdb
con = duckdb.connect()
con.execute("INSTALL httpfs; LOAD httpfs;")En versiones recientes httpfs a menudo se carga bajo demanda al usar s3:// o https://, pero instalarla explicitamente evita sorpresas en CI.
Lectura por HTTP/HTTPS
SELECT *
FROM read_parquet('https://ejemplo.com/datos/pedidos.parquet')
LIMIT 10;Util para datasets publicos o artefactos versionados en URL. Para volumen serio, prefiere S3/GCS con credenciales y particionado.
Configurar S3
Variables tipicas (AWS):
LOAD httpfs;
SET s3_region = 'eu-west-1';
SET s3_access_key_id = 'AKIA...';
SET s3_secret_access_key = 'secreto...';
-- Si usas endpoint compatible (MinIO, LocalStack):
-- SET s3_endpoint = 'localhost:9000';
-- SET s3_url_style = 'path';
-- SET s3_use_ssl = false;Mejor con secretos (DuckDB secrets):
CREATE SECRET s3_secret (
TYPE S3,
KEY_ID 'AKIA...',
SECRET 'secreto...',
REGION 'eu-west-1'
);En Python, lee de entorno; no hardcodees claves:
import os
import duckdb
con = duckdb.connect()
con.execute("LOAD httpfs")
con.execute(f"SET s3_region = '{os.environ['AWS_REGION']}'")
con.execute(f"SET s3_access_key_id = '{os.environ['AWS_ACCESS_KEY_ID']}'")
con.execute(f"SET s3_secret_access_key = '{os.environ['AWS_SECRET_ACCESS_KEY']}'")O deja que el SDK/entorno estandar resuelva credenciales cuando aplique a tu version/setup (instance role, AWS_PROFILE, etc.).
Consultar un lake particionado
Layout ejemplo:
s3://mi-lake/pedidos/
fecha=2026-01-01/pais=ES/part-000.parquet
fecha=2026-01-01/pais=PT/part-000.parquet
fecha=2026-01-02/pais=ES/part-000.parquetSELECT pais, SUM(importe) AS total
FROM read_parquet(
's3://mi-lake/pedidos/**/*.parquet',
hive_partitioning = true
)
WHERE fecha BETWEEN DATE '2026-01-01' AND DATE '2026-01-07'
AND pais IN ('ES', 'PT')
GROUP BY pais
ORDER BY total DESC;El filtro sobre particiones reduce listados GET y bytes transferidos. Sin hive_partitioning, pierdes ese recorte automatico por path.
Glob y multiples prefijos
SELECT * FROM read_parquet('s3://mi-lake/pedidos/fecha=2026-01-0*/*.parquet');
SELECT * FROM read_parquet([
's3://mi-lake/pedidos/fecha=2026-01-01/**/*.parquet',
's3://mi-lake/pedidos/fecha=2026-01-02/**/*.parquet'
]);Escribir de vuelta al lake
COPY (
SELECT pais, fecha, SUM(importe) AS total
FROM read_parquet(
's3://mi-lake/pedidos/**/*.parquet',
hive_partitioning = true
)
WHERE fecha = DATE '2026-01-02'
GROUP BY 1, 2
) TO 's3://mi-lake/marts/ventas_diarias'
(
FORMAT PARQUET,
PARTITION_BY (fecha),
OVERWRITE_OR_IGNORE
);Permisos IAM: ademas de s3:GetObject / ListBucket, la escritura necesita PutObject (y a veces DeleteObject si hay overwrite).
GCS y Azure (idea general)
Patrones equivalentes con prefijos y secretos propios:
-- GCS (ejemplo conceptual; revisa docs de tu version)
-- SET s3_endpoint / credenciales interoperables, o extension/secret tipo GCS
-- Azure: tipicamente azure:// con secret TYPE AZURELa idea es la misma: autenticar, listar por glob/particion, leer Parquet columnar, escribir marts.
Vista estable sobre el lake
CREATE OR REPLACE VIEW v_pedidos_lake AS
SELECT *
FROM read_parquet(
's3://mi-lake/pedidos/**/*.parquet',
hive_partitioning = true,
union_by_name = true
);
SELECT fecha, COUNT(*)
FROM v_pedidos_lake
WHERE fecha = DATE '2026-01-02'
GROUP BY fecha;union_by_name = true ayuda cuando el esquema del lake evoluciona anadiendo columnas.
Pushdown: por que importa
DuckDB + Parquet en S3:
- Lista objetos (mejor si el prefijo ya esta acotado).
- Lee metadata/footer de Parquet.
- Descarga solo row groups / columnas relevantes cuando el predicado lo permite.
Por eso SELECT pais, SUM(importe) es mucho mas barato que SELECT * seguido de agregar en Pandas.
Comprueba el plan:
EXPLAIN
SELECT pais, SUM(importe)
FROM read_parquet('s3://mi-lake/pedidos/**/*.parquet', hive_partitioning = true)
WHERE fecha = DATE '2026-01-02'
GROUP BY pais;Cache local (opcional)
Para iterar en desarrollo sin pagar listados/GETs repetidos, descarga un subconjunto o materializa:
CREATE OR REPLACE TABLE cache_pedidos_semana AS
SELECT *
FROM read_parquet(
's3://mi-lake/pedidos/**/*.parquet',
hive_partitioning = true
)
WHERE fecha >= CURRENT_DATE - INTERVAL 7 DAY;O COPY a disco local y trabaja contra ./cache/*.parquet.
Errores habituales
- Poner access keys en notebooks versionados o capturas de pantalla.
- Hacer
SELECT *sobre un prefijo enorme en S3 "para ver". - Filtrar por una columna que no es de particion esperando que no se listen ficheros (solo ayuda de verdad con particiones/paths y estadisticas).
- Glob
**desde la raiz del bucket: listados caros y lentos. - Escribir miles de archivos diminutos en el mart (small files).
- Mezclar regiones endpoint/bucket y recibir errores opacos de firma o timeout.
- Olvidar
LOAD httpfsen entornos limpios (CI, contenedor nuevo).
Buenas practicas
- Credenciales por entorno, rol de instancia o secrets; nunca en el repo.
- Particiona el lake por columnas de filtro real (
fechaprimero). - Acota globs al prefijo minimo necesario.
- Proyecta solo columnas usadas.
- Materializa marts agregados; no reconsultes raw para cada dashboard ad-hoc pesado.
- Usa
EXPLAIN/ conteos de archivos cuando una query remota sea lenta. - Separa zonas:
raw/,staging/,marts/con politicas IAM distintas.
Ejercicios
INSTALL/LOADhttpfs y lee un Parquet publico porhttps://(busca un dataset de ejemplo pequeno).- Configura credenciales por variables de entorno y lista con un
SELECT COUNT(*)sobre un prefijo de prueba. - Crea (localmente o en MinIO) un layout Hive
fecha=.../pais=...y consulta conhive_partitioning = true. - Compara tiempo/bytes percibidos entre
SELECT *ySELECT pais, importecon filtro de una solafecha. - Escribe un mart agregado a otro prefijo con
COPY ... PARTITION_BY.
Siguiente paso
Continua con Rendimiento: threads, memoria, planes, tipos y diseno de ficheros para que estas consultas sigan siendo rapidas.
