Подключение, таблица и вставка данных: Connection, CREATE TABLE и INSERT
- Открывать соединение (Connection), создавать курсор (Cursor), выполнять проверочный запрос и читать результат через fetchone
- Понимать разницу между Connection и Cursor
- Всегда закрывать соединение — даже при ошибке — с помощью try/finally
- Собрать открытие соединения в одну функцию, которой пользуется весь проект
- Писать полную команду CREATE TABLE с типами, первичным ключом и ограничениями
- Использовать IF NOT EXISTS, чтобы создание таблицы было безопасно запускать повторно
- Добавлять записи через INSERT INTO … (columns) VALUES (…)
- Распознавать ошибки нарушения ограничений (NOT NULL, UNIQUE) и понимать, почему они защищают данные
В этом уроке
Урок состоит из двух глав — в том же порядке, что и в презентации:
- Глава 1 — открытие соединения, проверка, что соединение работает, и правильное закрытие
- Глава 2 — базовый SQL: CREATE TABLE и INSERT
Глава 1: Открытие соединения, проверка, что оно работает, и правильное закрытие
Что мы изучим?
Жизненный цикл любой работы с базой данных:
1. open a connection → 2. create a cursor → 3. run a query → 4. read the result → 5. close- Соединение (Connection) и курсор (Cursor): что такое каждый из них.
- Проверочный запрос (Health Check): «база вообще отвечает?»
- Правильное закрытие, даже при ошибке.
get_connection()— одна функция, которой будет пользоваться весь проект.
Зачем это нужно?
Каждая функция CRUD и каждый Endpoint сервера (тема API студентов) начинается с открытия соединения и заканчивается его закрытием. Незакрытое соединение — классический баг: каждое соединение с MySQL занимает ресурсы — и у нас, и на сервере, — а у MySQL ограниченное число одновременных соединений. На сервере, который обрабатывает тысячи запросов, через какое-то время соединения заканчиваются (Too many connections), и всё застревает. Это ровно как открытые файлы в уроке о файлах, только в масштабе сервера.
Объяснение с основ
Connection — открытая линия
Соединение (Connection) = объект, который представляет открытый канал между нашим кодом и сервером MySQL. Через него создают курсор, через него подтверждают изменения (commit, позже в этой теме) и через него закрывают. Пока оно открыто, оно занимает ресурсы.
Cursor
Курсор (Cursor) = объект, который выполняет запрос и хранит его результаты и позволяет читать их строка за строкой. Представьте его как палец, который идёт по результатам запроса.
Его создают из соединения и выполняют через него:
cursor = connection.cursor() # create a cursor
cursor.execute("SELECT 1") # run a query through itЧтение результатов
| Метод курсора | Возвращает | Когда |
|---|---|---|
fetchone() | Одну строку (tuple) или None, если строки нет | Ожидается одна строка: проверка или поиск по id |
fetchall() | Список всех строк (список tuple) | Ожидается несколько строк |
Каждая строка — tuple (вспомните кортежи!): ("Dana", 24). (Позже в этой теме научимся получать строки в виде dict.)
Соединение = сам звонок: вы набрали номер, и линия открыта. Курсор = оператор, который ищет для вас информацию и зачитывает результат за результатом. fetchone = «скажите мне первый». fetchall = «зачитайте мне все». close = положить трубку. Если не положить трубку, линия остаётся занятой, и другие не могут дозвониться.
Проверочный запрос (Health Check)
Чтобы проверить, что соединение действительно работает (а не только что объект создан), выполняют простой запрос, который не зависит ни от одной таблицы:
SELECT 1
SELECT VERSION()Если ответ пришёл, база жива и отвечает. Именно так работает Endpoint /health настоящих серверов (тема бэкенда и FastAPI).
Правильное закрытие — try/finally
Что будет, если запрос упадёт после того, как мы открыли соединение?
connection = get_connection()
cursor = connection.cursor()
cursor.execute("SELEC 1") # typo -> exception
connection.close() # never reached!Ошибка останавливает функцию, и close не выполняется. Решение, которое мы изучили в теме исключений: finally, который выполняется всегда:
connection = get_connection()
try:
... # work with the database
finally:
connection.close() # runs even if an exception happenedСинтаксис
import mysql.connector
def get_connection():
return mysql.connector.connect(
host="localhost", user="root", password="kodkod", database="school")
connection = get_connection()
try:
cursor = connection.cursor() # create a cursor
cursor.execute("SELECT 1") # run a query
row = cursor.fetchone() # read one row -> (1,)
finally:
connection.close() # always closeПервый пример кода
week-4/school-db/check_connection.py:
import mysql.connector
connection = mysql.connector.connect(
host="localhost", user="root", password="kodkod", database="school")
try:
cursor = connection.cursor()
cursor.execute("SELECT VERSION()")
row = cursor.fetchone()
print("Row returned:", row)
print("MySQL version:", row[0])
cursor.execute("SELECT 1 + 1")
print("The database can calculate: 1 + 1 =", cursor.fetchone()[0])
print("Connection works!")
finally:
connection.close()
print("Connection closed.")Вывод:
Row returned: ('8.4.11',)
MySQL version: 8.4.11
The database can calculate: 1 + 1 = 2
Connection works!
Connection closed.Разбор кода построчно
| Код | Объяснение |
|---|---|
mysql.connector.connect(...) | Открытие. Вы получаете объект соединения (тема ООП: класс и объект!). |
try: | Отсюда любая ошибка всё равно дойдёт до finally. |
cursor = connection.cursor() | Создаёт курсор — через него выполняют запросы. |
cursor.execute("SELECT VERSION()") | Выполняет запрос. Результат хранится в курсоре. |
cursor.fetchone() | Одна строка: tuple с одним столбцом: ('8.4.11',). Запятая = tuple из одного элемента (вспомните кортежи!). |
row[0] | Первый столбец строки. |
cursor.fetchone()[0] | Первая строка → первый столбец. Распространено для запросов, которые возвращают одно значение. |
finally: connection.close() | Закрытие, всегда. Даже если один из запросов упадёт. |
Ещё один пример
Ошибка посередине и доказательство, что finally закрывает. А также: что происходит, когда пытаются использовать закрытое соединение.
import mysql.connector
def get_connection():
return mysql.connector.connect(
host="localhost", user="root", password="kodkod", database="school")
def run_check(sql):
connection = get_connection()
try:
cursor = connection.cursor()
cursor.execute(sql)
value = cursor.fetchone()[0]
print("OK:", sql, "->", value)
except mysql.connector.Error as error:
print("Query failed:", sql, "|", type(error).__name__ + ":", error)
finally:
connection.close()
print(" (connection closed)")
return connection
run_check("SELECT 40 + 2")
closed = run_check("SELEC 1")
try:
closed.cursor()
except mysql.connector.Error as error:
print("Using a closed connection:", error)Вывод:
OK: SELECT 40 + 2 -> 42
(connection closed)
Query failed: SELEC 1 | ProgrammingError: 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'SELEC 1' at line 1
(connection closed)
Using a closed connection: MySQL Connection not availableРазбор:
mysql.connector.Error— базовый класс всех ошибок драйвера (тема ООП: иерархия исключений!).ProgrammingError(ошибка в SQL),IntegrityError(нарушение ограничения, позже) иOperationalError(проблема с соединением) — его подклассы.- В запросе с опечаткой (
SELEC) ошибка была поймана, иfinallyвсё равно закрыл соединение. 1064— номер ошибки MySQL для синтаксической ошибки, аnear 'SELEC 1'говорит точно, где проблема. Это синтаксическая ошибка SQL (та же идея, что в Python, другой язык).- Использование соединения после
close→MySQL Connection not available. Соединение «мертво», и нужно открыть новое.
get_connection — одна точка подключения
В настоящем проекте соединение нужно каждой функции. Вместо того чтобы писать пять данных для подключения в десятках мест, пишут одну функцию (DRY). Это будет основа файла db.py во всех этих темах:
import mysql.connector
def get_connection():
return mysql.connector.connect(
host="localhost", user="root", password="kodkod", database="school")
def check_database():
connection = get_connection()
try:
cursor = connection.cursor()
cursor.execute("SELECT VERSION()")
return {"status": "ok", "mysql_version": cursor.fetchone()[0]}
except mysql.connector.Error as error:
return {"status": "error", "message": str(error)}
finally:
connection.close()
if __name__ == "__main__":
print(check_database()){'status': 'ok', 'mysql_version': '8.4.11'}check_database возвращает dict. В теме API студентов её можно подключить к Endpoint-у GET /health, и FastAPI превратит её результат в JSON.
Типичная ошибка
Ошибка 1: close без finally. Работает на «счастливом пути», но каждая ошибка посередине оставляет соединение открытым. На сервере после нескольких сотен неудачных запросов MySQL отказывает в новых соединениях: Too many connections.
Ошибка 2: забыть [0].
cursor.execute("SELECT VERSION()")
version = cursor.fetchone()
print("Version: " + version)TypeError: can only concatenate str (not "tuple") to strfetchone() возвращает строку (tuple), даже когда в ней один столбец. Само значение = [0].
Ошибка 3: fetchone(), когда результатов нет. fetchone() возвращает None, если строки нет, и тогда None[0] → TypeError: 'NoneType' object is not subscriptable. Позже в этой теме (поиск по id) мы всегда будем проверять if row is None.
Ошибка 4: новый запрос до того, как прочитаны все результаты.
cursor.execute("SELECT id FROM students") # 3 rows
first = cursor.fetchone() # read only the first one
cursor.execute("SELECT 1")mysql.connector.errors.InternalError: Unread result foundВ MySQL результаты приходят с сервера постепенно, и новый запрос не может уйти, пока предыдущий не прочитан до конца. Когда ожидаете несколько строк — fetchall(). Когда нужна одна строка — фильтруйте через WHERE, чтобы вернулась одна строка.
Итоги
- Цикл: открытие (
connect) → курсор (cursor()) → запрос (execute) → чтение (fetchone/fetchall) → закрытие (close). - Connection = канал к серверу базы данных. Cursor = выполняет запрос и хранит результаты.
fetchone()= одна строка (tuple) илиNone.fetchall()= список tuple. Одно значение:cursor.fetchone()[0].- Проверочный запрос:
SELECT 1/SELECT VERSION(). Основа для/health. - Всегда
try/finally+close(). - Одна
get_connection()на весь проект (DRY). Ошибки:mysql.connector.Errorи её подклассы.
Официальная документация
Глава 2: Базовый SQL — CREATE TABLE и INSERT
Что мы изучим?
Первые две команды SQL:
| Команда | Что делает | CRUD |
|---|---|---|
CREATE TABLE | Создаёт структуру: таблицу со столбцами | — (подготовка) |
INSERT INTO | Добавляет строку | Create |
В этом уроке мы сосредоточимся на самом SQL и будем выполнять его в Database Client в VS Code. В конце увидим, как выполнить тот же SQL из Python. (В следующих уроках научимся работать из Python правильно — параметры, commit и результаты.)
Зачем это нужно?
База school работает в контейнере — но она пуста. Таблица — это «ящик», где будут лежать данные, а INSERT — первое действие в любой системе: регистрация пользователя, добавление товара, оформление заказа. В API, который мы построим в теме API студентов, POST /students = INSERT INTO students.
Объяснение с основ
Структура команды SQL
- Ключевые слова вроде
CREATE,TABLE,INSERT,INTO,VALUESпо соглашению пишут заглавными буквами, чтобы отличать их от имён. (SQL не различает регистр ключевых слов, но так читается лучше.) - Имена таблиц и столбцов:
snake_case, строчными буквами, а для таблиц обычно во множественном числе (students). - Строки в SQL пишут в одинарных кавычках:
'Dana'. - Команда заканчивается
;(обязательно, когда выполняете несколько команд подряд, и всегда рекомендуется).
CREATE TABLE
CREATE TABLE students (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
age INT NOT NULL,
email VARCHAR(100) NOT NULL UNIQUE
);Каждая строка в скобках = столбец: <name> <TYPE> <constraints>, через запятую. (После последнего столбца запятой нет!)
IF NOT EXISTS
CREATE TABLE students падает, если таблица уже существует. На сервере, который каждый день запускается заново, нужна команда, которую безопасно выполнять повторно:
CREATE TABLE IF NOT EXISTS students (...);«Создай таблицу, только если её ещё нет». Если она есть, ничего не происходит, и ошибки нет. (Обратите внимание: если таблица существует с другой структурой, она не изменится.)
INSERT INTO
INSERT INTO students (name, age, email)
VALUES ('Dana', 24, 'dana@mail.com');INSERT INTO <table>— в какую таблицу.(name, age, email)— какие столбцы вы заполняете и в каком порядке.VALUES (...)— значения, точно в том же порядке.idнет в списке — его сам заполняетAUTO_INCREMENT.
Можно добавить несколько строк одной командой:
INSERT INTO students (name, age, email) VALUES
('Avi', 31, 'avi@mail.com'),
('Noa', 22, 'noa@mail.com');CREATE TABLE = напечатать пустую анкету: какие есть поля и какие из них обязательны. INSERT = кто-то заполняет одну анкету и сдаёт её. Канцелярия (база данных) сама выдаёт номер дела (id) и возвращает анкету, в которой не хватает обязательного поля.
Синтаксис
CREATE TABLE IF NOT EXISTS table_name (
id INT AUTO_INCREMENT PRIMARY KEY,
column_a VARCHAR(100) NOT NULL,
column_b INT NOT NULL,
column_c VARCHAR(100) UNIQUE
);
INSERT INTO table_name (column_a, column_b, column_c)
VALUES ('text value', 42, 'unique value');Как выполнять SQL в Database Client:
- Создайте новый файл с расширением
.sql, напримерschool.sql. - Вверху файла выберите соединение и базу
school(или: правый щелчок поschoolв дереве → Open Query). - Напишите команды, выделите их и нажмите Run (или
Ctrl+Enter). - В дереве сбоку нажмите Refresh — и таблица появится. Щелчок по ней открывает строки как электронную таблицу.
Первый пример кода
school.sql:
CREATE TABLE IF NOT EXISTS students (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
age INT NOT NULL,
email VARCHAR(100) NOT NULL UNIQUE
);
SHOW TABLES;Выполните в Database Client. Результат SHOW TABLES:
Tables_in_school
----------------
studentsТаблица существует! А в дереве сбоку появилась students с четырьмя столбцами.
Разбор кода построчно
| Строка | Объяснение |
|---|---|
CREATE TABLE IF NOT EXISTS students ( | Создать таблицу с именем students, если её ещё нет. |
id INT AUTO_INCREMENT PRIMARY KEY, | Идентификатор: целое число, которое база заполняет сама (1, 2, 3…), первичный ключ. MySQL не переиспользует номер удалённой строки. |
name VARCHAR(100) NOT NULL, | Текст до 100 символов, обязательный. |
age INT NOT NULL, | Целое число, обязательное. |
email VARCHAR(100) NOT NULL UNIQUE | Текст, обязательный, уникальный. Без запятой, потому что это последний столбец. |
); | Конец команды. |
SHOW TABLES; | Команда MySQL: какие таблицы есть в текущей базе. |
Выполните ту же команду снова: ошибки нет. Благодаря IF NOT EXISTS.
Ещё один пример
Добавляем строки и смотрим, что происходит при нарушении ограничений. Добавьте в school.sql и выполните:
INSERT INTO students (name, age, email) VALUES ('Dana', 24, 'dana@mail.com');
INSERT INTO students (name, age, email) VALUES
('Avi', 31, 'avi@mail.com'),
('Noa', 22, 'noa@mail.com');
SELECT * FROM students;id | name | age | email
---+------+-----+--------------
1 | Dana | 24 | dana@mail.com
2 | Avi | 31 | avi@mail.com
3 | Noa | 22 | noa@mail.comБаза сама выдала id: 1, 2, 3. (SELECT * = «покажи всё» — в следующем уроке изучим его подробно.)
Теперь нарушим ограничения — выполняйте каждую команду отдельно:
INSERT INTO students (name, age, email) VALUES ('Tom', 27, 'dana@mail.com');1062 (23000): Duplicate entry 'dana@mail.com' for key 'students.email'INSERT INTO students (name, email) VALUES ('Rina', 'rina@mail.com');1364 (HY000): Field 'age' doesn't have a default valueINSERT INTO students (name, age, email) VALUES ('Ron', 30);1136 (21S01): Column count doesn't match value count at row 1Мы запросили 3 столбца, но дали только 2 значения. Число значений должно совпадать с числом столбцов.
Разбор:
Duplicate entry ... for key 'students.email'— email Даны уже существует. База отказала, и строка не добавилась.Field 'age' doesn't have a default value— мы не заполнилиage, а он обязательный (NOT NULL, без значения по умолчанию).- Число в начале каждого сообщения (
1062,1364,1136) — код ошибки MySQL, по нему можно искать в интернете. - Из Python нарушение
UNIQUEприходит какIntegrityError(Integrity = целостность данных). Мы поймаем его позже в этой теме и будем возвращать 409 из API в теме API студентов. - Данные остались допустимыми. В этом сила ограничений: даже если в коде есть баг, база соблюдает правила.
Тот же SQL — из Python
На сервере мы не будем выполнять SQL вручную — его будет выполнять код. Вот как те же CREATE TABLE и INSERT выглядят из Python:
import mysql.connector
CREATE_STUDENTS = """
CREATE TABLE IF NOT EXISTS students (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
age INT NOT NULL,
email VARCHAR(100) NOT NULL UNIQUE
)
"""
connection = mysql.connector.connect(
host="localhost", user="root", password="kodkod", database="school")
try:
cursor = connection.cursor()
cursor.execute(CREATE_STUDENTS)
cursor.execute(
"INSERT INTO students (name, age, email) VALUES ('Tom', 27, 'tom@mail.com')")
connection.commit()
print("Inserted Tom")
finally:
connection.close()"""..."""— строка на несколько строк, удобна для длинного SQL.cursor.execute(...)выполняет одну команду за вызов.connection.commit()— сохраняет вставку в базе. Безcommitстрока исчезает при закрытии соединения. (В уроке «запросы из Python» расскажем подробнее.)
Типичная ошибка
Ошибка 1: двойные кавычки для строк.
INSERT INTO students (name, age, email) VALUES ("Dana", 24, "dana@mail.com");В стандартном SQL двойные кавычки — для имён (таблиц и столбцов), а одинарные — для строк. MySQL «прощает» и принимает это, но другие базы (PostgreSQL) выдадут ошибку. Для строк всегда '...'.
Ошибка 2: запятая после последнего столбца.
CREATE TABLE students (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
);1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ')' at line 4Ошибка 3: забыть список столбцов в INSERT.
INSERT INTO students VALUES ('Dana', 24, 'dana@mail.com');1136 (21S01): Column count doesn't match value count at row 1Без списка столбцов SQL ожидает значение для каждого столбца, включая id, и в порядке таблицы. Всегда пишите список столбцов явно. Это ещё и устойчиво к будущим изменениям таблицы.
Ошибка 4: изменить CREATE TABLE IF NOT EXISTS и ожидать, что таблица обновится. Вы добавили в команду столбец city и выполнили её: ничего не произошло, потому что таблица уже существует. На этапе обучения простое решение: удалить таблицу — DROP TABLE students; — и выполнить снова. (Это удалит и все строки! В настоящих системах используют миграции (Migrations) — инструменты, которые меняют структуру существующей таблицы без потери данных. Тема на будущее.)
Итоги
CREATE TABLE IF NOT EXISTS name (col TYPE constraints, ...);— создаёт структуру.IF NOT EXISTS= безопасно запускать повторно (но существующую структуру не обновляет).INSERT INTO name (cols) VALUES (...);— добавляет строку. Всегда со списком столбцов.idсAUTO_INCREMENTзаполняется сам.- Строки в SQL — в одинарных кавычках
'...'. После последнего столбца запятой нет. - Нарушение ограничения → ошибка с кодом (
1062— дубликат,1364— отсутствует обязательное поле). Строка не добавляется, и данные остаются допустимыми. - SQL выполняют в Database Client, а из Python — через
cursor.execute(...)иcommit().
Официальная документация
Практика
Упражнения опираются только на то, что пройдено в этом уроке и в предыдущих. Сначала попробуйте сами, и только потом открывайте решение.
Упражнение 1: Проверка здоровья (Health Check)
Напишите код, который подключается к базе school, выполняет два проверочных запроса — SELECT 1 + 1 и SELECT 'ok', — печатает результаты и в любом случае закрывает соединение.
Ожидаемый вывод:
The database computed: 2
Status: ok
Connection closedПоказать решение
import mysql.connector
connection = mysql.connector.connect(
host="localhost", user="root", password="kodkod", database="school")
try:
cursor = connection.cursor()
cursor.execute("SELECT 1 + 1")
two = cursor.fetchone()[0]
print("The database computed:", two)
cursor.execute("SELECT 'ok'")
status = cursor.fetchone()[0]
print("Status:", status)
finally:
connection.close()
print("Connection closed")The database computed: 2
Status: ok
Connection closedconnection.cursor()создаёт курсор, аcursor.execute(...)выполняет через него запрос.fetchone()возвращает одну строку в виде tuple, поэтому[0]— первый столбец.finallyгарантирует, что соединение закроется, даже если запрос упадёт.
Упражнение 2: Connection и Cursor
- В чём разница между Connection и Cursor?
- В чём разница между
fetchone()иfetchall()?
Показать решение
- Connection — открытое соединение с сервером MySQL: через него создают курсор, сохраняют (
commit) и закрывают. Cursor — объект, который выполняет запрос (execute) и хранит его результаты. fetchone()— одна строка (tuple) илиNone, если строки нет.fetchall()— список всех строк или пустой список.
Упражнение 3: CREATE TABLE IF NOT EXISTS
Напишите код на Python, который создаёт таблицу students (id, name, age, уникальный email) в базе school, только если её нет. Выполните создание дважды и покажите, что ошибки нет.
Ожидаемый вывод:
table ready
table readyПоказать решение
import mysql.connector
CREATE_STUDENTS = """
CREATE TABLE IF NOT EXISTS students (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
age INT NOT NULL,
email VARCHAR(100) NOT NULL UNIQUE
)
"""
for _ in range(2):
connection = mysql.connector.connect(
host="localhost", user="root", password="kodkod", database="school")
try:
cursor = connection.cursor()
cursor.execute(CREATE_STUDENTS)
print("table ready")
finally:
connection.close()table ready
table readyБез IF NOT EXISTS второй запуск упал бы: Table 'students' already exists. Строка в тройных кавычках (""") позволяет писать SQL на нескольких строках.
Упражнение 4: INSERT INTO
Продолжение: добавьте трёх студентов — Дану (24), Ави (31) и Ноа (22) — и сохраните. Затем посчитайте, сколько строк в таблице.
Ожидаемый вывод:
Rows: 3Показать решение
import mysql.connector
connection = mysql.connector.connect(
host="localhost", user="root", password="kodkod", database="school")
try:
cursor = connection.cursor()
cursor.execute("""
CREATE TABLE IF NOT EXISTS students (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
age INT NOT NULL,
email VARCHAR(100) NOT NULL UNIQUE
)
""")
cursor.execute("""
INSERT INTO students (name, age, email) VALUES
('Dana', 24, 'dana@mail.com'),
('Avi', 31, 'avi@mail.com'),
('Noa', 22, 'noa@mail.com')
""")
connection.commit()
cursor.execute("SELECT * FROM students")
print("Rows:", len(cursor.fetchall()))
finally:
connection.close()Rows: 3id вы не пишете — база присваивает его сама. commit() сохраняет изменение в базе.
Упражнение 5: База отказывает
Вставьте студента в таблицу, а затем попытайтесь вставить другого студента с тем же email и студента с возрастом NULL. Поймайте ошибку каждой попытки и напечатайте её сообщение (error.msg).
Ожидаемый вывод:
Rejected: Duplicate entry 'dana@mail.com' for key 'students.email'
Rejected: Column 'age' cannot be nullПоказать решение
import mysql.connector
connection = mysql.connector.connect(
host="localhost", user="root", password="kodkod", database="school")
try:
cursor = connection.cursor()
cursor.execute("""
CREATE TABLE IF NOT EXISTS students (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
age INT NOT NULL,
email VARCHAR(100) NOT NULL UNIQUE
)
""")
cursor.execute(
"INSERT INTO students (name, age, email) VALUES ('Dana', 24, 'dana@mail.com')")
attempts = [
"INSERT INTO students (name, age, email) VALUES ('Copy', 20, 'dana@mail.com')",
"INSERT INTO students (name, age, email) VALUES ('Noa', NULL, 'noa@mail.com')",
]
for sql in attempts:
try:
cursor.execute(sql)
except mysql.connector.IntegrityError as error:
print("Rejected:", error.msg)
connection.commit()
finally:
connection.close()Rejected: Duplicate entry 'dana@mail.com' for key 'students.email'
Rejected: Column 'age' cannot be nullОграничения, которые мы определили в таблице, проверяются при каждой вставке — без единой строки проверки в коде Python.