SQLite3 и Python: настройка, запросы, ошибки и примеры использования

Раздел: Базы данных -> Базы данных в 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 происходит автоматически после выхода из with

Python библиотеки для баз данных (библиотеки 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 позволяет конкурентное чтение без блокировок.

Работа с SQLite3 в Python - comments

En
Python база данных sqlite3 (python)