Python MySQL UPDATE – Изменение строк в таблице
Как обновлять строки MySQL в Python с помощью mysql-connector-python: параметризованные запросы, массовые обновления, транзакции и обработка ошибок.
Изменение существующих записей — одна из самых распространённых операций с базами данных. Будь то исправление опечатки, обновление адреса клиента или пометка заказа как отправленного — SQL-оператор UPDATE решает эту задачу. В данной главе показано, как выполнять операторы UPDATE из Python с помощью mysql-connector-python: обновление одной строки, нескольких столбцов, массовые обновления через executemany(), паттерн контекстного менеджера и типичные ошибки, с которыми сталкиваются новички.
Предварительные требования
Перед запуском примеров необходимо следующее:
- Python 3.8 или выше
- Установленный
mysql-connector-python(pip install mysql-connector-python) - Работающий сервер MySQL (локальный или удалённый)
- База данных и таблица для работы (см. MySQL Get Started и MySQL Create Table)
В примерах ниже предполагается, что у вас есть база данных mydatabase с таблицей customers, созданной и заполненной в главе MySQL Insert.
Оператор UPDATE
SQL-оператор UPDATE изменяет значения одного или нескольких столбцов в строках, удовлетворяющих условию:
UPDATE table_name
SET column1 = value1, column2 = value2, ...
WHERE condition;Всегда включайте условие WHERE. Без него будут обновлены все строки таблицы. MySQL применяет изменения молча — никакого запроса на подтверждение нет.
Подключение к MySQL
Перед выполнением любого оператора необходимо открыть соединение и создать курсор. Курсор отправляет SQL на сервер и получает результаты.
import mysql.connector
from mysql.connector import Error
mydb = mysql.connector.connect(
host="localhost",
user="yourusername",
password="yourpassword",
database="mydatabase"
)
mycursor = mydb.cursor()Обновление одной строки
Самый надёжный способ обратиться к одной строке — параметризованный запрос. Значения передаются в виде кортежа Python — коннектор экранирует их перед отправкой запроса в MySQL, что предотвращает SQL-инъекцию.
import mysql.connector
from mysql.connector import Error
try:
mydb = mysql.connector.connect(
host="localhost",
user="yourusername",
password="yourpassword",
database="mydatabase"
)
mycursor = mydb.cursor()
sql = "UPDATE customers SET address = %s WHERE address = %s"
val = ("Park Lane 38", "Highway 37")
mycursor.execute(sql, val)
mydb.commit() # write the change to disk
print(mycursor.rowcount, "record(s) affected")
except Error as e:
print(f"Error: {e}")
mydb.rollback() # undo any partial changes on failure
finally:
if mydb.is_connected():
mycursor.close()
mydb.close()cursor.rowcount показывает, сколько строк было фактически изменено. Значение 0 означает, что условие WHERE не соответствует ни одной строке — запрос выполнился без ошибки, но ничего не было обновлено.
Зачем нужен commit()
mysql-connector-python по умолчанию открывает соединения с отключённым автофиксом. До вызова mydb.commit() обновление существует только внутри текущей транзакции и невидимо для других соединений. Если скрипт завершится аварийно до фиксации, изменение автоматически откатится. Явный вызов mydb.rollback() в блоке except делает намерение очевидным.
Обновление нескольких столбцов одновременно
Разделяйте присвоения столбцов запятыми внутри условия SET, чтобы изменить несколько полей в одном операторе:
import mysql.connector
from mysql.connector import Error
try:
mydb = mysql.connector.connect(
host="localhost",
user="yourusername",
password="yourpassword",
database="mydatabase"
)
mycursor = mydb.cursor()
sql = "UPDATE customers SET name = %s, address = %s WHERE id = %s"
val = ("John Smith", "Main Street 10", 1)
mycursor.execute(sql, val)
mydb.commit()
print(mycursor.rowcount, "record(s) affected")
except Error as e:
print(f"Error: {e}")
mydb.rollback()
finally:
if mydb.is_connected():
mycursor.close()
mydb.close()Использование первичного ключа (id) в условии WHERE — самый надёжный способ обратиться ровно к одной строке.
Обновление нескольких строк одним оператором
Чтобы применить одно и то же изменение ко всем строкам, соответствующим условию, расширьте условие WHERE. Одного вызова execute() достаточно:
import mysql.connector
from mysql.connector import Error
try:
mydb = mysql.connector.connect(
host="localhost",
user="yourusername",
password="yourpassword",
database="mydatabase"
)
mycursor = mydb.cursor()
# Change the city to "Oslo" for every customer on "Park Lane"
sql = "UPDATE customers SET address = %s WHERE address LIKE %s"
val = ("Oslo 1", "%Park Lane%")
mycursor.execute(sql, val)
mydb.commit()
print(mycursor.rowcount, "record(s) affected")
except Error as e:
print(f"Error: {e}")
mydb.rollback()
finally:
if mydb.is_connected():
mycursor.close()
mydb.close()Обновление списка строк с помощью executemany()
Когда нужно обновить список строк с разными значениями для каждой, используйте executemany(). Он выполняет один и тот же параметризованный оператор по одному разу для каждого элемента за один обход сети, удерживая всё в рамках одной транзакции:
import mysql.connector
from mysql.connector import Error
# Each tuple: (new_address, customer_id)
updates = [
("Sunset Blvd 1", 3),
("Baker Street 2", 7),
("Abbey Road 3", 11),
]
try:
mydb = mysql.connector.connect(
host="localhost",
user="yourusername",
password="yourpassword",
database="mydatabase"
)
mycursor = mydb.cursor()
sql = "UPDATE customers SET address = %s WHERE id = %s"
mycursor.executemany(sql, updates)
mydb.commit()
print(mycursor.rowcount, "record(s) affected")
except Error as e:
print(f"Error: {e}")
mydb.rollback()
finally:
if mydb.is_connected():
mycursor.close()
mydb.close()Поскольку executemany() оборачивает все обновления в одну транзакцию, либо все они выполнятся успешно, либо ни одно — частично обновлённых данных не останется.
Использование контекстного менеджера (рекомендуемый паттерн)
Открытие соединения внутри оператора with гарантирует его закрытие даже при возникновении исключения в середине работы. Это самый чистый паттерн для производственного кода:
import mysql.connector
from mysql.connector import Error
db_config = {
"host": "localhost",
"user": "yourusername",
"password": "yourpassword",
"database": "mydatabase",
}
try:
with mysql.connector.connect(**db_config) as mydb:
with mydb.cursor() as mycursor:
sql = "UPDATE customers SET address = %s WHERE name = %s"
mycursor.execute(sql, ("New Road 5", "Alice"))
mydb.commit()
print(mycursor.rowcount, "record(s) affected")
except Error as e:
print(f"Error: {e}")Блок with закрывает и курсор, и соединение при выходе из блока — независимо от того, возникло ли исключение.
Предварительный просмотр изменений перед обновлением (сухой прогон)
Перед выполнением обновления, затрагивающего много строк, сначала запустите SELECT с тем же условием WHERE. Такой сухой прогон покажет ровно те строки, которые будут изменены:
import mysql.connector
from mysql.connector import Error
try:
mydb = mysql.connector.connect(
host="localhost",
user="yourusername",
password="yourpassword",
database="mydatabase"
)
mycursor = mydb.cursor()
condition = "%Highway%"
# Dry run: see which rows would be affected
mycursor.execute(
"SELECT id, name, address FROM customers WHERE address LIKE %s",
(condition,)
)
rows = mycursor.fetchall()
print(f"{len(rows)} row(s) would be updated:")
for row in rows:
print(row)
# Proceed only if rows were found
if rows:
mycursor.execute(
"UPDATE customers SET address = %s WHERE address LIKE %s",
("New Highway 1", condition)
)
mydb.commit()
print(f"{mycursor.rowcount} row(s) updated")
except Error as e:
print(f"Error: {e}")
mydb.rollback()
finally:
if mydb.is_connected():
mycursor.close()
mydb.close()Распространённые ошибки
Отсутствие условия WHERE
# DANGER: updates every row in the table
mycursor.execute("UPDATE customers SET address = %s", ("Wrong Street 1",))
mydb.commit()Всегда проверяйте наличие и правильность условия WHERE перед вызовом commit().
Передача строки вместо кортежа
# Wrong — iterates over characters of the string
mycursor.execute("UPDATE customers SET address = %s WHERE name = %s", "AliceMain Street")
# Correct — wrap values in a tuple
mycursor.execute("UPDATE customers SET address = %s WHERE name = %s", ("Main Street", "Alice"))Коннектор ожидает последовательность (кортеж или список) в качестве второго аргумента. Передача обычной строки заставляет его перебирать отдельные символы, что приводит к запутанному ProgrammingError.
Забытый commit()
mycursor.execute("UPDATE customers SET address = %s WHERE name = %s", ("Main Street", "Alice"))
# Missing mydb.commit() — the update is silently rolled back when the connection closesВызывайте mydb.commit() после каждой операции записи или установите autocommit=True для соединения, если хотите, чтобы каждый оператор фиксировался немедленно.
Форматирование строк вместо параметризованных запросов
# UNSAFE — vulnerable to SQL injection
name = input("Enter name: ")
sql = f"UPDATE customers SET address = 'New Road' WHERE name = '{name}'"
mycursor.execute(sql)
# Safe — always use %s placeholders
sql = "UPDATE customers SET address = %s WHERE name = %s"
mycursor.execute(sql, ("New Road", name))Никогда не собирайте SQL-строки через f-строки или конкатенацию + с данными, введёнными пользователем.
Лучшие практики
- Используйте параметризованные запросы (заполнители
%s) всегда. Это самое важное правило — оно предотвращает SQL-инъекцию. - Всегда включайте условие
WHEREи проверяйте его перед фиксацией. Отсутствующее условиеWHEREобновит каждую строку таблицы. - Явно фиксируйте или откатывайте транзакцию. Полагаться на закрытие соединения для отката — плохая практика. Явно определяйте исход в блоке
except. - Проверяйте
rowcountпосле обновления, чтобы убедиться, что изменено ожидаемое количество строк. - Используйте
executemany()для пакетных обновлений вместо цикла сexecute(). Это быстрее и помещает все обновления в одну транзакцию. - Добавляйте индексы на столбцы из условия
WHERE. Обновление, требующее полного сканирования таблицы, медленно на больших наборах данных. Индексируйте столбцы, по которым фильтруете. - Используйте транзакции для многоэтапных обновлений. Если вы обновляете связанные строки в нескольких таблицах (например, заказ и его позиции одновременно), оберните все операторы в одну транзакцию, чтобы данные никогда не оказались в несогласованном состоянии.
Связанные главы
- MySQL Get Started — установка коннектора и создание первого соединения
- MySQL Create Table — создание таблиц, которые вы будете обновлять
- MySQL Insert — добавление строк перед практикой их изменения
- MySQL Where — освоение условия
WHERE, используемого в каждом оператореUPDATE - MySQL Select — чтение строк после обновления для проверки результата
- MySQL Delete — удаление строк вместо их изменения
- MySQL Order By — сортировка результатов при предварительном просмотре строк перед обновлением