Импорт данных, анализ логов, обработка файлов - задачи из жизненного цикла продукта. Автоматизация таких процессов не оправдана: они разового характера - сделал и забыл.
Входные данные могут преподнести неожиданные сюрпризы: несоответствие формата даты, разделитель дробной части из другой локали. Текстовые процессоры помогают справляться с ними.
AWK - интерпретируемый скриптовый язык. Имеет C-подобный синтаксис. Построчно обрабатывает входной поток данных. Каждая строка разбивается на поля, после чего к ним применяется алгоритм обработки.
Сценарий выполнения:
шаблон {действие}
шаблон {действие}
. . .
Каждая строка поочередно сравнивается с шаблонами. При совпадении выполняется действие. Допускается указание пустого шаблона. Тогда действие выполняется над всеми строками. В языке предопределены шаблоны BEGIN и END. BEGIN выполняется до начала обработки, END - в конце.
Действие - набор операторов. Каждый оператор разделяется точкой с запятой, переводом строки или закрывающей скобкой.
Hello world на языке awk:
{
print "Hello world!";
exit;
}
Условные операторы, циклы, математические операции и ряд встроенных функций помогают решать различные задачи: сформировать отчет по "сырым" данным, преобразовать формат и даже распарсить JSON.
AWK съэкономит время на импорте данных в СУБД. Идея заключается в преобразовании исходных данных в команды SQL. Сценарий трансформации можно повторно использовать. Скорость загрузки не зависит от прикладных транзакций, кэша ORM и других накладных ресурсов.
Рассмотрим сценарий импорта на примере конкретной задачи.
Загрузить номера телефонов пользователей в систему.
Таблица contact:
TABLE contact (
id bigint,
type varchar(1024),
contact varchar(1024),
person_fk bigint
)
Файл импорта:
Иванов;Иван;Иванович;10.05.65;+79272101010
Петров;Генадий;Николаевич;18.01.80;+79272101894
Николаева;Жанна;Александровна;24.12.1991;+79273164132
SQL запрос добавления номера телефона Иванова Ивана Ивановича:
INSERT INTO contact(id,type,contract,person_fk)
SELECT nextval('id_seq'), 'PHONE', '+79272101010', person.id FROM person
WHERE
last_name = 'Иванов'
and first_name = 'Иван'
and middle_name = 'Иванович'
and birth_date = '1965-05-10'
and NOT EXISTS(SELECT 1 FROM contact WHERE
person_fk = person.id
and contact.type = 'PHONE'
and contact.contact = '+79272101010'
);
NOT EXISTS исключает уже зарегистрированные номера
Формат даты рождения может принимать вид ДД.ММ.ГГ ДД.ММ.ГГГГ. Для преобразование в ISO формат потребуются функции strftime, mktime.
AWK скрипт обработки:
{
current_year_part = strftime("%y", systime());
split($4,birth_date_parts,".");
if (length(birth_date_parts[3]) < 4)
birth_date_parts[3] = birth_date_parts[3] > current_year_part ? birth_date_parts[3] + 1900: birth_date_parts[3] + 2000
}
{
last_name = $1
first_name = $2
middle_name = $3
birth_date = strftime("%Y-%m-%d",mktime(birth_date_parts[3] " " birth_date_parts[2] " " birth_date_parts[1] " " 0 " " 0 " " 0))
phone_number = $5
}
{
printf "INSERT INTO contact(id,type,contract,person_fk) \n \
SELECT nextval('id_seq'), 'PHONE', '%s', person.id FROM person \n \
WHERE \n \
last_name = '%s' \n \
and first_name = '%s' \n \
and middle_name = '%s' \n \
and birth_date = '%s' \n \
and NOT EXISTS(SELECT 1 FROM contact WHERE \n \
person_fk = person.id \n \
and contact.type = 'PHONE' \n \
and contact.contact = '%s' \n \
); \n \
",phone_number, last_name, first_name, middle_name, birth_date, phone_number
}
AWK - быстрый инструмент способный приготовить "сырые" данные. Adam Drake сравнил Hadoop с AWK. Файл размером 1.75 Гб был обработан за 12 секунд. Производительность составила 270 MB/сек против 1.14 MB/сек (26 минут) со стороны Hadoop.