Работа с SQLite в Python с помощью встроенной библиотеки sqlite3
Библиотека sqlite3 в Python
Библиотека sqlite3 входит в стандартный набор модулей Python и предоставляет интерфейс для работы с базами данных SQLite. Это легковесная реляционная СУБД, не требующая отдельного сервера. Библиотека позволяет создавать, подключаться, выполнять запросы и управлять транзакциями.
Наиболее эффективное решение для повседневных задач - использование контекстного менеджера with при подключении и выполнении запросов. Такой подход гарантирует закрытие соединения и фиксацию транзакций.
import sqlite3
# создание или подключение к базе данных
with sqlite3.connect('example.db') as conn:
cursor = conn.cursor()
# создание таблицы
cursor.execute('''CREATE TABLE IF NOT EXISTS users
(id INTEGER PRIMARY KEY, name TEXT, age INTEGER)''')
# вставка данных
cursor.execute('INSERT INTO users (name, age) VALUES (?, ?)', ('Alice', 30))
# получение данных
cursor.execute('SELECT * FROM users')
rows = cursor.fetchall()
for row in rows:
print(row)библиотека sqlite3 python (библиотека sqlite3 в python)
(1, 'Alice', 30)
Sqlalchemy библиотеки python (библиотека sqlalchemy в python)
В этом примере соединение автоматически закрывается после выхода из блока with, а изменения фиксируются. Использование параметризованных запросов (знаки ?) защищает от SQL-инъекций.
Как выполнять запросы с параметрами?
Для передачи переменных в SQL-запросы применяются плейсхолдеры ? (или именованные :name).
name = 'Bob'
age = 25
cursor.execute('INSERT INTO users (name, age) VALUES (?, ?)', (name, age))
conn.commit()Это предотвращает подстановку неэкранированных строк.
Проблема: Конкатенация строк при построении запроса (например, f'SELECT * FROM users WHERE name = "{name}"') ведёт к уязвимостям SQL-инъекций и ошибкам синтаксиса. Решение: Всегда использовать параметризованные запросы.
Как получать данные в виде словаря?
По умолчанию строки возвращаются в виде кортежей. Для получения словаря (с именами столбцов) используется атрибут row_factory.
conn.row_factory = sqlite3.Row
cursor = conn.cursor()
cursor.execute('SELECT * FROM users')
for row in cursor:
print(row['name'], row['age'])
# или dict(row) для преобразования в обычный словарьЭто удобно при работе с большим количеством полей.
Как обрабатывать ошибки базы данных?
Исключения, связанные с SQLite, наследуются от sqlite3.Error. Рекомендуется оборачивать операции в блок try/except.
try:
cursor.execute('INVALID SQL')
except sqlite3.IntegrityError as e:
print('Нарушение целостности:', e)
except sqlite3.OperationalError as e:
print('Ошибка выполнения:', e)
except sqlite3.Error as e:
print('Другая ошибка SQLite:', e)Типичные ошибки: IntegrityError - при нарушении уникальности или внешнего ключа; OperationalError - например, при попытке создать уже существующую таблицу без IF NOT EXISTS.
Как управлять транзакциями?
По умолчанию каждый execute выполняется в отдельной транзакции, если не включён isolation_level=None. Для явного управления используются commit() и rollback().
with sqlite3.connect('example.db') as conn:
conn.execute('CREATE TABLE IF NOT EXISTS logs (msg TEXT)')
conn.execute('INSERT INTO logs VALUES (?)', ('start',))
conn.commit() # фиксация
conn.execute('INSERT INTO logs VALUES (?)', ('error',))
conn.rollback() # откат, вставка не сохранитсяАвтоматическая фиксация: При использовании with контекстный менеджер вызывает commit при успешном завершении блока, иначе rollback.
Как вставлять множество записей?
Метод executemany позволяет выполнить один и тот же запрос для списка кортежей.
data = [('Charlie', 35), ('Diana', 28), ('Eve', 22)]
cursor.executemany('INSERT INTO users (name, age) VALUES (?, ?)', data)
conn.commit()Это быстрее, чем вызов execute в цикле.
Как выполнить несколько операторов из скрипта?
Метод executescript принимает строку с несколькими SQL-командами, разделёнными точкой с запятой.
script = '''
CREATE TABLE IF NOT EXISTS products (id INTEGER PRIMARY KEY, name TEXT, price REAL);
INSERT INTO products VALUES (1, 'Laptop', 999.99);
INSERT INTO products VALUES (2, 'Mouse', 25.50);
'''
cursor.executescript(script)
conn.commit()Важно: executescript перед выполнением неявно вызывает commit для предыдущих транзакций.
Как зарегистрировать пользовательскую функцию SQLite?
Модуль позволяет добавлять функции Python, доступные в SQL-запросах.
def reverse_string(s):
return s[::-1] if s else ''
conn.create_function('reverse', 1, reverse_string)
cursor.execute('SELECT reverse(name) FROM users')
print(cursor.fetchall())Это полезно для вычислений, не реализованных в SQLite.
Расширенные примеры работы с sqlite3
Ниже приведены менее распространённые, но полезные сценарии использования библиотеки.
Подключение к базе в оперативной памяти
Временная база данных, существующая только во время работы приложения, полезна для тестов.
import sqlite3
conn = sqlite3.connect(':memory:')
cursor = conn.cursor()
cursor.execute('CREATE TABLE test (col1 TEXT)')
cursor.execute('INSERT INTO test VALUES ("data")')
cursor.execute('SELECT * FROM test')
print(cursor.fetchall())
conn.close()[('data',)]Использование собственной агрегатной функции
Пример функции, вычисляющей медиану значений.
import sqlite3
class Median:
def __init__(self):
self.values = []
def step(self, value):
if value is not None:
self.values.append(value)
def finalize(self):
if not self.values:
return None
sorted_vals = sorted(self.values)
n = len(sorted_vals)
if n % 2 == 1:
return sorted_vals[n//2]
else:
return (sorted_vals[n//2 - 1] + sorted_vals[n//2]) / 2
conn = sqlite3.connect(':memory:')
conn.create_aggregate('median', 1, Median)
cursor = conn.cursor()
cursor.execute('CREATE TABLE nums (val REAL)')
cursor.execute('INSERT INTO nums VALUES (1), (2), (3), (10)')
cursor.execute('SELECT median(val) FROM nums')
print(cursor.fetchone()[0])2.5
Работа с BLOB (бинарными данными)
SQLite поддерживает хранение бинарных объектов. В Python они передаются как bytes.
import sqlite3
conn = sqlite3.connect('blob_example.db')
cursor = conn.cursor()
cursor.execute('CREATE TABLE IF NOT EXISTS images (id INTEGER PRIMARY KEY, data BLOB)')
with open('image.png', 'rb') as f:
img_data = f.read()
cursor.execute('INSERT INTO images (data) VALUES (?)', (img_data,))
conn.commit()
# Чтение BLOB
cursor.execute('SELECT data FROM images WHERE id=1')
retrieved_data = cursor.fetchone()[0]
print(type(retrieved_data), len(retrieved_data))
conn.close()Результат:
<class 'bytes'> 12345 (размер файла)
Использование режима WAL (Write-Ahead Logging)
Режим WAL улучшает параллельный доступ при чтении. Устанавливается с помощью PRAGMA.
conn = sqlite3.connect('example_wal.db')
cursor = conn.cursor()
cursor.execute('PRAGMA journal_mode=WAL')
print(cursor.fetchone()) # ('wal',)
# Далее обычные операцииЭтот режим полезен для приложений, где много одновременных читателей.
Обработка внешних ключей
По умолчанию ограничения внешних ключей отключены. Включение:
conn = sqlite3.connect('test.db')
cursor = conn.cursor()
cursor.execute('PRAGMA foreign_keys = ON')
# Теперь внешние ключи будут проверятьсяБез этого каскадные операции (ON DELETE CASCADE) могут не срабатывать.
Копирование базы данных в память
Можно перенести содержимое файловой БД в оперативную для ускорения.
import sqlite3
disk_conn = sqlite3.connect('example.db')
disk_conn.backup(sqlite3.connect(':memory:'))
# Теперь memory_conn содержит ту же схему и данныеМетод backup() доступен с Python 3.7.
Использование курсора итератора
Курсор поддерживает итерацию, что экономит память при больших результатах.
cursor = conn.execute('SELECT * FROM large_table')
for row in cursor:
# обработка строки без загрузки всего результата
passЭто предпочтительно для выборок, которые не помещаются в памяти.