Базовый SQL: SELECT, WHERE по id и простая фильтрация
- Получать все строки или выбранные столбцы с помощью SELECT
- Получать одну запись по id с помощью WHERE
- Писать простые условия фильтрации с =, <>, >, < и AND
- Сортировать результаты с ORDER BY и понимать, что происходит, когда ни одна строка не найдена
Что мы изучим?
SELECT = Read из CRUD: чтение данных из таблицы.
- Все строки:
SELECT * FROM students. - Выбранные столбцы:
SELECT name, email FROM students. - Одну строку по id:
SELECT ... WHERE id = 2. - Простую фильтрацию с
WHEREи условиями. - Сортировку с
ORDER BY.
Зачем это нужно?
Два самых важных запроса в любом API:
| Endpoint | SQL |
|---|---|
GET /students | SELECT ... FROM students |
GET /students/7 | SELECT ... FROM students WHERE id = 7 |
Каждый раз, когда вы открываете список в приложении (товары, сообщения, заказы), за кулисами выполняется запрос SELECT.
Объяснение с основ
SELECT … FROM
SELECT name, age FROM students;«Выбери столбцы name и age из таблицы students». Результат — новая (временная) таблица с запрошенными строками и столбцами. База не меняется: SELECT только читает, точно как GET.
*(звёздочка) = все столбцы.- Порядок в списке задаёт порядок столбцов в результате.
WHERE — фильтрация строк
SELECT name, age FROM students WHERE age > 23;WHERE = «только строки, для которых условие истинно». База проверяет условие на каждой строке и возвращает только подходящие.
| Оператор | Значение | Пример |
|---|---|---|
= | Равно (один знак!, не ==) | WHERE id = 2 |
<> (или !=) | Не равно | WHERE name <> 'Avi' |
> < >= <= | Сравнение | WHERE age >= 18 |
AND | Оба условия | WHERE age > 20 AND age < 30 |
OR | Хотя бы одно | WHERE name = 'Dana' OR name = 'Noa' |
WHERE id — самое важное
id — первичный ключ, уникальный, поэтому WHERE id = 2 возвращает не больше одной строки. Это выборка для GET /students/{student_id}. И база находит её очень быстро, даже среди миллионов строк (потому что первичный ключ автоматически индексируется).
ORDER BY — сортировка
SELECT name, age FROM students ORDER BY age;
SELECT name, age FROM students ORDER BY age DESC;По умолчанию порядок возрастающий (ASC). DESC = убывающий. Без ORDER BY порядок не гарантирован, поэтому, если порядок важен, просите его явно.
SELECT name, age FROM students = «принесите мне имя и возраст всех студентов». WHERE age > 23 = «только тех, кто старше 23». ORDER BY age = «упорядоченных по возрасту». WHERE id = 2 = «папку номер 2». Вы говорите, что нужно, а как это найти — решает библиотекарь (декларативный SQL).
Синтаксис
SELECT * FROM table_name; -- all columns, all rows
SELECT col_a, col_b FROM table_name; -- chosen columns
SELECT col_a, col_b FROM table_name WHERE id = 3; -- one row by primary key
SELECT col_a FROM table_name WHERE col_b > 10 AND col_c = 'x';
SELECT col_a FROM table_name ORDER BY col_b DESC;-- в начале текста в SQL = комментарий (как # в Python).
Первый пример кода
Таблица из предыдущего урока:
id | name | age | email
---+------+-----+--------------
1 | Dana | 24 | dana@mail.com
2 | Avi | 31 | avi@mail.com
3 | Noa | 22 | noa@mail.comВыполните в Database Client (в файле .sql: выделить → Run):
SELECT * FROM students;Результат:
id | name | age | email
1 | Dana | 24 | dana@mail.com
2 | Avi | 31 | avi@mail.com
3 | Noa | 22 | noa@mail.comSELECT name, email FROM students;name | email
Dana | dana@mail.com
Avi | avi@mail.com
Noa | noa@mail.comSELECT id, name, age, email FROM students WHERE id = 2;id | name | age | email
2 | Avi | 31 | avi@mail.comРазбор кода построчно
| Запрос | Что сделала база |
|---|---|
SELECT * FROM students | Все столбцы, все строки. |
SELECT name, email FROM students | Все строки, но только два столбца, в запрошенном порядке. |
SELECT id, name, age, email ... WHERE id = 2 | Прошла по строкам (на практике — сразу к id 2 благодаря первичному ключу) и вернула только строку, где id = 2. |
Обратите внимание: в третьем запросе мы явно написали список столбцов, а не *. Эту привычку мы будем использовать в коде: если в будущем в таблицу добавится столбец (возможно, с чувствительной информацией, например паролем), он не «просочится» по ошибке в ответы API.
Ещё один пример
Фильтрация, составные условия, сортировка и пустой результат.
SELECT name, age FROM students WHERE age > 23;name | age
Dana | 24
Avi | 31SELECT name, age FROM students WHERE age > 20 AND age < 30 ORDER BY age;name | age
Noa | 22
Dana | 24SELECT name FROM students ORDER BY name;name
Avi
Dana
NoaSELECT id, name FROM students WHERE id = 99;(no rows)Разбор:
age > 23— Дана (24) и Ави (31). Ноа (22) не прошла условие.AND+ORDER BY— сначала фильтрация (только от 20 до 30), а затем сортировка оставшегося. Порядок в запросе фиксирован:SELECT ... FROM ... WHERE ... ORDER BY ....ORDER BY name— сортировка текста по алфавиту.WHERE id = 99— такой строки нет. Это не ошибка! Результат просто пуст: 0 строк. В Pythonfetchone()вернётNone, аfetchall()— пустой список. В API это превратится в 404.
Типичная ошибка
Ошибка 1: == вместо =.
SELECT * FROM students WHERE id == 2;1064 (42000): You have an error in your SQL syntax; ... near '== 2' at line 1В SQL сравнение — один =. (И это противоположно Python, где = — присваивание!)
Ошибка 2: строка без кавычек.
SELECT * FROM students WHERE name = Avi;1054 (42S22): Unknown column 'Avi' in 'where clause'Без кавычек SQL думает, что Avi — имя столбца (точно как print(Hello) в Python ищет переменную). → WHERE name = 'Avi'.
Ошибка 3: неправильный порядок частей.
SELECT name FROM students ORDER BY age WHERE age > 20;1064 (42000): You have an error in your SQL syntax; ... near 'WHERE age > 20' at line 1Порядок: SELECT → FROM → WHERE → ORDER BY.
Ошибка 4: считать пустой результат ошибкой — или наоборот. WHERE id = 99, который ничего не нашёл, возвращает 0 строк без исключения. Проверить («не найдено») и вернуть 404 должен наш код. Если не проверить, None[0] упадёт.
Ошибка 5: считать, что сравнение текста чувствительно к регистру. В MySQL по умолчанию WHERE name = 'avi' находит Avi — сравнение не различает заглавные и строчные буквы. Это удобно для поиска, но у этого есть цена: с UNIQUE на email база откажется сохранить Dana@mail.com, если dana@mail.com уже существует. (И это отличается от Python, где "avi" == "Avi" — False!)
Практика
Упражнения опираются только на то, что пройдено в этом уроке и в предыдущих. Сначала попробуйте сами, и только потом открывайте решение.
Упражнение 1: Базовый SELECT
Во всех упражнениях этого урока сначала создайте таблицу и данные:
import mysql.connector
connection = mysql.connector.connect(
host="localhost", user="root", password="kodkod", database="school")
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, city VARCHAR(50))""")
cursor.execute("""
INSERT INTO students (name, age, city) VALUES
('Dana', 24, 'Haifa'), ('Avi', 31, 'Tel Aviv'),
('Noa', 22, 'Haifa'), ('Tom', 28, NULL)""")
connection.commit()Напишите запрос, который возвращает имя и возраст всех студентов, и напечатайте каждую строку.
Ожидаемый вывод:
('Dana', 24)
('Avi', 31)
('Noa', 22)
('Tom', 28)Показать решение
import mysql.connector
connection = mysql.connector.connect(
host="localhost", user="root", password="kodkod", database="school")
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, city VARCHAR(50))""")
cursor.execute("""
INSERT INTO students (name, age, city) VALUES
('Dana', 24, 'Haifa'), ('Avi', 31, 'Tel Aviv'),
('Noa', 22, 'Haifa'), ('Tom', 28, NULL)""")
connection.commit()
cursor.execute("SELECT name, age FROM students")
for row in cursor.fetchall():
print(row)
connection.close()('Dana', 24)
('Avi', 31)
('Noa', 22)
('Tom', 28)SELECT name, age — только нужные столбцы. SELECT * возвращает все.
Упражнение 2: WHERE по id
С теми же данными получите студента, у которого id равен 2, и студента, у которого id равен 99.
Ожидаемый вывод:
(2, 'Avi', 31, 'Tel Aviv')
NoneПоказать решение
import mysql.connector
connection = mysql.connector.connect(
host="localhost", user="root", password="kodkod", database="school")
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, city VARCHAR(50))""")
cursor.execute("""
INSERT INTO students (name, age, city) VALUES
('Dana', 24, 'Haifa'), ('Avi', 31, 'Tel Aviv'),
('Noa', 22, 'Haifa'), ('Tom', 28, NULL)""")
connection.commit()
cursor.execute("SELECT * FROM students WHERE id = 2")
print(cursor.fetchone())
cursor.execute("SELECT * FROM students WHERE id = 99")
print(cursor.fetchone())
connection.close()(2, 'Avi', 31, 'Tel Aviv')
NoneПустой результат — не ошибка: fetchone() возвращает None, когда такой строки нет.
Упражнение 3: Фильтрация и сортировка
С теми же данными: получите имена студентов из Хайфы младше 30 лет и всех студентов, отсортированных по возрасту от старшего к младшему.
Ожидаемый вывод:
[('Dana',), ('Noa',)]
[('Avi', 31), ('Tom', 28), ('Dana', 24), ('Noa', 22)]Показать решение
import mysql.connector
connection = mysql.connector.connect(
host="localhost", user="root", password="kodkod", database="school")
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, city VARCHAR(50))""")
cursor.execute("""
INSERT INTO students (name, age, city) VALUES
('Dana', 24, 'Haifa'), ('Avi', 31, 'Tel Aviv'),
('Noa', 22, 'Haifa'), ('Tom', 28, NULL)""")
connection.commit()
cursor.execute("SELECT name FROM students WHERE city = 'Haifa' AND age < 30")
print(cursor.fetchall())
cursor.execute("SELECT name, age FROM students ORDER BY age DESC")
print(cursor.fetchall())
connection.close()[('Dana',), ('Noa',)]
[('Avi', 31), ('Tom', 28), ('Dana', 24), ('Noa', 22)]Каждая строка — tuple, даже когда в ней один столбец: ('Dana',).
Упражнение 4: NULL
С теми же данными: у какого студента нет города? Напишите два запроса — один с city = NULL и один с city IS NULL — и объясните разницу.
Ожидаемый вывод:
[]
[('Tom',)]Показать решение
import mysql.connector
connection = mysql.connector.connect(
host="localhost", user="root", password="kodkod", database="school")
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, city VARCHAR(50))""")
cursor.execute("""
INSERT INTO students (name, age, city) VALUES
('Dana', 24, 'Haifa'), ('Avi', 31, 'Tel Aviv'),
('Noa', 22, 'Haifa'), ('Tom', 28, NULL)""")
connection.commit()
cursor.execute("SELECT name FROM students WHERE city = NULL")
print(cursor.fetchall())
cursor.execute("SELECT name FROM students WHERE city IS NULL")
print(cursor.fetchall())
connection.close()[]
[('Tom',)]NULL означает «нет значения», поэтому city = NULL никогда не истинно — даже для Тома. Отсутствующее значение проверяют только через IS NULL.
Упражнение 5: Запросы к товарам
Напишите запросы SQL к таблице products (name, price, stock) и выполните их в Database Client:
- Названия товаров, запас которых меньше 5.
- Все товары, отсортированные по цене от самого дешёвого к самому дорогому, — название и цена.
- Все товары, отсортированные по цене от самого дорогого к самому дешёвому.
Показать решение
SELECT name FROM products
WHERE stock < 5;
SELECT name, price FROM products
ORDER BY price;
SELECT name, price FROM products
ORDER BY price DESC;ORDER BYпо умолчанию сортирует от меньшего к большему.DESCменяет порядок на обратный.
Итоги
SELECT cols FROM table— чтение.*= все столбцы, но в коде лучше перечислять их явно.WHERE condition— только подходящие строки.=(один знак),<>,>,<,AND,OR. Строки — в'...'.WHERE id = 2— не больше одной строки, по первичному ключу. ЭтоGET /students/{id}.ORDER BY col [DESC]— сортировка. Без ORDER BY порядок не гарантирован.- Порядок частей: SELECT → FROM → WHERE → ORDER BY.
- Пустой результат — не ошибка. Код проверяет его (
None/[]) и возвращает 404. Для NULL используйтеIS NULL.