W3docs

MySQL SELECT в Python

Как использовать SELECT в Python с mysql-connector-python: получение всех строк, одной строки, отдельных столбцов, fetchone и fetchall.

Оператор SELECT лежит в основе любой операции чтения из базы данных. В Python он выполняется через объект cursor, предоставляемый библиотекой mysql-connector-python, а результаты извлекаются с помощью fetchall(), fetchone() или fetchmany(). В этой главе рассматриваются все три метода получения данных, выборка конкретных столбцов, работа с именами столбцов и паттерны обработки ошибок, необходимые в production-коде.

Предварительные требования

Установите MySQL-коннектор, если ещё не сделали этого:

pip install mysql-connector-python

Во всех примерах предполагается:

  • Работающий MySQL-сервер (локальный или удалённый).
  • База данных mydatabase с таблицей customers, содержащей как минимум столбцы id, name и address.

Следуйте инструкциям из Python MySQL Create Table, чтобы создать таблицу, и Python MySQL Insert, чтобы заполнить её тестовыми данными перед запуском примеров SELECT ниже.

Подключение к базе данных

Каждый пример начинается с объекта подключения. Чтобы не повторять настройку в каждом фрагменте кода, разместите её в одном месте и используйте повторно:

import mysql.connector
from mysql.connector import Error

connection = mysql.connector.connect(
    host="localhost",
    user="yourusername",
    password="yourpassword",
    database="mydatabase"
)

Замените yourusername и yourpassword на ваши реальные учётные данные. Смотрите Python MySQL Get Started, чтобы узнать, как хранить учётные данные в переменных окружения вместо жёсткого кодирования.

Выборка всех строк с fetchall()

fetchall() извлекает все строки, возвращённые запросом, и сохраняет их в виде Python-списка кортежей. Используйте его, когда результирующий набор достаточно мал, чтобы без труда поместиться в памяти.

Выборка всех записей из таблицы customers

import mysql.connector
from mysql.connector import Error

try:
    connection = mysql.connector.connect(
        host="localhost",
        user="yourusername",
        password="yourpassword",
        database="mydatabase"
    )
    cursor = connection.cursor()
    cursor.execute("SELECT * FROM customers")

    rows = cursor.fetchall()

    for row in rows:
        print(row)

except Error as e:
    print(f"Error: {e}")
finally:
    if cursor:
        cursor.close()
    if connection.is_connected():
        connection.close()

Каждая row — это кортеж, значения которого соответствуют столбцам таблицы в том порядке, в котором они указаны в операторе CREATE TABLE. Для таблицы customers со столбцами (id, name, address) вывод будет примерно следующим:

(1, 'John', '123 Main St')
(2, 'Susan', '456 Oak Ave')
(3, 'Maria', '789 Pine Rd')

Зачем оборачивать всё в try/except/finally?

Если между открытием и закрытием соединения возникает исключение, соединение «утекает» — оно остаётся открытым и потребляет ресурсы сервера до тех пор, пока сервер не закроет его по таймауту. Блок finally гарантирует, что cursor.close() и connection.close() будут вызваны всегда, даже если что-то пошло не так.

Выборка одной строки с fetchone()

Когда нужна только первая подходящая строка — или вы знаете, что запрос возвращает ровно один результат — fetchone() эффективнее, чем fetchall(). Метод возвращает один кортеж или None, если результатов нет.

Получение одной строки из таблицы customers

import mysql.connector
from mysql.connector import Error

try:
    connection = mysql.connector.connect(
        host="localhost",
        user="yourusername",
        password="yourpassword",
        database="mydatabase"
    )
    cursor = connection.cursor()
    cursor.execute("SELECT * FROM customers")

    row = cursor.fetchone()

    if row:
        print("First customer:", row)
    else:
        print("No records found.")

except Error as e:
    print(f"Error: {e}")
finally:
    if cursor:
        cursor.close()
    if connection.is_connected():
        connection.close()

fetchone() перемещает внутренний указатель курсора вперёд. При повторном вызове возвращается следующая строка. Это делает метод удобным для последовательного перебора результатов по одной строке без загрузки всего результирующего набора в память.

Выборка конкретных столбцов

SELECT * возвращает все столбцы таблицы. Для больших таблиц или когда нужны лишь несколько полей, укажите столбцы явно. Это уменьшает объём данных, передаваемых по сети, и делает намерение кода очевидным.

Выборка только столбцов name и address

import mysql.connector
from mysql.connector import Error

try:
    connection = mysql.connector.connect(
        host="localhost",
        user="yourusername",
        password="yourpassword",
        database="mydatabase"
    )
    cursor = connection.cursor()
    cursor.execute("SELECT name, address FROM customers")

    rows = cursor.fetchall()

    for row in rows:
        print(row)

except Error as e:
    print(f"Error: {e}")
finally:
    if cursor:
        cursor.close()
    if connection.is_connected():
        connection.close()

Вывод (пример):

('John', '123 Main St')
('Susan', '456 Oak Ave')
('Maria', '789 Pine Rd')

Получение строк пакетами с fetchmany()

fetchmany(size) извлекает size строк за раз. Используйте его, когда результирующий набор слишком велик для fetchall(), но вы всё равно хотите обрабатывать строки пакетами, а не по одной.

Обработка результатов по 10 строк за раз

import mysql.connector
from mysql.connector import Error

try:
    connection = mysql.connector.connect(
        host="localhost",
        user="yourusername",
        password="yourpassword",
        database="mydatabase"
    )
    cursor = connection.cursor()
    cursor.execute("SELECT * FROM customers")

    batch_size = 10
    while True:
        rows = cursor.fetchmany(batch_size)
        if not rows:
            break
        for row in rows:
            print(row)

except Error as e:
    print(f"Error: {e}")
finally:
    if cursor:
        cursor.close()
    if connection.is_connected():
        connection.close()

fetchmany() возвращает пустой список, когда строк больше нет — это именно то, что проверяет условие if not rows: break.

Доступ к столбцам по имени с помощью словарного курсора

По умолчанию каждая строка — это обычный кортеж. При большом количестве столбцов обращение через row[4] вместо row["email"] делает код труднее для чтения и тихо ломается при изменении порядка столбцов. Передайте dictionary=True в конструктор курсора, чтобы получать каждую строку в виде dict.

Использование словарного курсора для доступа к столбцам по имени

import mysql.connector
from mysql.connector import Error

try:
    connection = mysql.connector.connect(
        host="localhost",
        user="yourusername",
        password="yourpassword",
        database="mydatabase"
    )
    # dictionary=True makes each row a dict
    cursor = connection.cursor(dictionary=True)
    cursor.execute("SELECT * FROM customers")

    rows = cursor.fetchall()

    for row in rows:
        print(f"ID: {row['id']}, Name: {row['name']}, Address: {row['address']}")

except Error as e:
    print(f"Error: {e}")
finally:
    if cursor:
        cursor.close()
    if connection.is_connected():
        connection.close()

Вывод:

ID: 1, Name: John, Address: 123 Main St
ID: 2, Name: Susan, Address: 456 Oak Ave

Словарные курсоры делают код самодокументируемым и устойчивым к изменению порядка столбцов.

Фильтрация строк с WHERE

Чтобы получить только строки, удовлетворяющие условию, добавьте предложение WHERE. Всегда используйте параметризованные запросы — никогда не подставляйте значения, введённые пользователем, непосредственно в SQL-строку, так как это открывает дверь для SQL-инъекций.

Получение клиентов, проживающих на определённой улице

import mysql.connector
from mysql.connector import Error

try:
    connection = mysql.connector.connect(
        host="localhost",
        user="yourusername",
        password="yourpassword",
        database="mydatabase"
    )
    cursor = connection.cursor()

    sql = "SELECT * FROM customers WHERE address = %s"
    val = ("123 Main St",)  # Always pass a tuple, even for a single value
    cursor.execute(sql, val)

    rows = cursor.fetchall()

    for row in rows:
        print(row)

except Error as e:
    print(f"Error: {e}")
finally:
    if cursor:
        cursor.close()
    if connection.is_connected():
        connection.close()

Заполнитель %s подставляется драйвером коннектора, который безопасно экранирует значение. Смотрите Python MySQL Where для подробного описания фильтрации, включая несколько условий с AND/OR.

Использование SELECT с ORDER BY и LIMIT

Сочетайте SELECT с ORDER BY для сортировки результатов и LIMIT для ограничения количества возвращаемых строк. Этот паттерн незаменим для отображения последних записей или реализации пагинации.

Получение 3 клиентов с наибольшими ID, отсортированных от новых к старым

import mysql.connector
from mysql.connector import Error

try:
    connection = mysql.connector.connect(
        host="localhost",
        user="yourusername",
        password="yourpassword",
        database="mydatabase"
    )
    cursor = connection.cursor()
    cursor.execute("SELECT * FROM customers ORDER BY id DESC LIMIT 3")

    rows = cursor.fetchall()

    for row in rows:
        print(row)

except Error as e:
    print(f"Error: {e}")
finally:
    if cursor:
        cursor.close()
    if connection.is_connected():
        connection.close()

Для отдельного руководства по сортировке смотрите Python MySQL Order By. По пагинации с OFFSETPython MySQL Limit.

Получение имён столбцов через cursor.description

После вызова execute() атрибут description курсора содержит метаданные о каждом столбце результата — включая его имя. Это полезно, когда имена столбцов нужны динамически (например, при формировании CSV-экспорта) без их жёсткого кодирования.

Вывод имён столбцов вместе с данными

import mysql.connector
from mysql.connector import Error

try:
    connection = mysql.connector.connect(
        host="localhost",
        user="yourusername",
        password="yourpassword",
        database="mydatabase"
    )
    cursor = connection.cursor()
    cursor.execute("SELECT * FROM customers")

    # cursor.description is a list of 7-item sequences; index 0 is the column name
    column_names = [desc[0] for desc in cursor.description]
    print("Columns:", column_names)

    rows = cursor.fetchall()
    for row in rows:
        print(dict(zip(column_names, row)))

except Error as e:
    print(f"Error: {e}")
finally:
    if cursor:
        cursor.close()
    if connection.is_connected():
        connection.close()

Вывод (пример):

Columns: ['id', 'name', 'address']
{'id': 1, 'name': 'John', 'address': '123 Main St'}
{'id': 2, 'name': 'Susan', 'address': '456 Oak Ave'}

Выбор между fetchall(), fetchone() и fetchmany()

МетодВозвращаетЛучше всего для
fetchall()Список всех кортежейНебольшие и средние результирующие наборы, когда все строки нужны сразу
fetchone()Один кортеж или NoneПоиск по первичному ключу; последовательный перебор строк
fetchmany(n)Список до n кортежейБольшие результирующие наборы, обрабатываемые потоковыми пакетами

Если вызвать fetchall() для набора из миллиона строк, Python одновременно сохранит все миллион кортежей в памяти. Для больших таблиц используйте fetchmany() или добавьте предложение LIMIT к запросу.

Распространённые ошибки и способы их исправления

ОшибкаВероятная причинаРешение
mysql.connector.errors.ProgrammingError: Table doesn't existНеверное имя таблицы или не выбрана база данныхПроверьте имя таблицы; убедитесь, что параметр database задан в connect()
mysql.connector.errors.InterfaceError: No result setВызов fetchall() после не-SELECT оператораВызывайте методы fetch только после SELECT, SHOW или других операторов, возвращающих результат
mysql.connector.errors.DatabaseError: Lost connectionТаймаут сети или перезапуск сервераПереустановите соединение; для долгоживущих приложений используйте пул соединений
InternalError: Unread result foundНачало нового execute() до получения предыдущих результатовВызовите fetchall() или cursor.reset(), чтобы очистить предыдущий результирующий набор

Резюме

  • Выполняйте оператор SELECT с помощью cursor.execute("SELECT ...").
  • Используйте fetchall() для небольших результирующих наборов, fetchone() для одной строки и fetchmany(n) для больших наборов данных, обрабатываемых пакетами.
  • Передавайте dictionary=True курсору, чтобы обращаться к столбцам по имени вместо индекса.
  • Всегда используйте параметризованные заполнители %s при фильтрации с пользовательскими значениями.
  • Оборачивайте каждую операцию с базой данных в try/except/finally, чтобы гарантировать закрытие соединения при возникновении ошибки.

Связанные главы

Was this page helpful?