DuckDB: Up and Running, глава 8: удалённые данные
Главная мысль: DuckDB читает файлы по ссылке, без ручной загрузки. Для CSV это скачивает файл целиком. Для Parquet DuckDB берёт только нужные колонки. Поэтому для удалённых данных выбирайте Parquet.
Расширение httpfs
Заголовок раздела «Расширение httpfs»httpfs реализует файловую систему поверх HTTP и HTTPS. Его устанавливают один раз, потом оно подгружается автоматически.
INSTALL httpfs;LOAD httpfs;- Работает с CSV, Parquet и другими форматами, которые знает DuckDB.
- Поддерживает объектное хранилище через S3 API и glob-маски.
- Подходит, когда данные больше диска, часто обновляются или лежат у нескольких источников.
Блоки
textв этой главе ходят в сеть или требуют аккаунта. Мы их не запускаем. Блокиsqlв практике делают то же самое на локальных файлах.
CSV по ссылке
Заголовок раздела «CSV по ссылке»- Для файла на GitHub нужен адрес «raw». На странице файла нажмите кнопку Raw и скопируйте URL.
- Ссылку вставляют вместо имени файла:
SELECT * FROM 'https://.../file.csv'. - Фильтр работает как обычно:
WHERE year(Date) = 2018. - Несколько файлов:
read_csv([url1, url2]). Схемы файлов должны совпадать. - CSV хранится построчно. Обычно DuckDB скачивает его целиком. Для больших файлов это медленно.
Parquet по ссылке
Заголовок раздела «Parquet по ссылке»Parquet хранит данные по колонкам и держит метаданные. DuckDB использует их и HTTP range requests. Это запросы части файла.
| Запрос | Что скачивается |
|---|---|
SELECT * | Весь файл |
DESCRIBE | Только схема |
SELECT col_a, col_b | Только эти две колонки |
SELECT avg(age) | Только колонка age |
SELECT count(*) | Почти ничего. Число строк лежит в метаданных |
Правило: сначала DESCRIBE, потом выбирайте колонки. Не пишите SELECT * по удалённому файлу.
Hugging Face Datasets
Заголовок раздела «Hugging Face Datasets»Hugging Face — площадка с моделями и датасетами для машинного обучения. DuckDB читает её файлы по пути hf://.
hf://datasets/<пользователь>/<датасет>/<файл>hf://datasets/<пользователь>/<датасет>/data/<файл>.parquet- Имя пользователя и датасета видно на странице датасета. Имена файлов — на вкладке Files.
- Если файлы лежат в папке, добавьте её в путь (например
data/). - Сохраните данные в таблицу:
CREATE TABLE tips AS SELECT * FROM 'hf://...'. Тогда следующие запросы не идут в сеть.
Glob: много файлов одним запросом
Заголовок раздела «Glob: много файлов одним запросом»| Маска | Значение |
|---|---|
* | Любое число любых символов |
** | Любое число подпапок |
? | Один любой символ |
[abc] | Один символ из списка |
[a-z] | Один символ из диапазона |
- Пример:
hf://datasets/<user>/<dataset>/*.csvчитает все CSV датасета. - Схема у всех файлов должна быть одной. Иначе будет ошибка. Параметр
union_by_name = trueобъединяет колонки по имени. - Лучше всего маски работают с Parquet. Можно взять одну колонку из нескольких файлов и скачать только её.
Приватные датасеты
Заголовок раздела «Приватные датасеты»Для приватных данных нужен токен доступа. Его создают в настройках Hugging Face. Для чтения хватает типа Read. Токен показывают один раз.
| Способ | Как | Оценка |
|---|---|---|
CONFIG | CREATE SECRET hf_token (TYPE HUGGINGFACE, TOKEN '...') | Токен виден в коде |
CREDENTIAL_CHAIN | CREATE SECRET hf_token (TYPE HUGGINGFACE, PROVIDER CREDENTIAL_CHAIN). Токен берётся из файла, куда его сохранил huggingface-cli login | Безопаснее: токена нет в коде |
После создания секрета приватный файл читается так же, как публичный.
Ключевые термины
Заголовок раздела «Ключевые термины»| Термин | Значение |
|---|---|
httpfs | Расширение для чтения и записи файлов по HTTP(S) и S3 |
| Raw URL | Прямая ссылка на содержимое файла |
| Range request | HTTP-запрос части файла |
hf:// | Путь к файлам на Hugging Face |
| Glob | Маска имён файлов с * и ? |
CREATE SECRET | Команда для хранения доступа (токена, ключа) |
| Credential chain | Автопоиск доступа в известных местах |
Что это значит на практике
Заголовок раздела «Что это значит на практике»- Parquet по HTTP и S3 — основа «лёгкого» озера данных. DuckDB читает его без загрузки в базу.
- Смотрите схему до запроса. Затем выбирайте только нужные колонки.
- Для повторных запросов делайте локальную таблицу или копию Parquet.
- Не пишите токены в код и не коммитьте их. Используйте
CREDENTIAL_CHAIN, переменные окружения или секреты CI. - Тесты пайплайна могут читать эталонные файлы по ссылке.
Практика в DuckDB
Заголовок раздела «Практика в DuckDB»Запустите duckdb в терминале. Блоки идут по порядку. Файлы создаются в папке /tmp. Сеть не нужна: мы читаем локальные файлы теми же функциями, что работают с URL.
Шаг 1. Создаём два Parquet-файла
Заголовок раздела «Шаг 1. Создаём два Parquet-файла»COPY ( SELECT i AS id, 'A' AS agency, 20 + i % 40 AS age, round(10 + i * 1.5, 2) AS net_sales FROM range(1, 1001) t(i)) TO '/tmp/duck_ch8_sales_part1.parquet' (FORMAT PARQUET);
COPY ( SELECT i AS id, 'B' AS agency, 20 + i % 40 AS age, round(10 + i * 1.5, 2) AS net_sales FROM range(1001, 2001) t(i)) TO '/tmp/duck_ch8_sales_part2.parquet' (FORMAT PARQUET);Шаг 2. Схема и метаданные
Заголовок раздела «Шаг 2. Схема и метаданные»DESCRIBE SELECT * FROM '/tmp/duck_ch8_sales_part1.parquet';
-- число строк берётся из метаданных. Колонки не читаютсяSELECT count(*) AS n FROM '/tmp/duck_ch8_sales_part1.parquet';
-- метаданные файла: строки и колонкиSELECT file_name, num_rows, num_row_groupsFROM parquet_file_metadata('/tmp/duck_ch8_sales_part1.parquet');Шаг 3. Только нужные колонки и glob
Заголовок раздела «Шаг 3. Только нужные колонки и glob»-- две колонки из двух файлов по маскеSELECT agency, avg(age) AS avg_age, count(*) AS nFROM '/tmp/duck_ch8_sales_part*.parquet'GROUP BY agencyORDER BY agency;
-- какой файл дал какие строкиSELECT filename, count(*) AS nFROM read_parquet('/tmp/duck_ch8_sales_part*.parquet', filename = true)GROUP BY filenameORDER BY filename;Проверка: во втором запросе должно быть две строки по 1000.
Задания
Заголовок раздела «Задания»1. Покажите, сколько строк всего в обоих файлах, не читая колонки с данными.
Решение
SELECT sum(num_rows) AS total_rowsFROM parquet_file_metadata('/tmp/duck_ch8_sales_part*.parquet');2000 строк. Число берётся из метаданных, данные не читаются.
2. Посчитайте сумму net_sales по agency. Читайте только две колонки.
Решение
SELECT agency, sum(net_sales) AS total_salesFROM '/tmp/duck_ch8_sales_part*.parquet'GROUP BY agencyORDER BY agency;A — 760 750, B — 2 260 750. Запрос читает только agency и net_sales.
3. Прочитайте два файла списком через read_parquet([...]) и найдите максимальный id.
Решение
SELECT max(id) AS max_idFROM read_parquet(['/tmp/duck_ch8_sales_part1.parquet', '/tmp/duck_ch8_sales_part2.parquet']);Максимальный id равен 2000. Он из второго файла.
4. Добавьте файл с другими колонками. Прочитайте все файлы одной маской.
Решение
COPY (SELECT 1 AS id, 'x' AS other_col) TO '/tmp/duck_ch8_extra.parquet' (FORMAT PARQUET);
SELECT count(*) AS n, count(other_col) AS with_other_colFROM read_parquet('/tmp/duck_ch8_*.parquet', union_by_name = true);2001 строка, other_col заполнен в одной. Без union_by_name DuckDB вернёт ошибку schema mismatch. С union_by_name = true колонки объединяются по имени, а пропуски равны NULL.
Удалить созданные файлы: rm /tmp/duck_ch8_*.parquet.
Те же запросы по сети выглядят так. Без доступа к сети их не запускайте:
INSTALL httpfs;LOAD httpfs;SELECT agency, avg(age) FROM 'https://example.com/data/sales.parquet' GROUP BY agency;SELECT * FROM 'hf://datasets/<user>/<dataset>/data/<file>.parquet' LIMIT 10;Вопросы для самопроверки
Заголовок раздела «Вопросы для самопроверки»- Почему Parquet удобнее CSV для чтения по сети?
- Какие запросы к удалённому Parquet почти ничего не скачивают?
- Как собрать путь
hf://к файлу датасета? - Чем
CREDENTIAL_CHAINлучше, чемCONFIGдля токена Hugging Face?
Предыдущая: Глава 7. JupySQL. Следующая: Глава 9. MotherDuck.