W3docs

MySQL Limit

Узнайте, как использовать MySQL LIMIT в Python для ограничения результатов, реализации пагинации и работы с большими наборами данных.

Оператор LIMIT управляет количеством строк, которые возвращает запрос SELECT. При работе с большими таблицами — тысячами или миллионами строк — загрузка всех записей сразу расходует память и замедляет приложение. LIMIT позволяет получать только те записи, которые действительно нужны.

В этой главе показано, как использовать LIMIT в Python с пакетом mysql-connector-python, включая пагинацию с OFFSET, совместное использование LIMIT с ORDER BY и рекомендации по обработке ошибок.

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

Перед запуском примеров убедитесь, что MySQL connector установлен:

pip install mysql-connector-python

Во всех примерах предполагается, что в базе данных mydatabase существует таблица customers. Создать её можно, следуя главе Python MySQL Create Table.

Как работает оператор LIMIT

Базовый синтаксис ограничивает результирующий набор фиксированным количеством строк:

SELECT * FROM table_name LIMIT n;

Чтобы пропустить несколько строк перед началом результирующего набора, добавьте OFFSET:

SELECT * FROM table_name LIMIT n OFFSET m;

OFFSET m означает «пропустить первые m строк». Это основа постраничной навигации по большим наборам данных.

Получение фиксированного количества строк

Пример ниже подключается к базе данных MySQL и извлекает только первых пять клиентов:

Получение первых 5 строк с помощью LIMIT

import mysql.connector
from mysql.connector import Error

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

    mycursor = mydb.cursor()
    mycursor.execute("SELECT * FROM customers LIMIT 5")

    results = mycursor.fetchall()

    for row in results:
        print(row)

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

Метод fetchall() возвращает список кортежей — по одному кортежу на каждую строку. Поскольку запрос уже ограничивает результаты пятью строками, список будет содержать не более пяти элементов независимо от общего числа строк в таблице.

Использование LIMIT с OFFSET для пагинации

OFFSET смещает начальную строку результата. В сочетании с LIMIT это позволяет реализовать пагинацию: страница 1 показывает строки 1–10, страница 2 — строки 11–20 и так далее.

Формула: OFFSET = (page_number - 1) * page_size.

Получение 2-й страницы результатов (строки 11–20)

import mysql.connector
from mysql.connector import Error

page_number = 2
page_size = 10
offset = (page_number - 1) * page_size  # evaluates to 10

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

    mycursor = mydb.cursor()

    sql = "SELECT * FROM customers LIMIT %s OFFSET %s"
    mycursor.execute(sql, (page_size, offset))

    results = mycursor.fetchall()

    for row in results:
        print(row)

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

Заполнители %s подставляются драйвером connector, который безопасно обрабатывает значения и предотвращает SQL-инъекции. Никогда не формируйте значения LIMIT или OFFSET путём конкатенации строк непосредственно в запросе.

Совместное использование LIMIT с ORDER BY

Без ORDER BY база данных может возвращать строки в произвольном порядке. При использовании LIMIT почти всегда нужен предсказуемый порядок — иначе страница 1 и страница 2 могут содержать перекрывающиеся или случайные строки.

Получение 5 последних добавленных клиентов

import mysql.connector
from mysql.connector import Error

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

    mycursor = mydb.cursor()

    # Sort by id descending so the newest rows come first, then take 5
    mycursor.execute("SELECT * FROM customers ORDER BY id DESC LIMIT 5")

    results = mycursor.fetchall()

    for row in results:
        print(row)

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

В MySQL порядок выполнения: FROMWHEREORDER BYLIMIT, поэтому LIMIT всегда применяется к отсортированному результирующему набору.

Получение одной строки с fetchone()

Когда заранее известно, что нужна только одна строка — например, при поиске записи по первичному ключу — можно ограничить запрос одной строкой и использовать fetchone() вместо fetchall(). Это позволяет избежать выделения памяти под список, который не будет использован:

Получение одной строки

import mysql.connector
from mysql.connector import Error

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

    mycursor = mydb.cursor()
    mycursor.execute("SELECT * FROM customers LIMIT 1")

    row = mycursor.fetchone()

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

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

fetchone() возвращает один кортеж или None, если результирующий набор пуст.

Создание многократно используемой функции пагинации

В реальных приложениях удобно обернуть логику пагинации в функцию, чтобы вызывающий код оставался чистым:

Вспомогательная функция paginate()

import mysql.connector
from mysql.connector import Error

def paginate(cursor, table, page, page_size=10):
    """Return one page of rows from *table*.

    Args:
        cursor:    An active mysql.connector cursor.
        table:     Name of the table to query (string).
        page:      1-based page number.
        page_size: Number of rows per page (default 10).

    Returns:
        A list of row tuples, or an empty list when no rows remain.
    """
    offset = (page - 1) * page_size
    # Table names cannot be parameterised, so validate the name first.
    if not table.isidentifier():
        raise ValueError(f"Invalid table name: {table!r}")
    sql = f"SELECT * FROM `{table}` LIMIT %s OFFSET %s"
    cursor.execute(sql, (page_size, offset))
    return cursor.fetchall()


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

    # Print the first three pages
    for page in range(1, 4):
        rows = paginate(mycursor, "customers", page=page, page_size=5)
        if not rows:
            print(f"Page {page}: no more rows.")
            break
        print(f"--- Page {page} ---")
        for row in rows:
            print(row)

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

Обратите внимание, что имена таблиц нельзя передавать как параметры %s — connector не поддерживает параметризацию идентификаторов. Проверка isidentifier() защищает от инъекций через аргумент с именем таблицы.

Распространённые ошибки

LIMIT без ORDER BY даёт непредсказуемые результаты. MySQL не гарантирует стабильный порядок строк, если не указан ORDER BY. Два одинаковых запроса могут вернуть разные строки, если таблица была изменена между вызовами.

Большие значения OFFSET работают медленно. LIMIT 10 OFFSET 1000000 всё равно заставляет MySQL просканировать и отбросить миллион строк, прежде чем вернуть десять. Для глубокой пагинации в очень больших таблицах рассмотрите keyset-пагинацию (также называемую cursor-based pagination): сохраняйте последний просмотренный id и используйте WHERE id > last_id LIMIT 10.

Не формируйте значения LIMIT путём конкатенации строк. Используйте заполнители %s или, если значение вычисляется в Python, убедитесь, что оно является целым числом, прежде чем встраивать его в строку SQL.

Итоги

ЦельШаблон SQL
Первые N строкSELECT ... LIMIT N
Пропустить M строк, взять NSELECT ... LIMIT N OFFSET M
Страница P размером NLIMIT N OFFSET (P-1)*N
Только одна строкаSELECT ... LIMIT 1 + fetchone()
Первые N в нужном порядкеSELECT ... ORDER BY col LIMIT N

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

Was this page helpful?