Техническое собеседование: 10 каверзных вопросов по SQL
Здравствуйте, коллеги. Представляю вам небольшую подборку каверзных вопросов по нашему любимому языку структурированных запросов. Список составлен на основе моего собственного опыта работы и хождения по собеседованиям. Я старался отбирать только те вопросы, ответы на которые могут помочь на практике, а не только на техническом собеседовании. Вопросы касаются базовых механизмов языка, потому в первую очередь будут интересны новичкам, но, возможно, и матерые разработчики узнают из них что-то новое. Итак, приступим.
1. Что вернет условие 2 <> NULL
?
Сравнение с NULL
— это, наверное, первый подводный камень, на который натыкаются люди при работе с базой данных. Вопреки привычной логике условие
2 <> NULL
возвращает ложь (FALSE)
, как впрочем и условие
2 = NULL
Дело здесь в том, что тип значения NULL
в SQL имеет несколько другой оттенок значения, чем в прикладных языках программирования. Если в С-подобных языках NULL
значит отсутствие какого-то значения, то в SQL он значит лишь то, что мы не знаем этого значения. По этой причине любое сравнение с NULL
возвращает ложь.
Возвращаясь к сути вопроса, мы не можем сказать «Два не равно NULL
» потому, что мы не знаем значения справа от знака неравенства, а там как раз может оказаться двойка.
2. Что вернет условие 3 NOT IN (1, 2, NULL)
?
Здесь та же история, что и в предыдущем случае. Условие
3 NOT IN (1, 2, NULL)
возвращает ложь (FALSE), как и условие
3 IN (1, 2, NULL)
Причина этого заключается в особенностях работы оператора IN
. Проверяя, что определенное значение входит в коллекцию, оператор IN
просто сравнивает это значение с каждым элементом коллекции.
Другими словами:
3 IN (1, 2, NULL)
это то же самое, что и
(3 = 1) OR (3 = 2) OR (3 = NULL)
В случае с NOT IN
условие:
3 NOT IN (1, 2, NULL)
это то же самое, что и
(3 <> 1) AND (3 <> 2) AND (3 <> NULL)
Как мы знаем из предыдущего примера, 3 <> NULL
возвращает ложь, а значит и все условие (3 <> 1) AND (3 <> 2) AND (3 <> NULL)
тоже будет ложным.
3. Выполнится ли этот запрос?
SELECT order_id, order_code, SUM(order_value) FROM orders GROUP BY order_id
Едино правильного ответа на этот вопрос нет — все зависит от базы данных. Проблема этого запроса заключается в том, что колонка order_code
не указана в выражении GROUP BY
и при этом для нее не определена агрегатная функция. То есть по отношению к колонке order_code
мы не знаем группировать ее или группировать по ней.
Если этот запрос будет выполняться в MySQL, то колонка order_code
добавится в выражение GROUP BY
автоматически и запрос выполнится нормально. Если же этот запрос будет выполняться MS SQL Server, то по умолчанию будет сгенерирована ошибка. Впрочем, это поведение настраивается.
4. Почему не выполнится этот запрос?
SELECT user_name, YEAR(user_birth_date) AS year_of_birth FROM users WHERE year_of_birth = 2000
Запрос не выполнится из-за обращения к псевдониму year_of_birth
в выражении WHERE
. Дело в том, что псевдонимы полей в SQL используются для форматирования данных уже полученных из базы. Поэтому их можно использовать только в выражениях, которые отвечают за оформление результата, таких как GROUP BY
, ORDER BY
и HAVING
. В выражениях, отвечающих за получение данных, таких как WHERE
, нужно использовать оригинальные имена полей.
WHERE YEAR(user_birth_date) = 2000
5. Имеет ли значение порядок колонок в составном индексе?
Да.
CREATE NONCLUSTERED INDEX MyInd on users (user_name, user_birth_date);
это не то же самое, что
CREATE NONCLUSTERED INDEX MyInd on users (user_birth_date, user_name);
При создании составного индекса сначала строится обычный индекс для его первой колонки, а потом уже на основе него строится индекс для второй колонки и так дальше.
Другими словами, колонки, по которым поиск выполняется чаще всего, должны стоять в составном индексе первыми.
6. Какая разница между TRUNCATE TABLE table_name
и DELETE FROM table_name?
Фактически обе эти команды вызовут удаление всех строк из таблицы под названием table_name
, но вот произойдет это совсем по-разному:
- При вызове команды
TRUNCATE
таблица полностью сбрасывается и создается снова, в то время как командаDELETE
удаляет каждую строку таблицы по отдельности. Из-за этогоTRUNCATE
отрабатывает значительно быстрее. - Как следствие первого пункта, команда
TRUNCATE
не вызывает срабатывание триггеров и правил внешних ключей, то есть, очищая таблицу таким способом, можно не бояться каскадного удаления или изменения данных в других таблицах. - В отличие от
DELETE
командаTRUNCATE
не транзакционная. То есть, если в момент ее вызова, таблицаtable_name
будет заблокирована какой-либо транзакцией — может возникнуть ошибка.
7. Какая разница между типами CHAR
и VARCHAR?
Оба эти типа используются для хранения текстовой информации ограниченной длины, а различия между ними следующие:
- Тип
CHAR
хранит значение фиксированной длины. Если строка, помещаемая в колонку данного типа, имеет меньшую длину, чем длина типа — строка будет дополнена пробелами. Например, если в колонку типаCHAR(10)
записать строкуSQL
, то она сохранится какSQL
.Тип
VARCHAR
хранит значение переменной длины. Под каждое значение этого типа выделяется столько памяти, сколько нужно для этого конкретного значения. - Для типа
CHAR
используется статическое распределение памяти, из-за чего операции с ним быстрее, чем сVARCHAR
.
Таким образом, тип CHAR
подходит для хранения строковых данных фиксированной длины (например, инвентарных номеров, хешей), а для остальных строк больше подойдут VARCHAR
или NVARCHAR
.
8. Какая разница между типами VARCHAR
и NVARCHAR
?
Тип NVARCHAR
, пожалуй, самый универсальный из строчных типов данных в БД. Он позволяет хранить строки переменной длины в формате Unicode. В этом формате каждый символ занимает 2 байта, а сама кодировка содержит 65 536 символов и включает в себя все языки мира, в том числе иероглифы.
Тип VARCHAR
хранит данные в формате SACII. В этом формате каждый символ занимает 1 байт, но отельная кодировка содержит всего 256 символов. Из-за этого для каждого мирового языка выделяется своя кодировка.
Таким образом, в формате VARCHAR
стоит хранить строчные данные, которые точно не придется переводить (например, адреса электронной почты). Для других случаев больше подойдет NVARCHAR
.
9. Какая разница между UNION
и UNION ALL
?
Выражения UNION
и UNION ALL
— это очень надежные поставщики лишних или недостающих строк в результате запроса. Оба эти выражения используются, чтобы объединить результаты нескольких независимых друг от друга запросов. А разница между ними заключается в том, что, если в результатах запросов есть одинаковые строки, то UNION
удалит дубликаты, оставив только одну из таких строк. В то же время UNION ALL
, как можно догадаться из названия, просто объединит результаты запросов, не обращая внимания на дубликаты.
10. Какая разница между выражениями WHERE
и HAVING
?
Ну и наконец, вопрос, который задают практически на каждом собеседовании по базам данных: про HAVING
.
Выражения WHERE
и HAVING
используются для фильтрации результата запроса и ожидают после себя некоторое условие, по которому нужно отфильтровать данные. Но, если WHERE
работает само по себе и фильтрует данные каждой строки результата по отдельности, то выражение HAVING
имеет смысл только в сочетании с выражением GROUP BY
и фильтрует уже сгруппированные значения.
Спасибо за внимание. Пишите в комментариях, на сколько вопросов вы знали ответ, а также с какими хитрыми задачами по SQL вам самим приходилось сталкиваться.