SQLite3 и Python: настройка, запросы, ошибки и примеры использования
Основы работы с SQLite3 в Python
Наиболее эффективный способ работы с SQLite3 в Python
Для работы с базой данных SQLite3 используется встроенный модуль sqlite3. Пример подключения, создания таблицы и вставки данных:
import sqlite3
# Подключение к базе данных (файл example.db)
conn = sqlite3.connect('example.db')
cursor = conn.cursor()
# Создание таблицы
cursor.execute('''CREATE TABLE IF NOT EXISTS users
(id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
age INTEGER)''')
# Вставка записи
cursor.execute("INSERT INTO users (name, age) VALUES ('Alice', 30)")
conn.commit()
# Выборка данных
cursor.execute("SELECT * FROM users")
for row in cursor.fetchall():
print(row)
# Закрытие соединения
conn.close()Python база данных sqlite3 (работа с sqlite3 в python)
Данный подход гарантирует управление соединением вручную, что полезно для полного контроля над транзакциями. Однако важно не забывать вызывать commit() и закрывать соединение, иначе изменения могут быть потеряны.
Почему данные не сохраняются после вставки?
Ошибка: отсутствие вызова conn.commit() – данные остаются в транзакции и теряются при закрытии соединения.
Решение: всегда фиксировать транзакцию явно или использовать контекстный менеджер.
Как использовать контекстный менеджер для автоматического закрытия соединения?
Python 3.7+ позволяет использовать sqlite3.connect() как контекстный менеджер. При выходе из блока соединение автоматически закрывается, а транзакция фиксируется, если не возникло исключение.
import sqlite3
with sqlite3.connect('example.db') as conn:
cursor = conn.cursor()
cursor.execute("INSERT INTO users (name, age) VALUES ('Bob', 25)")
# commit происходит автоматически после выхода из withPython библиотеки для баз данных (библиотеки python для баз данных)
Этот способ снижает риск утечки ресурсов и упрощает код.
Что если нужно откатить транзакцию при ошибке?
При использовании контекстного менеджера исключение приводит к откату. Если требуется откатить без исключения, используйте conn.rollback() внутри блока.
Как защититься от SQL-инъекций?
Проблема: конкатенация строк в SQL-запросах позволяет внедрить вредоносный код.
# Опасный код
user_input = "' OR '1'='1"
cursor.execute(f"SELECT * FROM users WHERE name = '{user_input}'")
# Выполнит SELECT * FROM users WHERE name = '' OR '1'='1'
Решение: использовать параметризованные запросы. В sqlite3 параметры передаются через ? или именованные метки.
safe_input = "Alice"
cursor.execute("SELECT * FROM users WHERE name = ?", (safe_input,))
Параметры автоматически экранируются, что предотвращает инъекции.
Как работать с базой данных в оперативной памяти (временная БД)?
Используйте специальное имя ':memory:' при подключении. Такая база существует только во время работы программы.
import sqlite3
with sqlite3.connect(':memory:') as conn:
cursor = conn.cursor()
cursor.execute('CREATE TABLE test (value INTEGER)')
cursor.execute('INSERT INTO test VALUES (1)')
cursor.execute('SELECT * FROM test')
print(cursor.fetchall()) # [(1,)]
Применяется для тестирования или временных вычислений.
Как обрабатывать исключения при работе с SQLite3?
sqlite3 выбрасывает исключение sqlite3.DatabaseError при проблемах с БД. Типичные ошибки:
sqlite3.OperationalError– синтаксические ошибки SQL, отсутствие таблицы, блокировка.sqlite3.IntegrityError– нарушение ограничений (PRIMARY KEY, UNIQUE, NOT NULL).
try:
cursor.execute("INSERT INTO users (name) VALUES (NULL)")
except sqlite3.IntegrityError as e:
print("Ошибка целостности:", e)
Рекомендуется оборачивать операции с БД в try-except для корректной обработки сбоев.
Как выбрать только одну строку или часть результатов?
Метод fetchone() возвращает одну запись, fetchmany(size) – заданное количество.
cursor.execute("SELECT * FROM users")
first = cursor.fetchone()
print(first) # первая запись или None
next_five = cursor.fetchmany(5)
for row in next_five:
print(row)
Это удобно для постраничного вывода.
Как управлять транзакциями вручную?
По умолчанию sqlite3 начинает неявную транзакцию перед каждым DML-запросом. Для явного управления используйте BEGIN, COMMIT, ROLLBACK.
conn = sqlite3.connect('example.db')
cursor = conn.cursor()
cursor.execute("BEGIN")
try:
cursor.execute("INSERT INTO users (name) VALUES ('Charlie')")
cursor.execute("INSERT INTO users (name) VALUES ('Dave')")
cursor.execute("COMMIT")
except:
cursor.execute("ROLLBACK")
raise
finally:
conn.close()
Такой подход даёт полный контроль над атомарностью операций.
Как работать с датами и временем в SQLite3?
SQLite не имеет отдельного типа для даты, но Python-объекты datetime можно сохранять как текст в формате ISO или как число (timestamp). Модуль sqlite3 автоматически преобразует datetime в строку при вставке и обратно при чтении, если установлен detect_types.
from datetime import datetime
conn = sqlite3.connect('example.db', detect_types=sqlite3.PARSE_DECLTYPES)
cursor = conn.cursor()
cursor.execute('''CREATE TABLE IF NOT EXISTS events
(id INTEGER PRIMARY KEY, event_date TIMESTAMP)''')
cursor.execute("INSERT INTO events (event_date) VALUES (?)", (datetime.now(),))
conn.commit()
cursor.execute("SELECT event_date FROM events")
row = cursor.fetchone()
print(row[0]) # datetime объект
Без detect_types дата вернется как строка.
Почему возникает ошибка 'database is locked'?
Причина: multiple concurrent connections пытаются писать в базу, или не закрыто предыдущее соединение.
Решение: использовать timeout при подключении (connect('file.db', timeout=5)), либо закрывать соединения своевременно, либо использовать WAL-режим.
conn = sqlite3.connect('example.db', timeout=10)
# При блокировке будет ждать до 10 секунд, затем выбросит исключение.
Как использовать row factory для доступа к столбцам по имени?
По умолчанию строки возвращаются как кортежи. Можно установить sqlite3.Row в качестве фабрики, чтобы обращаться к полям по имени.
conn.row_factory = sqlite3.Row
cursor = conn.cursor()
cursor.execute("SELECT name, age FROM users")
for row in cursor:
print(row['name'], row['age'])
Удобно для читаемого кода.
Расширенные примеры работы с SQLite3 в Python
Пример 1: Использование executemany для массовой вставки данных
Метод executemany позволяет вставить несколько строк за один вызов, что значительно ускоряет операцию.
import sqlite3
with sqlite3.connect('example.db') as conn:
cursor = conn.cursor()
data = [('Eve', 28), ('Frank', 35), ('Grace', 22)]
cursor.executemany("INSERT INTO users (name, age) VALUES (?, ?)", data)
# После выхода из with commit автоматический
# В таблицу users добавлены три записи
Пример 2: Выполнение скрипта SQL из файла
Если у вас есть файл с SQL-командами, его можно выполнить с помощью executescript.
# prepare.sql:
# CREATE TABLE IF NOT EXISTS products (id INTEGER PRIMARY KEY, name TEXT, price REAL);
# INSERT INTO products (name, price) VALUES ('Laptop', 999.99);
with sqlite3.connect('shop.db') as conn:
with open('prepare.sql', 'r', encoding='utf-8') as f:
sql = f.read()
conn.executescript(sql)
cursor = conn.execute("SELECT * FROM products")
print(cursor.fetchall())
Пример 3: Использование пользовательских функций в SQLite
Можно зарегистрировать Python-функцию и вызывать её внутри SQL.
import sqlite3
import math
def circle_area(radius):
return math.pi * radius * radius
with sqlite3.connect(':memory:') as conn:
conn.create_function("circle_area", 1, circle_area)
cursor = conn.execute("SELECT circle_area(3.0)")
print(cursor.fetchone()[0]) # 28.274333882308138
28.274333882308138
Пример 4: Работа с BLOB-данными (изображения, файлы)
Бинарные данные можно хранить в столбце типа BLOB, передавая объект bytes.
import sqlite3
with sqlite3.connect('files.db') as conn:
conn.execute('''CREATE TABLE IF NOT EXISTS images
(id INTEGER PRIMARY KEY, data BLOB)''')
with open('photo.jpg', 'rb') as f:
blob = f.read()
conn.execute("INSERT INTO images (data) VALUES (?)", (blob,))
cursor = conn.execute("SELECT data FROM images WHERE id=1")
row = cursor.fetchone()
if row:
with open('output.jpg', 'wb') as out:
out.write(row[0])
Файл output.jpg создан, содержит копию photo.jpg
Пример 5: Использование агрегатных функций и сортировки
Пример с группировкой и сортировкой по возрасту.
import sqlite3
with sqlite3.connect('example.db') as conn:
cursor = conn.cursor()
cursor.execute('''SELECT name, age FROM users
ORDER BY age DESC
LIMIT 3''')
for name, age in cursor:
print(f"{name}: {age}")
Frank: 35 Alice: 30 Eve: 28
Пример 6: Обработка ошибок с откатом транзакции вручную
import sqlite3
conn = sqlite3.connect('test.db')
cursor = conn.cursor()
try:
cursor.execute("BEGIN")
cursor.execute("INSERT INTO users (name) VALUES ('Ivan')")
# Предположим, следующая команда вызовет IntegrityError, так как id уже существует?
cursor.execute("INSERT INTO users (id, name) VALUES (1, 'Dup')") # ошибка, если id=1 уже есть
conn.commit()
except sqlite3.DatabaseError as e:
print("Ошибка:", e)
conn.rollback()
finally:
conn.close()
Если записи с id=1 нет, то вставка пройдет. При ошибке транзакция откатывается, первая вставка тоже отменяется.
Пример 7: Подключение к базе данных в памяти с множеством таблиц
import sqlite3
conn = sqlite3.connect(':memory:')
cursor = conn.cursor()
cursor.execute("CREATE TABLE employees (id INTEGER PRIMARY KEY, name TEXT, salary REAL)")
cursor.execute("CREATE TABLE departments (id INTEGER PRIMARY KEY, name TEXT)")
cursor.executemany("INSERT INTO employees (name, salary) VALUES (?, ?)",
[('John', 50000), ('Jane', 60000)])
cursor.executemany("INSERT INTO departments (name) VALUES (?)",
[('HR',), ('IT',)])
# INNER JOIN
cursor.execute('''SELECT e.name, d.name
FROM employees e
CROSS JOIN departments d''')
for row in cursor.fetchall():
print(row)
conn.close()
('John', 'HR')
('John', 'IT')
('Jane', 'HR')
('Jane', 'IT')
Пример 8: Работа с NULL-значениями и IS NULL
import sqlite3
with sqlite3.connect(':memory:') as conn:
conn.execute("CREATE TABLE test (value INTEGER)")
conn.execute("INSERT INTO test VALUES (10)")
conn.execute("INSERT INTO test VALUES (NULL)")
cursor = conn.execute("SELECT * FROM test WHERE value IS NULL")
print(cursor.fetchall()) # [(None,)]
[(None,)]
Пример 9: Использование встроенной функции sqlite3 для дампа базы данных
import sqlite3
with sqlite3.connect('example.db') as conn:
# Получить SQL-скрипт для восстановления БД
for line in conn.iterdump():
print(line)
BEGIN TRANSACTION; CREATE TABLE IF NOT EXISTS users (...); ... COMMIT;
Пример 10: Параллельное чтение из нескольких соединений (WAL режим)
import sqlite3
# Включение WAL (Write-Ahead Log)
conn = sqlite3.connect('example.db')
conn.execute("PRAGMA journal_mode=WAL;")
conn.close()
# Теперь несколько соединений могут одновременно читать
conn1 = sqlite3.connect('example.db')
conn2 = sqlite3.connect('example.db')
c1 = conn1.execute("SELECT * FROM users")
c2 = conn2.execute("SELECT * FROM users")
print(c1.fetchone())
print(c2.fetchone())
conn1.close()
conn2.close()
(1, 'Alice', 30) (1, 'Alice', 30)
WAL позволяет конкурентное чтение без блокировок.