Строки и приведение типов: вопросы для собеседования (часть 2)
LIKE, SUBSTRING, CONCAT, TRIM, CAST, регулярные выражения — строковые функции нужны для парсинга и очистки данных. На собеседовании могут попросить извлечь домен из email, привести строку к числу или разобрать URL. Эти задачи проверяют практический опыт работы с «грязными» данными.
Вопросы 6–10 из 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` и фильтрах. Это ускоряет соединения и делает результаты стабильными.
Подробный разбор →