Как составить SQL запрос для поиска первых десяти пользователей, проживающих в Москве
Что проверяют
Запрос на пять строк, поэтому смотрят на детали: поймёте ли вы, что «первые» требуют сортировки, и как передадите город из Python без SQL-инъекции. Следом обычно спрашивают про индекс и постраничную выдачу.
Как отвечать
Сразу уточните, что значит «первые»: по дате регистрации, по id или просто любые десять. Без ORDER BY порядок строк не гарантирован: база вернёт то, что быстрее прочитать, и завтра результат может быть другим.
SELECT id, name, city, created_at
FROM users
WHERE city = 'Москва'
ORDER BY created_at, id
LIMIT 10;Что стоит сказать про запрос:
idвторым ключом: делает порядок детерминированным, если у двоих одинаковое время регистрации;- диалект:
LIMITработает в PostgreSQL, MySQL и SQLite, стандартная формаFETCH FIRST 10 ROWS ONLY, в SQL ServerSELECT TOP 10; - индекс: составной по
(city, created_at)позволяет взять десять строк прямо из индекса без сортировки всей выборки; - явные колонки:
SELECT *тянет лишнее и ломает код при изменении схемы.
Из Python значение передаётся параметром, а не f-строкой: в psycopg это cur.execute("... WHERE city = %s LIMIT %s", (city, 10)). В SQLAlchemy 2.0 select(User).where(User.city == city).order_by(User.created_at).limit(10), в Django User.objects.filter(city=city).order_by("created_at")[:10], где срез превращается в LIMIT.
Хороший плюс: заметить, что хранить город строкой рискованно. «Москва», «москва» и «г. Москва» для базы разные значения, надёжнее справочник городов и city_id.
Где спотыкаются
Пишут LIMIT 10 без ORDER BY и считают, что получат первых зарегистрированных. В PostgreSQL после обновлений строк и VACUUM физический порядок меняется, а с ним и выдача.
Подставляют город через f-строку или конкатенацию. С константой это выглядит безобидно, но интервьюер почти наверняка спросит, что будет, если город придёт из запроса пользователя.
Для глубоких страниц предлагают OFFSET 100000. База всё равно читает и отбрасывает эти строки; лучше пагинация по ключу: WHERE (created_at, id) > (:last_created, :last_id).