[Программирование, Java] Применение JDBC в процессе ETL
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
В предыдущей статье мы упоминали о Java + JDBC в качестве альтернативы Python + ODBC. В этой статье приведём условную ситуацию, приближенную к той, которая побудила нас сменить язык программирования.Была поставлена задача по обработке более 25 млн. строк посредством процесса ETL: на основе имеющихся данных расcчитать два дополнительных столбца и вернуть их в базу. Python прекрасно справлялся с подобными задачами, но в данной ситуации скорость получения результата была не самой оптимальной: обработка длилась более суток.Так как процедуру предполагалось выполнять многократно, то было решено найти более быстрый инструмент. Поиск альтернативных библиотек для Python ни к чему не привёл. Поэтому было решено применить тяжёлую артиллерию — Java, так как Java работает с БД быстрее, чем Python чисто технически. И чем больше объём данных, тем заметнее реализуется это преимущество.На первом этапе разовьём класс DBC из предыдущей статьи, добавив:
- метод execQuerySelect для выполнения select-запросов в разных вариантах (с TOP, where и т.д.)
- метод execQueryInsert для вставки результата в БД
- вспомогательные метода buildInsertColumns и buildInsertValues для формирования колонок и строк соответственно.
В итоге класс DBC пополнится следующим кодом:
/**
* Выполнение select запроса к БД
*
* @param select Названия необходимых колонок (* - получить все)
* @param from Таблица из которой небходимо получить данные
* @return ResultSet с результатами работы запроса
*/
public ResultSet execQuerySelect(String select, String from) {
if (!readyToWork) {
System.err.println("DBC not ready to work! Abort:execQuerySelected");
return null;
}
String query = "SELECT " + select + " FROM " + from;
return execQuery(query);
}
/**
* Выполнение select запроса к БД
*
* @param select Названия необходимых колонок (* - получить все)
* @param from Таблица из которой небходимо получить данные
* @param top Количество "верхних" строк получаемых из базы
* @return ResultSet с результатами работы запроса
*/
public ResultSet execQuerySelect(String select, String from, Integer top) {
if (!readyToWork) {
System.err.println("DBC not ready to work! Abort:execQuerySelected");
return null;
}
String query = "SELECT TOP(" + top + ") " + select + " FROM " + from;
return execQuery(query);
}
/**
* Выполнение select запроса к БД
*
* @param select Названия необходимых колонок (* - получить все)
* @param from Таблица из которой небходимо получить данные
* @param where Условие выборки значений из таблицы
* @return ResultSet с результатами работы запроса
*/
public ResultSet execQuerySelect(String select, String from, String where) {
if (!readyToWork) {
System.err.println("DBC not ready to work! Abort:execQuerySelected");
return null;
}
String query = "SELECT " + select + " FROM " + from + " WHERE " + where;
return execQuery(query);
}
/**
* Выполнение select запроса к БД
*
* @param select Названия необходимых колонок (* - получить все)
* @param from Таблица из которой небходимо получить данные
* @param where Условие выборки значений из таблицы
* @param top Количество "верхних" строк получаемых из базы
* @return ResultSet с результатами работы запроса
*/
public ResultSet execQuerySelect(String select, String from, String where, Integer top) {
if (!readyToWork) {
System.err.println("DBC not ready to work! Abort:execQuerySelected");
return null;
}
String query = "SELECT TOP(" + top + ") " + select + " FROM " + from + " WHERE " + where;
return execQuery(query);
}
/**
* Выполнение insert запроса к БД
*
* @param table Имя таблицы для вставки
* @param columns Массив колонок для вставки
* @param data Вектор словарей со значениями для вставки
* @return Флаг успешности выполнения запроса
*/
public boolean execQueryInsert(String table, String[] columns, Vector<HashMap<String, String>> data) {
if (!readyToWork) {
System.err.println("DBC not ready to work! Abort:execQuerySelected");
return false;
}
String query = "INSERT INTO " + table + " " + buildInsertColumns(columns) + " VALUES ";
for (int i = 0; i < data.size() - 1; i++) {
query = query.concat(buildInsertValues(columns, data.get(i), false));
}
query = query.concat(buildInsertValues(columns, data.get(data.size() - 1), true));
execQuery(query);
return true;
}
/**
* Формирует строку с перечнем колонок для запроса insert
*
* @param columns Массив колонок
* @return Строку с перечнем колонок для запроса insert
*/
private String buildInsertColumns (String[] columns) {
String r = "(";
for (int i = 0; i < columns.length - 1; i++) {
r = r.concat("[" + columns[i] + "], ");
}
r = r.concat("[" + columns[columns.length-1] + "])");
return r;
}
/**
* Формирует строку с перечнем значений для запроса insert
*
* @param columns Массив колонок
* @param data Словарь значений для вставки
* @param isLast Флаг нахождения значений на последней позиции среди остальных
* @return Строку с перечнем значений для запроса insert
*/
private String buildInsertValues (String[] columns, HashMap<String, String> data, boolean isLast) {
String vals = "(";
for (int i = 0; i < columns.length - 1; i++) {
vals = vals.concat(data.get(columns[i]).concat(", "));
}
vals = vals.concat(data.get(columns[columns.length-1]).concat(")"));
if (isLast) {
vals = vals.concat(";");
}
else
{
vals = vals.concat(", ");
}
return vals;
}
Теперь, имея все необходимые инструменты, перейдём к решению нашей задачи. В рамках абстракции заменим сложные вычисления на вычисления суммы и разности двух чисел. Итоговый алгоритм заключается в получении двух чисел из одной таблицы, проведении расчетов, вставке результата в новую таблицу. С учётом алгоритма решения нашей задачи MainClass из предыдущей статьи будет выглядеть так:
package DataBaseTools;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Vector;
public class MainClass {
public static void main(String[] args) throws SQLException {
// create instance
DBC dbc = new DBC("localhost", "testDB");
// get query response
ResultSet response = dbc.execQuerySelect("x, y", "digits",);
Vector<HashMap<String, String>> insertBuffer = new Vector<HashMap<String, String>>();
// handle received data
if (response == null) {
System.out.println("NULL");
} else {
while (response.next()) {
HashMap<String, String> rowData = new HashMap<String, String>();
int x = response.getInt("x");
int y = response.getInt("y");
int a = x + y;
int s = x - y;
rowData.put("a", a.toString());
rowData.put("s", s.toString());
insertBuffer.add(rowData);
if (insertBuffer.size() == 1000) {
String[] cols = {"a", "s"};
System.out.println(dbc.execQueryInsert("[results]", cols, insertBuffer));
insertBuffer.clear();
}
}
if (insertBuffer.size() > 0) {
String[] cols = {"a", "s"};
System.out.println(dbc.execQueryInsert("[results]", cols, insertBuffer));
insertBuffer.clear();
}
}
}
}
Этот код создаёт подключение к базе, получает данные из исходной таблицы, производит расчет и запись результата в буфер, а при наполнении буфера происходит запись в базу. После выполнения цикла оставшиеся в буфере данные также записываются в базу. Обработка такого количества данных при изначальной сложности вычислений заняла чуть более 7 часов, что минимум в 4 раза быстрее аналогичного кода на Python. При увеличении скорости работы с БД такое решение позволит увеличить сложность вычислений, не увеличивая время выполнения.
===========
Источник:
habr.com
===========
Похожие новости:
- [Разработка веб-сайтов, Работа с видео, Программирование, Видеоконференцсвязь] How to make a multipoint WebRTC conference (MCU) with recording and screen-sharing in the browser
- [Разработка веб-сайтов, Работа с видео, Программирование, Видеоконференцсвязь] Как сделать многоточечную WebRTC-конференцию (MCU) с записью и демонстрацией экрана в браузере
- [Веб-дизайн, Разработка веб-сайтов, JavaScript, Программирование] Лёгкая, гибкая, производительная обёртка над Web Animations API — @okikio/animate (перевод)
- [JavaScript, Node.JS, DevOps] Непрерывная интеграция и развертывание (Deployment) с помощью Jenkins (перевод)
- [JavaScript, Программирование, VueJS] Как работают функции provide и inject во Vue 3?
- [Java] Создание архетипа Maven из существующего проекта (перевод)
- [Программирование, Java] Работа с БД с помощью JAVA и JDBC
- [Программирование, Венчурные инвестиции, Развитие стартапа, Образование за рубежом, Бизнес-модели] Перевод Курса по стартапам и бизнесу от Стэнфордского Университета. Лекция №4. Создание продукта, общение с клиентами… (перевод)
- [Java] Отладка Java-приложений из командной строки (перевод)
- [JavaScript, Разработка игр, Логические игры] Путеводитель разработчика по Garbo-боту
Теги для поиска: #_programmirovanie (Программирование), #_java, #_java, #_etl, #_programmirovanie (
Программирование
), #_java
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 12:47
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
В предыдущей статье мы упоминали о Java + JDBC в качестве альтернативы Python + ODBC. В этой статье приведём условную ситуацию, приближенную к той, которая побудила нас сменить язык программирования.Была поставлена задача по обработке более 25 млн. строк посредством процесса ETL: на основе имеющихся данных расcчитать два дополнительных столбца и вернуть их в базу. Python прекрасно справлялся с подобными задачами, но в данной ситуации скорость получения результата была не самой оптимальной: обработка длилась более суток.Так как процедуру предполагалось выполнять многократно, то было решено найти более быстрый инструмент. Поиск альтернативных библиотек для Python ни к чему не привёл. Поэтому было решено применить тяжёлую артиллерию — Java, так как Java работает с БД быстрее, чем Python чисто технически. И чем больше объём данных, тем заметнее реализуется это преимущество.На первом этапе разовьём класс DBC из предыдущей статьи, добавив:
/**
* Выполнение select запроса к БД * * @param select Названия необходимых колонок (* - получить все) * @param from Таблица из которой небходимо получить данные * @return ResultSet с результатами работы запроса */ public ResultSet execQuerySelect(String select, String from) { if (!readyToWork) { System.err.println("DBC not ready to work! Abort:execQuerySelected"); return null; } String query = "SELECT " + select + " FROM " + from; return execQuery(query); } /** * Выполнение select запроса к БД * * @param select Названия необходимых колонок (* - получить все) * @param from Таблица из которой небходимо получить данные * @param top Количество "верхних" строк получаемых из базы * @return ResultSet с результатами работы запроса */ public ResultSet execQuerySelect(String select, String from, Integer top) { if (!readyToWork) { System.err.println("DBC not ready to work! Abort:execQuerySelected"); return null; } String query = "SELECT TOP(" + top + ") " + select + " FROM " + from; return execQuery(query); } /** * Выполнение select запроса к БД * * @param select Названия необходимых колонок (* - получить все) * @param from Таблица из которой небходимо получить данные * @param where Условие выборки значений из таблицы * @return ResultSet с результатами работы запроса */ public ResultSet execQuerySelect(String select, String from, String where) { if (!readyToWork) { System.err.println("DBC not ready to work! Abort:execQuerySelected"); return null; } String query = "SELECT " + select + " FROM " + from + " WHERE " + where; return execQuery(query); } /** * Выполнение select запроса к БД * * @param select Названия необходимых колонок (* - получить все) * @param from Таблица из которой небходимо получить данные * @param where Условие выборки значений из таблицы * @param top Количество "верхних" строк получаемых из базы * @return ResultSet с результатами работы запроса */ public ResultSet execQuerySelect(String select, String from, String where, Integer top) { if (!readyToWork) { System.err.println("DBC not ready to work! Abort:execQuerySelected"); return null; } String query = "SELECT TOP(" + top + ") " + select + " FROM " + from + " WHERE " + where; return execQuery(query); } /** * Выполнение insert запроса к БД * * @param table Имя таблицы для вставки * @param columns Массив колонок для вставки * @param data Вектор словарей со значениями для вставки * @return Флаг успешности выполнения запроса */ public boolean execQueryInsert(String table, String[] columns, Vector<HashMap<String, String>> data) { if (!readyToWork) { System.err.println("DBC not ready to work! Abort:execQuerySelected"); return false; } String query = "INSERT INTO " + table + " " + buildInsertColumns(columns) + " VALUES "; for (int i = 0; i < data.size() - 1; i++) { query = query.concat(buildInsertValues(columns, data.get(i), false)); } query = query.concat(buildInsertValues(columns, data.get(data.size() - 1), true)); execQuery(query); return true; } /** * Формирует строку с перечнем колонок для запроса insert * * @param columns Массив колонок * @return Строку с перечнем колонок для запроса insert */ private String buildInsertColumns (String[] columns) { String r = "("; for (int i = 0; i < columns.length - 1; i++) { r = r.concat("[" + columns[i] + "], "); } r = r.concat("[" + columns[columns.length-1] + "])"); return r; } /** * Формирует строку с перечнем значений для запроса insert * * @param columns Массив колонок * @param data Словарь значений для вставки * @param isLast Флаг нахождения значений на последней позиции среди остальных * @return Строку с перечнем значений для запроса insert */ private String buildInsertValues (String[] columns, HashMap<String, String> data, boolean isLast) { String vals = "("; for (int i = 0; i < columns.length - 1; i++) { vals = vals.concat(data.get(columns[i]).concat(", ")); } vals = vals.concat(data.get(columns[columns.length-1]).concat(")")); if (isLast) { vals = vals.concat(";"); } else { vals = vals.concat(", "); } return vals; } package DataBaseTools;
import java.sql.ResultSet; import java.sql.SQLException; import java.util.HashMap; import java.util.Vector; public class MainClass { public static void main(String[] args) throws SQLException { // create instance DBC dbc = new DBC("localhost", "testDB"); // get query response ResultSet response = dbc.execQuerySelect("x, y", "digits",); Vector<HashMap<String, String>> insertBuffer = new Vector<HashMap<String, String>>(); // handle received data if (response == null) { System.out.println("NULL"); } else { while (response.next()) { HashMap<String, String> rowData = new HashMap<String, String>(); int x = response.getInt("x"); int y = response.getInt("y"); int a = x + y; int s = x - y; rowData.put("a", a.toString()); rowData.put("s", s.toString()); insertBuffer.add(rowData); if (insertBuffer.size() == 1000) { String[] cols = {"a", "s"}; System.out.println(dbc.execQueryInsert("[results]", cols, insertBuffer)); insertBuffer.clear(); } } if (insertBuffer.size() > 0) { String[] cols = {"a", "s"}; System.out.println(dbc.execQueryInsert("[results]", cols, insertBuffer)); insertBuffer.clear(); } } } } =========== Источник: habr.com =========== Похожие новости:
Программирование ), #_java |
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 12:47
Часовой пояс: UTC + 5