Строки и приведение типов: вопросы для собеседования (часть 2)

LIKE, SUBSTRING, CONCAT, TRIM, CAST, регулярные выражения — строковые функции нужны для парсинга и очистки данных. На собеседовании могут попросить извлечь домен из email, привести строку к числу или разобрать URL. Эти задачи проверяют практический опыт работы с «грязными» данными.

Агрегация, GROUP BY и HAVINGДаты и времяEXPLAIN и оптимизацияВыражения и NULLJOIN и операции множествОсновы SQL-запросовПодзапросы и CTEОконные функции

Вопросы 610 из 20

6В отчёте по городам вы делаете агрегацию по `city`, но видите две строки: `Moscow` и `Moscow ` (с пробелом в конце). Как исправить это прямо в запросе?
AИспользовать `TRIM(city)` и группировать по нему: `SELECT TRIM(city) ... GROUP BY TRIM(city)`
BПривести значение к числу через `CAST(city AS int)`: приведение типа автоматически удалит лишние пробелы по краям
CСравнивать значение через `LIKE 'Moscow'`: оператор сам игнорирует пробелы в конце строки города
DДобавить `LIMIT 1` в конец запроса: в результат попадёт только одна строка с городом из двух одинаковых
Ответ: Лишние пробелы часто убирают через `TRIM`, особенно перед `GROUP BY` и `JOIN`.

Пробелы в начале или конце строки — частый «мусор» в данных. При `GROUP BY` такие значения считаются разными, поэтому появляются дубликаты групп. `TRIM` нормализует строку и делает агрегацию корректной без ручной чистки таблицы.

Подробный разбор →
7В таблице `products` код товара хранится как текст, например `00123` (важны ведущие нули). Вы хотите соединить её с `catalog(code)` (тоже текст). Какое действие чаще всего приводит к багу и пропущенным совпадениям?
AСравнивать `products.code = catalog.code` напрямую без приведения и нормализации регистра
BПривести `code` к числу: `CAST(products.code AS int)` или `products.code::int` перед соединением
CУбрать пробелы по краям через `TRIM(products.code)` перед соединением
DЗаменить значения `NULL` на пустую строку через `COALESCE(products.code, '')` перед соединением
Ответ: Коды с ведущими нулями обычно нужно хранить и сравнивать как текст.

Приведение к `int` удаляет ведущие нули: `00123` превращается в `123`. После этого сравнение с текстовым `catalog.code` перестаёт находить совпадения. Для таких идентификаторов приведение к числу — частый источник скрытых ошибок.

Подробный разбор →
8Вы строите ключ для дедупликации email и хотите устранить три источника «ложных разных» значений: пробелы по краям, разный регистр и точки в локальной части (для Gmail-подобных адресов). Какое выражение учитывает все три фактора?
A`LOWER(TRIM(email))` без удаления точек из локальной части адреса
B`LOWER(TRIM(REPLACE(email, '.', '')))` со снятием пробелов и точек
C`UPPER(TRIM(email))` с приведением символов к верхнему регистру
D`TRIM(REPLACE(email, '.', ''))` без приведения букв к одному регистру
Ответ: Для нормализации email комбинируют `TRIM`, `LOWER` и `REPLACE` для удаления точек.

Пробелы по краям, разный регистр и точки в локальной части — частые причины ложных дубликатов. `LOWER(TRIM(REPLACE(email, '.', '')))` устраняет все три источника шума: `TRIM` снимает пробелы, `REPLACE` убирает точки, `LOWER` приводит к единому регистру. Только `LOWER(TRIM(...))` или `UPPER(TRIM(...))` оставляет точки и сохранит `j.smith@gmail.com` отдельно от `jsmith@gmail.com`. Без `LOWER` сохраняется чувствительность к регистру.

Подробный разбор →
9Поле `age_text` хранится как текст и иногда содержит пробелы, например `18`. Вы хотите отобрать пользователей 18+ через `WHERE`. Какой вариант наиболее корректен?
A`WHERE age_text >= 18`
B`WHERE LOWER(age_text) >= '18'`
C`WHERE CAST(TRIM(age_text) AS int) >= 18`
D`WHERE TRIM(age_text) >= 18`
Ответ: Если сравнение должно быть числовым, приводите к числовому типу после простой очистки строки.

Сравнение строк и чисел либо даст ошибку, либо будет работать не так, как вы ожидаете. Если возраст хранится текстом, надежнее сначала убрать лишние пробелы `TRIM(...)`, а затем привести к `int` через `CAST`. Тогда `WHERE ... >= 18` будет числовым и понятным.

Подробный разбор →
10Вы сделали регистронезависимый `JOIN` по email как `LOWER(u.email) = LOWER(s.email)`, и запрос стал заметно медленнее. Какое объяснение наиболее вероятно на базовом уровне и что обычно делают в аналитических витринах?
AФункция `LOWER` делает сравнение случайным, поэтому результаты плавают между запусками; нужно заменить её на `UPPER` для стабильности.
BСоединение `JOIN` по строковым ключам всегда работает медленно, и ускорить его на стороне базы практически невозможно без смены движка.
CФункция над колонкой мешает использовать индекс и оптимизации; обычно хранят отдельное нормализованное поле и соединяют уже по нему.
DПроблема в том, что `LOWER` неявно приводит результат к типу `int`; нужно вернуть его обратно в строку через приведение `::text`.
Ответ: Функции в условии соединения мешают использовать индекс; нормализацию обычно выносят в отдельное поле витрины.

Нормализовать строки прямо в запросе удобно, но это добавляет вычисления на каждую строку и часто не даёт оптимизатору использовать индекс. Типичный практичный подход — хранить готовое нормализованное значение (например, email в нижнем регистре) в витрине или материализованном слое и использовать его в `JOIN` и фильтрах. Это ускоряет соединения и делает результаты стабильными.

Подробный разбор →
1234

Хотите тренировать интерактивно?

В приложении — таймер, прогресс, стрики и 1700+ вопросов по всем темам.

Тренировать SQL в Telegram

Другие темы: SQL

Агрегация, GROUP BY и HAVINGДаты и времяEXPLAIN и оптимизацияВыражения и NULLJOIN и операции множествОсновы SQL-запросовПодзапросы и CTEОконные функции