// Основные методы класса ZennoPoster.Db:// 1. ExecuteNonQuery - выполнение запросов без возврата данных (INSERT, UPDATE, DELETE)// 2. ExecuteScalar - получение одного значения// 3. ExecuteQuery - выполнение запросов с заполнением таблицы/списка
1.2 ExecuteNonQuery - Выполнение команд без возврата данных
// Удаление записи из базы данныхint rowsAffected = ZennoPoster.Db.ExecuteNonQuery( "DELETE FROM User WHERE Id = 2", null, // параметры запроса ZennoLab.InterfacesLibrary.Enums.Db.DbProvider.SqlClient, "Data Source=SQLSERVER;Initial Catalog=TestDb;Integrated Security=True;max pool size=500");// Под капотом: отправляется SQL запрос на удаление, возвращается количество затронутых строк// INSERT запросint inserted = ZennoPoster.Db.ExecuteNonQuery( "INSERT INTO Users (Name, Age) VALUES ('John', 25)", null, ZennoLab.InterfacesLibrary.Enums.Db.DbProvider.SqlClient, "connection_string_here");// Результат: inserted = 1 (если вставка успешна)
1.3 ExecuteScalar - Получение одного значения
// Подсчет количества записейstring count = ZennoPoster.Db.ExecuteScalar( "SELECT COUNT(*) FROM User", null, ZennoLab.InterfacesLibrary.Enums.Db.DbProvider.SqlClient, "Data Source=SQLSERVER;Initial Catalog=TestDb;Integrated Security=True");// Под капотом: выполняется запрос, возвращается ТОЛЬКО первая колонка первой строки// Если результат: COUNT(*) = 150, то count = "150"// Получение конкретного значенияstring userName = ZennoPoster.Db.ExecuteScalar( "SELECT Name FROM User WHERE Id = 1", null, ZennoLab.InterfacesLibrary.Enums.Db.DbProvider.SqlClient, "connection_string_here");// Если в БД: Id=1, Name="Иван", Age=25// Результат: userName = "Иван" (Age игнорируется, берется только первая колонка)
1.4 ExecuteQuery - Заполнение таблиц и списков
// Заполнение таблицы проектаIZennoTable table = project.Tables["Table 1"];int rowCount = ZennoPoster.Db.ExecuteQuery( "SELECT * FROM User", null, ZennoLab.InterfacesLibrary.Enums.Db.DbProvider.SqlClient, "Data Source=SQLSERVER;Initial Catalog=TestDb;Integrated Security=True", ref table);// Под капотом: все строки результата добавляются в таблицу ZennoPoster// Если в User 10 записей с полями Id, Name, Age - все 10 строк попадут в Table 1// Заполнение списка с разделителемIZennoList list = project.Lists["List 1"];int rows = ZennoPoster.Db.ExecuteQuery( "SELECT Name, Age FROM User", null, ZennoLab.InterfacesLibrary.Enums.Db.DbProvider.SqlClient, "connection_string_here", ref list, " | " // разделитель полей);// Результат в списке:// "Иван | 25"// "Петр | 30"// "Мария | 22"
ЧАСТЬ 2: РАСШИРЕННАЯ БИБЛИОТЕКА z3nCore.Db
2.1 Основные методы работы с данными - ДЕТАЛЬНЫЙ РАЗБОР
DbGet - Получение данных из таблицы
// ПРИМЕР 1: Простое получение одного поляproject.Variables["acc0"].Value = "5"; // устанавливаем ID записиstring username = project.DbGet("username", "users");// Что происходит под капотом:// 1. Формируется SQL запрос: SELECT "username" FROM "users" WHERE "id" = 5// 2. Выполняется запрос к БД// 3. Возвращается значение поля username для записи с id=5// Результат: username = "john_doe"// ПРИМЕР 2: Получение нескольких полейstring userData = project.DbGet("name,email,phone", "clients");// SQL под капотом: SELECT "name", "email", "phone" FROM "clients" WHERE "id" = {acc0}// Если в БД: id=5, name="Иван", email="ivan@mail.ru", phone="+7900111"// Результат: userData = "Иван\nivan@mail.ru\n+7900111" (поля разделены \n)// ПРИМЕР 3: Использование альтернативного ключаstring data = project.DbGet("balance", "accounts", key: "login", acc: "admin");// SQL под капотом: SELECT "balance" FROM "accounts" WHERE "login" = admin// Ищет запись не по id, а по полю login со значением admin
DbUpd - Обновление данных в таблице
// ПРИМЕР 1: Обновление одного поляproject.Variables["acc0"].Value = "10";project.DbUpd("status = 'active'", "users");// SQL под капотом: // UPDATE "users" SET "status" = 'active', "last" = '12-28T15:30' WHERE "id" = 10// Обратите внимание: автоматически добавляется обновление поля last с текущей датой!// ПРИМЕР 2: Обновление нескольких полейproject.DbUpd("balance = 100, verified = 1, comment = 'VIP client'", "accounts");// SQL под капотом:// UPDATE "accounts" SET "balance" = 100, "verified" = 1, "comment" = 'VIP client', "last" = '12-28T15:30' WHERE "id" = {acc0}// ПРИМЕР 3: Отключение автоматического обновления lastproject.DbUpd("name = 'Test'", "users", last: false);// SQL под капотом:// UPDATE "users" SET "name" = 'Test' WHERE "id" = {acc0}// Поле last НЕ обновляется
DbGetRandom - Получение случайной записи
// ПРИМЕР 1: Простое получение случайного значенияstring randomUser = project.DbGetRandom("username", "users");// SQL под капотом:// SELECT "username" FROM "users" // WHERE TRIM("username") != '' -- исключаем пустые значения// AND id < {range} -- ограничиваем диапазоном (если указан)// ORDER BY RANDOM() -- случайная сортировка// LIMIT 1; -- берем только одну запись// Из таблицы users с записями:// id=1, username="" <- пропускается (пустое)// id=2, username="alice" <- может быть выбрана// id=3, username="bob" <- может быть выбрана// id=4, username="charlie" <- может быть выбрана// Результат: randomUser = "bob" (случайно выбранный непустой username)// ПРИМЕР 2: Получение с ID записиstring randomWithId = project.DbGetRandom("email", "users", acc: true);// SQL под капотом:// SELECT "id", "email" FROM "users" // WHERE TRIM("email") != ''// AND id < {range}// ORDER BY RANDOM()// LIMIT 1;// Результат: randomWithId = "7\nuser7@mail.com" // Первая строка - ID записи, вторая - значение email// ПРИМЕР 3: С указанием диапазонаstring randomFromRange = project.DbGetRandom("phone", "contacts", range: 100);// SQL под капотом:// SELECT "phone" FROM "contacts"// WHERE TRIM("phone") != ''// AND id < 100 -- только записи с id меньше 100// ORDER BY RANDOM()// LIMIT 1;// ПРИМЕР 4: Инверсия условия (получить ТОЛЬКО пустые)string emptyField = project.DbGetRandom("description", "products", invert: true);// SQL под капотом:// SELECT "description" FROM "products"// WHERE TRIM("description") = '' -- ищем ТОЛЬКО пустые// AND id < {range}// ORDER BY RANDOM()// LIMIT 1;
SqlGet и SqlUpd - Расширенные методы с WHERE
// ПРИМЕР 1: SqlGet с условием WHEREstring activeUsers = project.SqlGet("id,login,email", "accounts", where: "status = 'active' AND created_at > '2024-01-01'");// SQL под капотом:// SELECT "id", "login", "email" FROM "accounts" // WHERE status = 'active' AND created_at > '2024-01-01'// Результат для нескольких записей:// "1\njohn\njohn@mail.com\n5\nalice\nalice@mail.com\n8\nbob\nbob@mail.com"// Все поля всех записей идут подряд через \n// ПРИМЕР 2: SqlUpd с условием WHEREproject.SqlUpd("status = 'expired', discount = 0", "subscriptions", where: "end_date < '2024-12-01'");// SQL под капотом:// UPDATE "subscriptions" // SET "status" = 'expired', "discount" = 0, "last" = '12-28T15:30'// WHERE end_date < '2024-12-01'// Обновятся ВСЕ записи, удовлетворяющие условию WHERE
2.2 Управление таблицами - ДЕТАЛЬНЫЙ РАЗБОР
TblAdd - Создание таблицы
var tableStructure = new Dictionary<string, string>{ { "id", "INTEGER PRIMARY KEY" }, { "username", "TEXT DEFAULT ''" }, { "balance", "REAL DEFAULT 0" }, { "is_active", "INTEGER DEFAULT 1" }};project.TblAdd(tableStructure, "accounts");// SQL под капотом для SQLite:// CREATE TABLE "accounts" (// "id" INTEGER PRIMARY KEY,// "username" TEXT DEFAULT '',// "balance" REAL DEFAULT 0,// "is_active" INTEGER DEFAULT 1// );// SQL под капотом для PostgreSQL:// CREATE TABLE "accounts" (// "id" SERIAL, -- AUTOINCREMENT заменяется на SERIAL// "username" TEXT DEFAULT '',// "balance" REAL DEFAULT 0,// "is_active" INTEGER DEFAULT 1// );// Если таблица уже существует - ничего не произойдет
TblExist - Проверка существования таблицы
if (project.TblExist("users")){ project.SendInfoToLog("Таблица существует", true);}// SQL под капотом для SQLite:// SELECT COUNT(*) FROM sqlite_master // WHERE type='table' AND name='users';// Возвращает "1" если существует, "0" если нет// SQL под капотом для PostgreSQL:// SELECT COUNT(*) FROM information_schema.tables // WHERE table_schema = 'public' AND table_name = 'users';
AddRange - Инициализация диапазона записей
// ПРИМЕР 1: Создание 50 пустых записейproject.AddRange("tasks", 50);// Что происходит под капотом:// 1. Проверяется максимальный существующий ID:// SELECT COALESCE(MAX("id"), 0) FROM "tasks"; -- например, вернет 10// // 2. Добавляются недостающие записи:// INSERT INTO "tasks" ("id") VALUES (11) ON CONFLICT DO NOTHING;// INSERT INTO "tasks" ("id") VALUES (12) ON CONFLICT DO NOTHING;// ...// INSERT INTO "tasks" ("id") VALUES (50) ON CONFLICT DO NOTHING;//// Результат: в таблице tasks будут записи с id от 1 до 50// Все поля кроме id будут иметь значения по умолчанию// ПРИМЕР 2: Использование переменной rangeEndproject.Variables["rangeEnd"].Value = "100";project.AddRange("accounts"); // range берется из переменной// Создаст записи до id=100 включительно
2.3 Управление колонками - ДЕТАЛЬНЫЙ РАЗБОР
ClmnAdd - Добавление колонок
// ПРИМЕР 1: Добавление одной колонкиproject.ClmnAdd("phone", "users");// SQL под капотом:// ALTER TABLE "users" ADD COLUMN "phone" TEXT DEFAULT '';// Таблица ДО:// id | username | email// 1 | alice | alice@mail.com// 2 | bob | bob@mail.com// Таблица ПОСЛЕ:// id | username | email | phone// 1 | alice | alice@mail.com | // 2 | bob | bob@mail.com | // ПРИМЕР 2: Добавление колонки с другим типомproject.ClmnAdd("age", "users", defaultValue: "INTEGER DEFAULT 0");// SQL под капотом:// ALTER TABLE "users" ADD COLUMN "age" INTEGER DEFAULT 0;// ПРИМЕР 3: Массовое добавление колонокvar newColumns = new Dictionary<string, string>{ { "created_at", "TEXT DEFAULT CURRENT_TIMESTAMP" }, { "updated_at", "TEXT DEFAULT ''" }, { "deleted", "INTEGER DEFAULT 0" }};project.ClmnAdd(newColumns, "users");// Выполнится 3 SQL запроса:// ALTER TABLE "users" ADD COLUMN "created_at" TEXT DEFAULT CURRENT_TIMESTAMP;// ALTER TABLE "users" ADD COLUMN "updated_at" TEXT DEFAULT '';// ALTER TABLE "users" ADD COLUMN "deleted" INTEGER DEFAULT 0;
ClmnDrop - Удаление колонок
project.ClmnDrop("temporary_field", "users");// SQL под капотом для PostgreSQL:// ALTER TABLE "users" DROP COLUMN "temporary_field" CASCADE;// SQL под капотом для SQLite:// ALTER TABLE "users" DROP COLUMN "temporary_field";// ВАЖНО: В SQLite удаление колонок может не поддерживаться в старых версиях
ClmnPrune - Удаление лишних колонок
// Определяем какие колонки должны остатьсяvar requiredColumns = new Dictionary<string, string>{ { "id", "INTEGER PRIMARY KEY" }, { "username", "TEXT DEFAULT ''" }, { "email", "TEXT DEFAULT ''" }};// В таблице есть колонки: id, username, email, phone, address, temp_dataproject.ClmnPrune(requiredColumns, "users");// Что происходит под капотом:// 1. Получается список текущих колонок// 2. Сравнивается с requiredColumns// 3. Удаляются лишние:// ALTER TABLE "users" DROP COLUMN "phone";// ALTER TABLE "users" DROP COLUMN "address";// ALTER TABLE "users" DROP COLUMN "temp_data";// В результате останутся только: id, username, email
2.4 Специальные методы - ДЕТАЛЬНЫЙ РАЗБОР
DbSettings - Загрузка настроек из БД
project.DbSettings();// SQL под капотом:// SELECT "id", "value" FROM "_settings"// Таблица _settings:// id | value// ------------|----------------// apiKey | sk-1234567890// maxRetries | 5// timeout | 30000// baseUrl | https://api.example.com// После выполнения создаются переменные проекта:// project.Variables["apiKey"].Value = "sk-1234567890"// project.Variables["maxRetries"].Value = "5"// project.Variables["timeout"].Value = "30000"// project.Variables["baseUrl"].Value = "https://api.example.com"
MigrateTable - Копирование таблицы
project.MigrateTable("users_old", "users_new");// Что происходит под капотом:// 1. Создается новая таблица users_new с той же структурой// 2. Копируются все данные: // INSERT INTO "users_new" SELECT * FROM "users_old"// 3. Переименовываются специальные колонки:// ALTER TABLE "users_new" RENAME COLUMN "acc0" TO "id"// ALTER TABLE "users_new" RENAME COLUMN "key" TO "id"
2.5 Низкоуровневый метод DbQ
// DbQ - прямое выполнение любого SQL запроса// SELECT запросы возвращают данныеstring result = project.DbQ("SELECT username FROM users WHERE id = 5");// Результат: "alice"string multiRow = project.DbQ("SELECT username FROM users LIMIT 3");// Результат: "alice\nbob\ncharlie" (строки через \n)// Запросы модификации возвращают количество затронутых строкstring affected = project.DbQ("UPDATE users SET status = 'active' WHERE age > 18");// Результат: "15" (обновлено 15 записей)// С логированием для отладкиproject.DbQ("DELETE FROM logs WHERE created_at < '2024-01-01'", log: true);// В лог выведется: 🐘 [DELETE FROM logs WHERE created_at < '2024-01-01'] - [120]// (удалено 120 записей, 🐘 означает PostgreSQL)// Защита от опасных запросовproject.DbQ("DELETE FROM users"); // ОШИБКА! DELETE без WHERE запрещенproject.DbQ("DROP TABLE users"); // ОШИБКА! DROP без WHERE запрещен
2.6 Практические примеры комплексного использования
// Создаем стандартную таблицу с базовыми полямиproject.Variables["projectTable"].Value = "my_accounts";project.Variables["cfgToDo"].Value = "register,verify,post"; // задачи проектаproject.TblPrepareDefault();// Под капотом создастся таблица:// id | status | last | register | verify | post// И заполнится диапазоном записей согласно rangeEnd
"Хочу получить случайный неиспользованный аккаунт"
// Ищем аккаунт без статуса 'used'string account = project.DbGetRandom("login,password", "accounts", acc: true);string[] data = account.Split('\n');project.Variables["acc0"].Value = data[0]; // сохраняем ID// Сразу помечаем как используемыйproject.DbUpd("status = 'in_progress'", "accounts");// После работыproject.DbUpd("status = 'used'", "accounts");
"Нужно обработать все записи по очереди"
// Получаем следующую необработанную записьstring nextId = project.DbQ(@" SELECT MIN(id) FROM tasks WHERE status = '' OR status IS NULL");if (!string.IsNullOrEmpty(nextId)){ project.Variables["acc0"].Value = nextId; // Обрабатываем... project.DbStatus("completed", "tasks");}else{ throw new Exception("Все задачи выполнены");}
"Хочу перенести данные между таблицами"
// Копируем только нужные записи в архивproject.DbQ(@" INSERT INTO accounts_archive (id, login, password, status) SELECT id, login, password, status FROM accounts WHERE status = 'banned'");// Удаляем перенесенныеproject.DbQ("DELETE FROM accounts WHERE status = 'banned'");
"Нужно сбросить статусы и начать заново"
// Сбрасываем все статусыproject.DbQ("UPDATE accounts SET status = '', last = ''");// Или только для определенных записейproject.DbQ(@" UPDATE accounts SET status = '', error_count = 0 WHERE status = 'error' AND error_count < 3");
"Хочу найти дубликаты в таблице"
// Находим дублирующиеся emailstring duplicates = project.DbQ(@" SELECT email, COUNT(*) as cnt FROM users GROUP BY email HAVING COUNT(*) > 1");// Результат: "test@mail.com\n3\nadmin@site.ru\n2"// Означает: test@mail.com встречается 3 раза, admin@site.ru - 2 раза
"Нужно добавить новое поле во все таблицы проекта"
// Список таблиц проектаstring[] tables = { "accounts", "proxies", "results", "logs" };// Добавляем поле во все таблицыforeach (string table in tables){ if (project.TblExist(table)) { project.ClmnAdd("project_version", table, defaultValue: "TEXT DEFAULT 'v1.0'"); }}
"Хочу работать с JSON данными в БД"
// Сохраняем JSON в полеvar data = new { cookies = "cookie_string", headers = new { userAgent = "Mozilla/5.0" }, timestamp = DateTime.Now};string json = Global.ZennoLab.Json.JsonConvert.SerializeObject(data);project.DbUpd($"session_data = '{json.Replace("'", "''")}'", "accounts");// Читаем и парсим JSONstring jsonFromDb = project.DbGet("session_data", "accounts");project.Json.FromString(jsonFromDb);string cookies = project.Json.cookies;
"Нужна статистика по проекту"
// Общая статистикаstring stats = project.DbQ(@" SELECT COUNT(*) as total, COUNT(CASE WHEN status = 'success' THEN 1 END) as success, COUNT(CASE WHEN status = 'error' THEN 1 END) as errors, COUNT(CASE WHEN status = '' THEN 1 END) as pending FROM accounts");// Результат: "100\n45\n10\n35" (всего, успех, ошибки, ожидают)// Статистика по днямstring daily = project.DbQ(@" SELECT substr(last, 1, 5) as day, COUNT(*) as processed FROM accounts WHERE last != '' GROUP BY substr(last, 1, 5) ORDER BY day DESC");
"База повреждена или нужно почистить"
// Удаляем пустые записиproject.DbQ(@" DELETE FROM accounts WHERE login = '' AND password = '' AND status = ''");// Исправляем битые данныеproject.DbQ(@" UPDATE accounts SET status = 'need_check' WHERE status NOT IN ('new', 'active', 'error', 'banned')");// Вакуум для SQLite (уменьшение размера файла)if (project.Variables["DBmode"].Value == "SQLite"){ project.DbQ("VACUUM");}
"Нужно мигрировать с SQLite на PostgreSQL"
// Сначала работаем с SQLiteproject.Variables["DBmode"].Value = "SQLite";project.Variables["DBsqltPath"].Value = @"C:\old_database.db";// Переключаемся и мигрируемproject.Variables["DBmode"].Value = "PostgreSQL";project.Variables["DBpstgrPass"].Value = "myPassword";project.MigrateAllTables(); // Автоматически перенесет все таблицы