
Использование переменных в SQL запросах на Python требует строгого контроля типов и экранирования данных. В `sqlite3` плейсхолдеры обозначаются знаком ?, а значения передаются кортежем: cursor.execute(«SELECT * FROM products WHERE category = ?», (category,)). Такой подход предотвращает SQL-инъекции и автоматически преобразует типы данных.
В `psycopg2` для PostgreSQL используются плейсхолдеры %s, независимо от типа переменной. Пример: cursor.execute(«INSERT INTO orders (user_id, total) VALUES (%s, %s)», (user_id, total)). Это гарантирует корректное экранирование строк и чисел и упрощает повторное использование кода с разными значениями.
Для массовой вставки данных применяются методы executemany или подготовленные выражения. В `sqlite3` cursor.executemany(«INSERT INTO logs (event, created_at) VALUES (?, ?)», log_list) позволяет вставлять тысячи записей за одну операцию, снижая накладные расходы на парсинг SQL и повышая производительность.
Правильная вставка переменных также облегчает отладку и поддержку кода. Важно избегать конкатенации строк и использования f-строк для подстановки данных – это повышает риск ошибок при обработке спецсимволов и может приводить к повреждению таблиц при некорректных значениях.
Использование параметров с библиотекой sqlite3
Библиотека sqlite3 поддерживает безопасную вставку переменных через параметры, предотвращая SQL-инъекции. Для передачи значений используют символы-заполнители ? или именованные параметры :имя. Пример с позиционными параметрами:
cursor.execute("INSERT INTO users (name, age) VALUES (?, ?)", (username, user_age))
Именованные параметры упрощают работу с большим количеством значений и повышают читаемость:
cursor.execute("UPDATE users SET age = :age WHERE name = :name", {"name": username, "age": user_age})
Метод executemany() позволяет эффективно вставлять несколько записей за один вызов:
cursor.executemany("INSERT INTO users (name, age) VALUES (?, ?)", [(u1, a1), (u2, a2), (u3, a3)])
При использовании параметров sqlite3 автоматически экранирует специальные символы, включая кавычки и слэши. Это исключает необходимость ручной обработки строк перед вставкой в запрос.
Для SELECT-запросов параметризация работает аналогично. Пример с фильтром по имени:
cursor.execute("SELECT id, age FROM users WHERE name = ?", (username,))
Важно всегда передавать параметры как кортеж или словарь. Прямое форматирование строк через % или f"" создаёт уязвимость к SQL-инъекциям и не рекомендуется.
Использование параметров также ускоряет подготовку запроса: при повторных вызовах SQLite повторно компилирует план выполнения без пересборки SQL-кода, что экономит ресурсы при массовых операциях.
Передача значений через плейсхолдеры %s в psycopg2

В psycopg2 для безопасной передачи данных в SQL-запросы используется синтаксис плейсхолдеров %s. Вместо конкатенации строк передача значений через плейсхолдеры предотвращает SQL-инъекции и обеспечивает корректное экранирование.
Пример базового использования:
import psycopg2
conn = psycopg2.connect(dbname="testdb", user="user", password="pass", host="localhost")
cur = conn.cursor()
sql = "INSERT INTO employees (name, salary) VALUES (%s, %s)"
cur.execute(sql, ("Иван", 50000))
conn.commit()
cur.close()
conn.close()
Передаваемые значения должны быть упакованы в кортеж или список. Использование других типов, например строки, приведет к ошибке TypeError. Для одного значения также необходимо использовать запятую: (value,).
Для множественных вставок удобно применять executemany():
data = [("Мария", 60000), ("Петр", 55000)]
cur.executemany(sql, data)
Этот метод эффективно выполняет повторяющиеся запросы и уменьшает нагрузку на базу.
Плейсхолдеры %s работают независимо от типа данных: строки, числа, даты автоматически приводятся к корректному SQL-формату. Не нужно вручную добавлять кавычки или экранировать символы.
Важно: никогда не подставляйте значения через f-строки или конкатенацию. psycopg2 не интерпретирует %s как форматирование Python, а как метку для безопасной подстановки параметров.
Для сложных запросов с динамическими таблицами или столбцами плейсхолдеры применяются только к значениям, структура SQL должна оставаться статичной.
Форматирование строк с f-строками: когда это допустимо
Использование f-строк в Python позволяет вставлять переменные напрямую в строки. В контексте SQL это может быть безопасно только в строго ограниченных сценариях.
Допустимые случаи:
- Динамическое формирование имен таблиц или столбцов, если эти значения заранее проверены и не зависят от пользовательского ввода. Пример:
table_name = "users" query = f"SELECT * FROM {table_name}" - Логирование запросов для отладки, когда SQL не выполняется. F-строки здесь безопасны, так как не влияют на базу данных.
- Встроенные константы и параметры, которые никогда не поступают извне и не могут быть изменены пользователем.
Недопустимые случаи:
- Вставка пользовательского ввода напрямую через f-строки. Это создаёт уязвимость SQL-инъекций. Пример, которого следует избегать:
username = input("Введите имя: ") query = f"SELECT * FROM users WHERE username = '{username}'" - Любая динамическая генерация условий WHERE или VALUES без строгой валидации.
Рекомендации:
- Использовать f-строки только для статических и проверенных частей запроса.
- Для данных от пользователя применять параметризованные запросы через placeholders, например через
cursor.execute("SELECT * FROM users WHERE username = %s", (username,))в psycopg2. - При формировании имен таблиц или столбцов создавать белый список допустимых значений и проверять переменные перед вставкой в f-строку.
Защита от SQL-инъекций при вставке данных

SQL-инъекции возникают, когда пользовательский ввод напрямую вставляется в SQL-запрос. Основной способ защиты – использование параметризованных запросов.
Примеры безопасной вставки данных с библиотекой sqlite3:
import sqlite3
conn = sqlite3.connect('database.db')
cursor = conn.cursor()
username = "user_input"
password = "pass_input"
Параметризованный запрос
cursor.execute("INSERT INTO users (username, password) VALUES (?, ?)", (username, password))
conn.commit()
Для других библиотек синтаксис может отличаться:
- psycopg2 (PostgreSQL): используется %s как плейсхолдер:
cursor.execute("INSERT INTO users (username, password) VALUES (%s, %s)", (username, password)) - MySQL Connector: используется %s:
cursor.execute("INSERT INTO users (username, password) VALUES (%s, %s)", (username, password))
Рекомендации по защите:
- Никогда не формируйте SQL-запросы через конкатенацию строк или f-строки для пользовательских данных.
- Используйте только официальные API библиотек для параметризации.
- При работе с числовыми параметрами тоже применяйте плейсхолдеры, а не преобразование через
str(). - Валидация входных данных не заменяет параметризацию, но снижает риск логических ошибок.
- Регулярно обновляйте библиотеки, так как некоторые версии содержат уязвимости при обработке специальных символов.
Для массовой вставки данных применяйте executemany(), это безопаснее, чем многократная конкатенация запросов:
data = [("user1", "pass1"), ("user2", "pass2")]
cursor.executemany("INSERT INTO users (username, password) VALUES (?, ?)", data)
conn.commit()
Контроль прав доступа: аккаунт базы данных, выполняющий вставку, должен иметь минимальные привилегии, ограниченные только необходимыми таблицами.
Вставка нескольких записей через executemany

Метод executemany позволяет вставлять сразу несколько записей в таблицу, минимизируя количество отдельных запросов к базе данных и снижая нагрузку на соединение. Он особенно эффективен при работе с большими объемами данных.
Синтаксис следующий: cursor.executemany(sql, seq_of_params), где sql – подготовленный SQL-запрос с плейсхолдерами, а seq_of_params – список или кортеж кортежей с параметрами для каждой записи.
Пример вставки нескольких пользователей в таблицу users:
sql = "INSERT INTO users (name, email, age) VALUES (%s, %s, %s)"
data = [
('Иван', 'ivan@example.com', 28),
('Мария', 'maria@example.com', 34),
('Петр', 'petr@example.com', 22)
]
cursor.executemany(sql, data)
connection.commit()
Важно: используйте плейсхолдеры, специфичные для вашей СУБД (%s для MySQL, ? для SQLite), чтобы избежать SQL-инъекций. executemany автоматически обрабатывает каждый набор параметров, подставляя их корректно.
Для больших массивов данных рекомендуется разбивать seq_of_params на блоки по 500–1000 записей, чтобы снизить нагрузку на память и избежать ошибок таймаута соединения.
Метод executemany также поддерживает операции обновления и удаления, если SQL-запрос использует параметры. Это позволяет применять пакетные изменения к данным без написания циклов в Python.
Работа с NULL и пустыми значениями в параметрах
При работе с SQL-запросами на Python важно корректно обрабатывать NULL и пустые строки в параметрах. Прямое использование Python-значений `None` или пустых строк без обработки может привести к некорректным результатам запроса или ошибкам базы данных.
Для вставки NULL используйте `None` в параметризованных запросах. Например, с библиотекой sqlite3:
cursor.execute("INSERT INTO users (name, age) VALUES (?, ?)", (user_name, age if age is not None else None))
При фильтрации записей с возможным NULL нельзя использовать обычное сравнение `= NULL`. Вместо этого применяйте `IS NULL`:
cursor.execute("SELECT * FROM users WHERE age IS ? OR age IS NULL", (age,))
Для пустых строк используйте явную проверку и замену на `NULL`, если логика базы требует отсутствия значения:
param = user_input if user_input.strip() != "" else None
cursor.execute("INSERT INTO users (comment) VALUES (?)", (param,))
При обновлении данных также рекомендуется различать пустое значение и отсутствие значения, чтобы не перезаписать существующий NULL или содержимое случайно:
new_email = form_email if form_email else None
cursor.execute("UPDATE users SET email = ? WHERE id = ?", (new_email, user_id))
Использование параметров с `None` гарантирует корректную работу с NULL без прямого включения значений в строку запроса, что повышает безопасность и предотвращает SQL-инъекции.
Обработка специальных символов и кавычек

Вставка переменных напрямую в SQL-запросы без обработки специальных символов создаёт риск синтаксических ошибок и SQL-инъекций. Основные символы, требующие внимания: одинарная кавычка (‘), двойная кавычка («), обратная косая (\), символ процента (%) и подчёркивание (_), используемые в LIKE-паттернах.
Для безопасной вставки строковых значений рекомендуется использовать параметризованные запросы через placeholders:
| Библиотека | Синтаксис вставки |
|---|---|
| sqlite3 | cursor.execute(«SELECT * FROM users WHERE name = ?», (user_name,)) |
| psycopg2 | cursor.execute(«SELECT * FROM users WHERE name = %s», (user_name,)) |
| MySQL Connector | cursor.execute(«SELECT * FROM users WHERE name = %s», (user_name,)) |
При использовании LIKE необходимо экранировать символы % и _ с помощью обратного слэша, если они не должны интерпретироваться как шаблон:
search_term = user_input.replace("%", "\\%").replace("_", "\\_")
Для случаев, когда параметризованные запросы недоступны, можно использовать метод экранирования библиотеки:
| Библиотека | Метод экранирования |
|---|---|
| sqlite3 | sqlite3.Connection.execute(«INSERT INTO table (col) VALUES (?)», (value,)) |
| MySQL Connector | mysql.connector.connection.MySQLConnection.converter.escape(value) |
| psycopg2 | psycopg2.extensions.adapt(value).getquoted() |
Никогда не комбинируйте строки напрямую через конкатенацию или f-строки для SQL-запросов, если значения приходят от пользователя. Одинарные кавычки внутри строки можно безопасно экранировать двойной кавычкой в PostgreSQL или функцией escape в MySQL, но предпочтение всегда отдаётся параметрам.
Особое внимание следует уделять многострочным строкам и символам обратного слэша, так как они могут нарушить синтаксис запроса или изменить логику LIKE-паттернов. Всегда проверяйте результат экранирования перед выполнением.
Динамическая генерация условий WHERE с переменными

Для создания гибких SQL-запросов с условием WHERE в Python важно использовать параметризацию. Она предотвращает SQL-инъекции и упрощает работу с динамическими фильтрами. Например, при фильтрации по нескольким полям можно формировать список условий:
Пример:
«`python
conditions = []
params = []
if name:
conditions.append(«name = %s»)
params.append(name)
if age:
conditions.append(«age >= %s»)
params.append(age)
query = «SELECT * FROM users»
if conditions:
query += » WHERE » + » AND «.join(conditions)
cursor.execute(query, params)
«`
В данном примере conditions собирает только те фильтры, которые переданы, а params хранит значения переменных. Конкатенация через AND позволяет комбинировать любые условия без дублирования кода.
При работе с диапазонами дат или списками значений можно использовать конструкцию IN с плейсхолдерами:
«`python
ids = [1, 2, 5]
placeholders = «, «.join([«%s»] * len(ids))
query = f»SELECT * FROM orders WHERE id IN ({placeholders})»
cursor.execute(query, ids)
«`
Для сложных условий удобно вынести генерацию в функцию, которая возвращает условия и параметры. Это обеспечивает читаемость и повторное использование:
«`python
def build_filters(filters):
conds, vals = [], []
for key, val in filters.items():
if val is not None:
conds.append(f»{key} = %s»)
vals.append(val)
return conds, vals
conds, vals = build_filters({«name»: name, «age»: age})
query = «SELECT * FROM users»
if conds:
query += » WHERE » + » AND «.join(conds)
cursor.execute(query, vals)
«`
Главное правило – не использовать прямую вставку переменных через строковое форматирование. Всегда применять плейсхолдеры (%s, ?) и передавать значения отдельно. Это гарантирует безопасность и корректность запросов при динамической генерации условий.
Вопрос-ответ:
Почему нельзя просто конкатенировать строки при вставке переменных в SQL-запрос?
Если соединять строки напрямую, есть риск SQL-инъекций. Это означает, что злоумышленник может вставить вредоносный код в переменную и изменить логику запроса. Кроме того, такой способ может вызвать ошибки при работе с типами данных, например, при вставке строк с апострофами или кавычками.
Какие способы передачи переменных в SQL-запрос через Python считаются безопасными?
Наиболее безопасный метод — использование параметризованных запросов через библиотеки, такие как sqlite3 или psycopg2. Вместо подстановки значений через конкатенацию строк, параметры передаются отдельными аргументами. Библиотека самостоятельно экранирует данные, предотвращая SQL-инъекции и проблемы с форматированием.
Можно ли использовать переменные в SQL-запросах с разными типами данных, например числа и строки одновременно?
Да, параметры позволяют передавать значения разных типов без ручного преобразования. Например, в sqlite3 вы можете передать кортеж с числом и строкой, и библиотека корректно вставит их в запрос. Это избавляет от необходимости самостоятельно ставить кавычки для строк или конвертировать числа.
В чем отличие использования символа вопроса «?» и именованных параметров в SQL-запросах на Python?
Символ «?» применяется для позиционных параметров: значения передаются в виде кортежа и подставляются в том порядке, в котором указаны. Именованные параметры используют синтаксис вроде «:name», где каждому имени соответствует отдельное значение в словаре. Именованные параметры удобны, если нужно вставить одно значение несколько раз или сделать код более читаемым.
Что делать, если нужно вставить список значений в оператор IN через параметры?
Параметры напрямую не принимают списки в конструкции IN, поэтому приходится создавать строку с нужным количеством плейсхолдеров. Например, для списка из трёх элементов запрос будет иметь вид «WHERE id IN (?, ?, ?)», а значения передаются в кортеже. Такой способ сохраняет безопасность и работает с любым количеством элементов.
Почему нельзя просто подставлять переменные в SQL запрос через конкатенацию строк?
Подставлять значения напрямую через сложение строк опасно из-за риска SQL-инъекций. Злоумышленник может передать в переменной специальный текст, который изменит структуру запроса, что приведёт к утечке данных или нарушению работы базы. Чтобы избежать этого, используют параметры запроса, которые гарантируют правильное экранирование значений и безопасную передачу данных в базу.
Какие способы передачи переменных в SQL запросы на Python существуют и чем они отличаются?
В Python обычно применяются два подхода: через символы подстановки и через именованные параметры. При использовании символов подстановки (например, ? или %s в зависимости от драйвера) значения передаются в виде кортежа или списка, и библиотека сама подставляет их в запрос. При именованных параметрах используются словари с ключами, соответствующими плейсхолдерам в запросе. Оба метода защищают от некорректной интерпретации данных, но именованные параметры удобнее при работе с большим количеством переменных, так как повышают читаемость и снижают риск ошибок в порядке значений.
