Русский
Русский
English
Статистика
Реклама

Sqlite3

Модуль для работы с sqlite3

07.05.2021 16:08:17 | Автор: admin

Сегодня Я хотел бы поделиться своей не большой разработкой, которая помогает мне уже более чем пол года: "Модуль для работы с sqlite3".

Концепция

Вместо написания SQL запросов мы будем передавать ключи, значения, названия таблиц, условия и callback'и, которые будут вызывать по завершению запросов(в каждый callback мы будем передавать ошибку и результат, если такой есть).

Представим модуль в виде класса.

Всего будет 4 метода:

  1. getData() - для получения данных из таблицы.

  2. insertData() - для добавления данных в таблицу.

  3. updateData() - для обновления данных в таблице.

  4. deleteData() - для удаления данных из таблицы.

Конечно же с помощью 4 методов приведенных выше мы не сможем исключить все виды запросов, но в моем случаи эти запросы самые частые.

Кодим

Для начало создадим сам класс, настроем экспорт и подключимся к БД и создадим метод, который будет добавлять кавычки у элемента, если тип элемента соответствует типу строки.

class DataBase {    /**     *      * @readonly     */    static sqlite3 = require('sqlite3').verbose();        /**    *     * @readonly    */   static database = new this.sqlite3.Database('./database/database.db');    static ToString(value) {        return typeof(value) === 'string' ? '\'' + value + '\'' : value;    }}module.exports = {    database: DataBase};

Для начало напишем метод, который будет возвращать данные из таблицы, не обязательно, но мы будем учитывать, кол-во строк, которые нам будут возвращаться(одна или несколько).

Начинаем собирать запрос с ключевого слова "SELECT", далее перебираем все столбцы, которые нам передали и добавляем их, но не забываем, что символ "*" не нужно "заковывать" в кавычки, т к этот символ говорит нам, что нужно получить все столбцы. Заканчиваем собор указанием таблицы и условия. Далее, исходя из кол-во строк, которые нам надо вернуть вызываем соответствующий метод.

class DataBase {    /**     *      * @readonly     */    static sqlite3 = require('sqlite3').verbose();        /**    *     * @readonly    */   static database = new this.sqlite3.Database('./database/database.db');        /**     *      * @param {String[]} keys      * @param {String} table      * @param {String} condition      * @param {Boolean} some      * @param {Function()} callback      */    static getData(keys, table, condition = '', some = true, callback = () => {}) {        let sql = 'SELECT ';        for (let i = 0; i < keys.length; i++) {            sql += keys[i] === '*' ? keys[i] : '`' + keys[i] + '`';            if (keys.length > i + 1)                sql += ', ';        }        sql += ' FROM `' + table + '` ' + condition;                if (some)            this.database.all(sql, (err, rows) => {                callback(err, rows);            });        else            this.database.get(sql, (err, row) => {                callback(err, row);            });    };    static ToString(value) {        return typeof(value) === 'string' ? '\'' + value + '\'' : value;    }}module.exports = {    database: DataBase};

Напишем метод отвечающий за обновление данных.

Начинаем с указания таблицы, установкой ключей и значений, а завершаем добавлением условия.

class DataBase {    /**     *      * @readonly     */    static sqlite3 = require('sqlite3').verbose();        /**    *     * @readonly    */   static database = new this.sqlite3.Database('./database/database.db');        /**     *      * @param {String[]} keys      * @param {String} table      * @param {String} condition      * @param {Boolean} some      * @param {Function()} callback      */    static getData(keys, table, condition = '', some = true, callback = () => {}) {        let sql = 'SELECT ';        for (let i = 0; i < keys.length; i++) {            sql += keys[i] === '*' ? keys[i] : '`' + keys[i] + '`';            if (keys.length > i + 1)                sql += ', ';        }        sql += ' FROM `' + table + '` ' + condition;                if (some)            this.database.all(sql, (err, rows) => {                callback(err, rows);            });        else            this.database.get(sql, (err, row) => {                callback(err, row);            });    };        /**     *      * @param {String[]} keys      * @param {Values[]} values      * @param {String} table      * @param {String} condition      * @param {Function()} callback      */    static updateData(keys, values, table, condition, callback = () => {}) {        let sql = 'UPDATE `' + table + '` SET ';        for (let i = 0; i < keys.length; i++) {            sql += '`' + keys[i] + '` = ' + this.ToString(values[i]);            if (keys.length > i + 1)                sql += ', ';        }        sql += ' ' + condition;                this.database.run(sql, (err) => {            callback(err);        });    }    static ToString(value) {        return typeof(value) === 'string' ? '\'' + value + '\'' : value;    }}module.exports = {    database: DataBase};

Остается совсем чуть-чуть, напишем метод для удаления данных(она максимально простой) и метод для добавления данных.

Добавления данных начнем с указания таблицы, потом передадим все ключи, которые мы хотим установить и заканчиваем установкой всех значений(значение должно иметь такой же индекс как и ключ, которому необходимо установить это значение).

class DataBase {    /**     *      * @readonly     */    static sqlite3 = require('sqlite3').verbose();        /**    *     * @readonly    */   static database = new this.sqlite3.Database('./database/database.db');        /**     *      * @param {String[]} keys      * @param {String} table      * @param {String} condition      * @param {Boolean} some      * @param {Function()} callback      */    static getData(keys, table, condition = '', some = true, callback = () => {}) {        let sql = 'SELECT ';        for (let i = 0; i < keys.length; i++) {            sql += keys[i] === '*' ? keys[i] : '`' + keys[i] + '`';            if (keys.length > i + 1)                sql += ', ';        }        sql += ' FROM `' + table + '` ' + condition;                if (some)            this.database.all(sql, (err, rows) => {                callback(err, rows);            });        else            this.database.get(sql, (err, row) => {                callback(err, row);            });    };        /**     *      * @param {String[]} keys      * @param {Values[]} values      * @param {String} table      * @param {String} condition      * @param {Function()} callback      */    static updateData(keys, values, table, condition, callback = () => {}) {        let sql = 'UPDATE `' + table + '` SET ';        for (let i = 0; i < keys.length; i++) {            sql += '`' + keys[i] + '` = ' + this.ToString(values[i]);            if (keys.length > i + 1)                sql += ', ';        }        sql += ' ' + condition;                this.database.run(sql, (err) => {            callback(err);        });    }        /**     * @param {String[]} keys     * @param {String[]} values     * @param {String} table      * @param {Function()} callback      */    static insertData(keys, values, table, callback = () => {}) {        let sql = 'INSERT INTO `' + table + '` (';        for (let i = 0; i < keys.length; i++) {            sql += '`' + keys[i] + '`';            if (keys.length > i + 1)                sql += ', ';        }        sql += ') VALUES (';        for (let i = 0; i < values.length; i++) {            sql += this.ToString(values[i]);            if (values.length > i + 1)                sql += ', ';        }        sql += ')';        this.database.run(sql, (err) => {            callback(err);        });    };    /**     *      * @param {String} table      * @param {String} condition      * @param {Function()} callback      */    static deleteData(table, condition = '', callback = () => {}) {        this.database.run('DELETE FROM `' + table + '` ' + condition, (err) => {            callback(err);        });    }    static ToString(value) {        return typeof(value) === 'string' ? '\'' + value + '\'' : value;    }}module.exports = {    database: DataBase};

На этом все, спасибо за внимание!
Проект на GitHub

Подробнее..

Сколько данных может обработать Raspberry Pi быстро

02.07.2020 00:11:00 | Автор: admin
Время обработки данных в одном знакомом мне проекте энтерпрайз-хранилища данных с реляционной моделью составляет почти 6 часов. Много это или мало?

Заметка описывает эксперимент по созданию маленькой копии энтерпрайз-хранилища данных с сильно ограниченными техническими условиями. А именно, на базе одноплатного компьютера Raspberry Pi.
Модель и архитектура будут упрощёнными, но похожими на энтерпрайз-хранилище. Результатом является оценка возможности использования Raspberry Pi в области обработки и анализа данных.




1


Роль опытного и сильного игрока будет выполнять машина Exadata Х5 (один юнит) корпорации Оракл.
Процесс обработки данных включает в себя следующие шаги:

  • Чтение из файла 10,3 ГБ 350 миллионов записей за 180 минут.
  • Обработка и очистка данных 2 SQL запроса и 130 минут.
  • Загрузка измерений 10 минут.
  • Загрузка таблиц фактов 20 миллионами новых записей 5 SQL запросов и 35 минут.

Итого, интеграция 350 миллионов записей за 6 часов, что эквивалентно 990 тысячам записей в минуту, или примерно 17 тысячам записей исходных данных в секунду.

2


В роли экспериментального оппонента будет выступать Raspberry Pi 3 Model B+ с 4х-ядерным процессором 1.4 ГГц.
В качестве хранилища используется sqlite3, чтение файлов происходит с помощью PHP.

Модель данных в реляционной базе sqlite3 описана в статье о маленьком хранилище.

Тест первый


Исходный файл access.log 37 МБ с 200 тысячами записей.

  • Прочитать лог и записать в базу данных заняло 340 секунд.
  • Загрузка измерений с 5 тысячами записей длилась 5 секунд.
  • Загрузка таблиц фактов 90 тысячами новых записей 32 секунды.

Итого, интеграция 200 тысяч записей заняла почти 7 минут, что эквивалентно 28 тысячам записей в минуту, или 470 записям исходных данных в секунду. База данных занимает 7,5 МБ; всего 8 SQL запросов для обработки данных.

Тест второй


Файл более активного сайта. Исходный файл access.log 67 МБ с 290 тысячами записей.

  • Прочитать лог и записать в базу данных заняло 670 секунд.
  • Загрузка измерений с 25 тысячами записей длилась 8 секунд.
  • Загрузка таблиц фактов 240 тысячами новых записей 80 секунд.

Итого, интеграция 290 тысяч записей заняла чуть больше 12 минут, что эквивалентно 23 тысячам записей в минуту, или 380 записям исходных данных в секунду. База данных занимает 22,9 МБ

Вывод


Для получения данных в виде модели, которая позволит проводить эффективный анализ, необходимы значительные вычислительные и материальные ресурсы, и время в любом случае.
Например, один юнит Экзадаты обходится более чем в 100К. Один Raspberry Pi стоит 60 единиц.
Линейно их нельзя сравнивать, т.к. с увеличением объёмов данных и требований надёжности возникают сложности.

Однако, если представить себе случай, когда тысяча Raspberry Pi работают параллельно, то, исходя из эксперимента, они обработают около 400 тысяч записей исходных данных в секунду.
И если решение для Экзадаты оптимировать до 40 тысяч записей в секунду, то это ощутимо меньше, чем 400 тысяч. Это подтверждает внутреннее ощущение того, что цены энтерпрайз-решений завышены.

В любом случае, Raspberry Pi отлично справлятся с обработкой данных и реляционными моделями соответствующего масштаба.

Ссылка


Домашний Raspberry Pi был настроен как веб сервер. Эксперимент с его производительностью можно провести самостоятельно по адресу. Модель базы данных (DDL), процедуры загрузки (ETL) и саму базу данных можно там же скачать.
Подробнее..

Delphi и SQLite. Альтернатива хранимым процедурам

20.06.2021 14:08:21 | Автор: admin

SQLite во многих случаях является удобным, незаменимым инструментом. Я уже не могу себе представить - как мы все жили без него. Тем не менее, есть некоторые неудобства при его использовании, связанные с тем, что это легкая встраиваемая СУБД.

Самое большое неудобство для меня, как Delphi-разработчика - отсутствие хранимых процедур. Я очень не люблю смешивать Delphi-код и SQL-скрипты. Это делает код намного менее читабильным, и затрудняет его поддержку. Следовательно, нужно как-то разнести код Delphi и тексты SQL-скриптов.

Предлагаю свой вариант решения проблемы

  • Выносим весь SQL-код в отдельный тестовый файл ресурсов, подключенный к проекту.

  • Запросы в SQL-файле разделяем маркерами начала с идентификаторами и маркерами конца. В моём случае синтаксис маркера начала - //SQL ИмяПроцедуры. Маркер конца - GO.

  • Создаем класс - менеджер SQL-запросов. При загрузке приложения он читает SQL-файл из ресурсов и составляет из него список хранимых процедур с уникальными именами-идентификаторами.

  • В процессе работы приложения мендежер извлекает текст SQL-запроса по его идентификатору для последующей его передачи на выполнение.

Главная идея - простота и легкость использования, подобная вызову хранимых процедур и удобство при создании и модификации SQL-запросов

Код юнита менеджера запросов:

unit uSqlList;interfaceuses System.Classes, Winapi.Windows, System.SysUtils,  System.Generics.Collections;type  TSqlList = class(TObjectDictionary<string, TStrings>)  const    SCRIPTS_RCNAME = 'SqlList';  private    function GetScripts(const AName: string): TStrings;    procedure FillList;    function GetItem(const AKey: string): string;  public    constructor Create;  public    property Sql[const Key: string]: string read GetItem; default;  end;var  SqlList: TSqlList;implementationfunction GetStringResource(const AName: string): string;var  LResource: TResourceStream;begin  LResource := TResourceStream.Create(hInstance, AName, RT_RCDATA);  with TStringList.Create do    try      LoadFromStream(LResource);      Result := Text;    finally      Free;      LResource.Free;    end;end;{ TScriptList }constructor TSqlList.Create;begin  inherited Create([doOwnsValues]);  FillList;end;procedure TSqlList.FillList;var  LScripts: TStrings;  I: Integer;  S, LKey: string;  LStarted: Boolean;  LSql: TStrings;begin  LScripts := GetScripts(SCRIPTS_RCNAME);  try    LStarted := False;    LSql := nil;    for I := 0 to LScripts.Count - 1 do    begin      S := LScripts[I];      if LStarted then      begin        if S = 'GO' then        begin          LStarted := False;          Continue;        end        else if not S.StartsWith('//') then          LSql.Add(S);      end      else      begin        LStarted := S.StartsWith('//SQL ');        if LStarted then        begin          LKey := S.Substring(6);          LSql := TStringList.Create;          Add(LKey, LSql);        end;        Continue;      end;    end;  finally    LScripts.Free;  end;end;function TSqlList.GetItem(const AKey: string): string;begin  Result := Items[AKey].Text;end;function TSqlList.GetScripts(const AName: string): TStrings;begin  Result := TStringList.Create;  try    Result.Text := GetStringResource(AName);  except    FreeAndNil(Result);    raise;  end;end;initializationSqlList := TSqlList.Create;finalizationFreeAndNil(SqlList);end.

Пример содержимого файла SQL-скриптов:

//SQL GetOrderSELECT * FROM Orders WHERE ID = :IDGO//SQL DeleteOpenedOrdersDELETE FROM Orders WHERE Closed = 0GO

Подключение файла скриптов к проекту:

{$R 'SqlList.res' '..\Common\DataBase\SqlList.rc'}

Использование с компонентом TFDConnection:

  Connection.ExecSQL(SqlList['GetOrder'], ['123']);

Собственно, это всё. Использую данное решение уже в нескольких проектах и мне оно кажется очень удобным. Буду благодарен за советы и замечания. Рад, если мой посто кому-то будет полезен!

Подробнее..
Категории: Delphi , Sqlite , Sqlite3

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru