Урок 80. MySQL и Python

📁 Блок: Базы данных ⏱️ Время изучения: ~90 мин 🎯 Сложность: Средняя
#close #for #in

⚡ Кратко: типичные ошибки

  • Забыть запятую в кортеже из одного значения: (value,).
  • Использовать f-string для SQL — это SQL-инъекция.
  • Не закрывать соединение и курсор.
  • Повторно вызывать fetchall() после исчерпания курсора.
  • Ловить голый Exception вместо конкретных ошибок MySQL.
  • Забывать commit() при INSERT/UPDATE/DELETE.

🐛 Типичные ошибки начинающих

1

Забыта запятая в кортеже из одного значения

Неправильно:

cursor.execute("SELECT * FROM employees WHERE id = %s", (100))  # целое число, не кортеж!

Правильно:

cursor.execute("SELECT * FROM employees WHERE id = %s", (100,))  # кортеж из одного элемента

Почему: (100) — это просто число 100 в скобках, а (100,) — кортеж. execute() ожидает последовательность параметров.

2

SQL-инъекция через f-string

Неправильно:

user_input = input("Введите ID: ")
cursor.execute(f"SELECT * FROM employees WHERE id = {user_input}")

Правильно:

user_input = input("Введите ID: ")
cursor.execute("SELECT * FROM employees WHERE id = %s", (user_input,))

Почему: пользователь может ввести 1 OR 1=1 и получить все строки таблицы.

3

Не закрыто соединение

Неправильно:

connection = pymysql.connect(**config)
cursor = connection.cursor()
cursor.execute("SELECT * FROM departments")
# соединение так и осталось открытым

Правильно:

with pymysql.connect(**config) as connection:
    with connection.cursor() as cursor:
        cursor.execute("SELECT * FROM departments")
        for row in cursor:
            print(row)

Почему: открытые соединения расходуют ресурсы сервера и могут привести к исчерпанию лимита подключений.

4

Вызов fetch после исчерпания курсора

Неправильно:

cursor.execute("SELECT * FROM departments")
all_rows = cursor.fetchall()
print(cursor.fetchone())  # None, ожидалась первая строка

Правильно:

cursor.execute("SELECT * FROM departments")
all_rows = cursor.fetchall()
print(all_rows[0])  # берём первую строку из уже полученных данных

Почему: курсор — однонаправленный итератор. После fetchall() читать больше нечего.

5

Ловля голого Exception

Неправильно:

try:
    connection = pymysql.connect(**config)
except Exception as e:
    print("Ошибка:", e)

Правильно:

try:
    connection = pymysql.connect(**config)
except pymysql.MySQLError as e:
    print("Ошибка MySQL:", e)

Почему: конкретное исключение помогает отделить ошибки базы данных от других проблем в коде.

6

Забытые параметры в execute()

Неправильно:

name = "IT"
cursor.execute("SELECT * FROM departments WHERE department_name = %s")  # нет второго аргумента

Правильно:

name = "IT"
cursor.execute("SELECT * FROM departments WHERE department_name = %s", (name,))

Почему: плейсхолдеры %s должны получить значения вторым аргументом execute().

7

Отсутствие commit() для изменяющих запросов

Неправильно:

cursor.execute("INSERT INTO departments (department_name) VALUES (%s)", ("IT",))
# изменения не сохранятся в базе!

Правильно:

cursor.execute("INSERT INTO departments (department_name) VALUES (%s)", ("IT",))
connection.commit()

Почему: PyMySQL по умолчанию работает в режиме транзакций. Без commit() изменения остаются временными.