Назад к статьям

База данных для бота: SQLite и PostgreSQL

В статье разберём хранение данных для бота: пользователи, заказы, теги для рассылок. Примеры на SQLite (простой старт) и PostgreSQL (масштаб и надёжность).

Какие таблицы нужны

Типичный минимум: users (user_id, username, дата первого визита, при необходимости тег или сегмент), orders (id, user_id, сумма, статус, дата), при рассылках — таблица тегов (user_id, tag). Состояния FSM aiogram можно хранить в памяти или в Redis/БД — в примерах ниже только пользователи и заказы.

SQLite

Один файл на диске, отдельный сервер БД не нужен. Удобно для прототипов и небольших ботов. Подключение через встроенный sqlite3 или aiosqlite для async.

Пример: создание таблицы пользователей и сохранение при /start. Для конкурентной записи в async-боте лучше использовать aiosqlite или вызывать save_user в отдельном потоке.

import sqlite3 conn = sqlite3.connect("bot.db") conn.execute(""" CREATE TABLE IF NOT EXISTS users ( user_id INTEGER PRIMARY KEY, username TEXT, first_seen TEXT DEFAULT CURRENT_TIMESTAMP ) """) conn.commit() def save_user(user_id: int, username: str = None): conn.execute( "INSERT OR IGNORE INTO users (user_id, username) VALUES (?, ?)", (user_id, username), ) conn.commit() # В хендлере /start: # save_user(message.from_user.id, message.from_user.username)

PostgreSQL (asyncpg)

Для большого числа пользователей и сложных запросов лучше PostgreSQL: транзакции, бэкапы, надёжность. Подключение через пул asyncpg. Пример пула и двух функций — получить пользователя и добавить заказ:

import asyncpg # При старте бота: pool = await asyncpg.create_pool("postgresql://user:pass@localhost/botdb", min_size=1, max_size=10) async def get_user(user_id: int): async with pool.acquire() as conn: row = await conn.fetchrow("SELECT * FROM users WHERE user_id = $1", user_id) return dict(row) if row else None async def add_order(user_id: int, amount: int, status: str = "new"): async with pool.acquire() as conn: await conn.execute( "INSERT INTO orders (user_id, amount, status) VALUES ($1, $2, $3)", user_id, amount, status, )

Таблицы создаются отдельно (миграциями или вручную). Пример схемы для orders: CREATE TABLE orders (id SERIAL PRIMARY KEY, user_id BIGINT NOT NULL, amount INTEGER NOT NULL, status VARCHAR(50), created_at TIMESTAMPTZ DEFAULT NOW());

Миграции

При изменении схемы (новые поля, таблицы) не меняйте БД «вручную» на проде без плана. Фиксируйте изменения в скриптах миграций (например, папка migrations/, файлы 001_create_users.sql, 002_add_orders.sql) и применяйте их по порядку. Так проще откатываться и держать dev/prod в одном состоянии.

Итоги

SQLite — для старта и небольших ботов; PostgreSQL — для масштаба и продакшена. Продумайте схему (users, orders, теги) и ведите миграции при любых изменениях структуры.