Загрузка вашего первого CSV в базу данных без повреждения данных
Импорт запускается, сообщается, что все прошло успешно, и данные теперь немного отличаются от файла. Почтовые индексы потеряли ведущие нули, даты стали текстовыми, столбец с одним случайным словом стал текстовым для каждой строки, а общая строка в нижней части экспорта теперь является записью.
Что вы делаете: создаете таблицу сами, загружаете все как текст, проверяете, затем приводите в чистую таблицу. Это один дополнительный шаг, и он делает всю нагрузку обратимой.
Краткая версия. Позвольте импортеру перенести данные. Не позволяйте ему решать типы.
Пять решений перед загрузкой
| Решение | Неправильный ответ будет стоить вам |
|---|---|
| Какие столбцы являются текстовыми, а не цифровыми | Ведущие нули и идентификаторы длиной более 15 цифр. |
| Какой столбец является ключевым | Позже вы узнаете, что это не уникально. |
| Какой формат даты использует файл | Треть строк молча ошибается |
| В какой кодировке он находится | Имена, поступающие как é и £ |
| Есть ли в файле ненужные строки | Общая строка становится записью |
Ответьте на все пять, просмотрев файл, прежде чем прикасаться к базе данных. Откройте первую тысячу строк в текстовом редакторе, а не в Excel, потому что к моменту просмотра Excel уже изменит некоторые из них. Эта команда просмотра находится в открытие большого CSV.
Постановочный шаблон
Два стола. Один хранит все в виде текста в том виде, в каком оно поступило; другой содержит напечатанную, очищенную версию. Это звучит как дополнительная работа и занимает около четырех минут.
-- 1. staging: every column TEXT, nothing can be lost
CREATE TABLE orders_raw (
order_id TEXT,
customer TEXT,
order_date TEXT,
amount TEXT,
status TEXT
);
-- 2. import into it (sqlite3 shell)
.mode csv
.import --skip 1 orders.csv orders_raw
-- 3. look before casting
SELECT COUNT(*) FROM orders_raw;
SELECT * FROM orders_raw LIMIT 10;
SELECT order_date FROM orders_raw
WHERE order_date NOT LIKE '____-__-__' LIMIT 20;
Именно этот третий запрос и определяет шаблон. В нем перечислены значения, которые не имеют ожидаемой формы, прежде чем какое-либо преобразование их выбросит.
-- 4. cast into the real table, deliberately
CREATE TABLE orders (
order_id TEXT PRIMARY KEY,
customer TEXT,
order_date DATE,
amount REAL,
status TEXT
);
INSERT INTO orders
SELECT TRIM(order_id),
TRIM(customer),
DATE(order_date),
CAST(REPLACE(REPLACE(amount, ',', ''), '£', '') AS REAL),
LOWER(TRIM(status))
FROM orders_raw
WHERE order_id IS NOT NULL AND order_id <> '';
Каждая трансформация видна, и каждая из них — это решение, которое вы приняли. Если приведение завершается неудачно или выдает нули, необработанная таблица все еще существует, и ничего не нужно повторно загружать.
Три проверки после загрузки
-- 1. row count against the file
SELECT COUNT(*) FROM orders; -- compare with wc -l, minus the header
-- 2. no column silently empty
SELECT SUM(CASE WHEN order_date IS NULL THEN 1 ELSE 0 END) AS null_dates,
SUM(CASE WHEN amount IS NULL THEN 1 ELSE 0 END) AS null_amounts
FROM orders;
-- 3. a value you can verify by eye
SELECT * FROM orders WHERE order_id = '00042';
Второй перехватывает неудачное приведение, которое является наиболее распространенной причиной сбоя при загрузке при сообщении об успехе. Столбец, который после приведения становится полностью нулевым, означает, что исходный формат каждый раз не соответствует тому, что вы предполагали.
Другие инструменты
| инструмент | Команда |
|---|---|
| SQLite-оболочка | .mode csv затем .import --skip 1 file.csv table |
| Браузер БД для SQLite | Файл, Импорт, Таблица из файла CSV |
| DuckDB | CREATE TABLE t AS SELECT * FROM read_csv_auto('file.csv') |
| PostgreSQL | \copy orders FROM 'file.csv' CSV HEADER |
| pandas | df.to_sql('orders', con, if_exists='append', index=False) |
DuckDB's read_csv_auto необычайно хорош в догадках, и вы можете переопределить любой столбец с помощью types аргумент, когда догадка неверна. Постгрес \copy запускает клиентскую сторону, что важно, потому что простой COPY команде необходимо, чтобы файл находился на сервере.
Когда файл будет приходить каждый месяц
- Пишите нагрузку как скрипт, а не как последовательность кликов.
- Поместите имя исходного файла и дату загрузки в столбец, чтобы можно было отследить любую строку.
- Загрузите в промежуточную версию, запустите проверки и только потом замените или добавьте в чистую таблицу.
- Сохраните необработанные файлы. Хранилище стоит дешево, и единственным реальным восстановлением является перезагрузка.
- Запишите количество строк каждой загрузки в небольшую таблицу. Дрифт проявляется как число, а не как ощущение.
Как применить это в своей работе
- Создайте таблицу самостоятельно для следующего загружаемого файла, а не позволяйте этому инструменту сделать это.
- Добавьте промежуточный шаг один раз и посмотрите, сколько он зафиксирует в файле, который, по вашему мнению, был чистым.
- Запустите три проверки и запишите цифры в примечаниях к проекту.
- Создайте текст каждого столбца идентификатора и затем проверьте один известный идентификатор.
- Превратите повторный импорт в сценарий, если вы делаете это впервые дважды.
Единственная привычка, которую нужно сохранить
Подсчитайте строки в файле до импорта и после. Это занимает десять секунд, это единственный способ заметить усеченную или частичную загрузку, и именно это число вам понадобится, когда кто-нибудь спросит, полны ли данные.
Знаете ли вы количество строк в последнем файле, который вы во что-нибудь загрузили?
SQL для аналитиков — это 458 страниц, которые считывают запросы построчно в повседневных словах, поэтому ошибочный результат нужно где-то отслеживать, а не перепечатывать, пока он не станет выглядеть лучше.
SQL для аналитиков, 19 $ →Настройка базы данных SQL покрывает инструмент, проверка качества данных о том, что нужно запустить потом, и Настройка базы данных крышки, какой из них установить.
Прочтите проверки качества данных →