Skip to content

Вопросы по SQL Server, задаваемые на интервью опытным разработчикам

Пересказ статьи Sergey Gigoyan. SQL Server interview questions for experienced developers


В этой статье приводятся 10 технических вопросов по SQL Server для старших разработчиков SQL Server и даются ответы на них с примерами. Хотя в интернете существуют сотни статей и публикаций в блогах, имеющих отношение к интервью по серверам SQL, я решил поделиться моим подходом к интервьюированию кандидатов на разработчика баз данных старшего уровня (senior).

Стояла задача задать минимальное число вопросов за ограниченное время (час или менее) и, в то же время, выбрать такие вопросы, которые в максимальной степени покроют основные темы разработки баз данных. Таким образом, вместо того, чтобы задавать несколько вопросов по одной теме, я попытался сформулировать вопросы так, чтобы каждый вопрос представлял целиком одну тему.
Для интервьюирования опытных кандидатов вопросы подразумевают довольно глубокий теоретический уровень. Кроме того, вопросы сформулированы таким образом, чтобы при ответе на главный вопрос, интервьюируемые бы автоматически отвечали на несколько подвопросов, связанных одной темой. Хотя некоторые вопросы могут показаться краткими и точными, требующими простых ответов, мы не ожидаем лаконичных ответов в стиле "да" или "нет". Кандидат должен дать полный ответ, почему это возможно или невозможно, и в идеале пояснить ответ примером. Таким образом, это покажет, насколько глубоко кандидаты понимают материал. При этом не подразумевается, что все опытные главные разработчики баз данных ответят на все предложенные вопросы. На самом деле было бы замечательно и удивительно для интервьюеров, если бы были получены правильные и развернутые ответы на все вопросы. Однако даже кандидаты с богатым опытом могут что-то забыть или могут иметь мало практики в конкретной области, или никогда не использовать некоторые системные возможности баз данных. Следовательно, если кандидаты сталкиваются с трудностью ответа на главный вопрос, интервьюер может дать подсказку, подводящую их к ответу на подчиненные вопросы, для проверки уровня знаний соответствующей темы.

Стоит заметить, что хотя все приведенные ниже вопросы являются теоретическими и не требуют написания кода, я верю, что без большого опыта в разработке схем баз данных невозможно правильно ответить на эти вопросы. Поэтому они могут быть полезны как для тестирования уровня теоретической подготовки кандидатов, так и их практического опыта. Эти вопросы могут оказаться очень эффективными для телефонного интервью, благодаря их сложности, небольшого времени и отсутствия необходимости написания кода. Я бы рекомендовал проведение интервью на написание кода T-SQL после успешного прохождения кандидатом теоретического этапа.

Вопросы


Вот вопросы. Я рекомендую внимательно прочитать вопросы, правильно их понять, а затем попытаться ответить. Даже если вы не знаете ответа, но в курсе темы, подумайте и попытайтесь угадать ответ, прежде чем прочитать предложенные ответы в последующем разделе. Итак, вопросы:

  1. Возможно ли создать первичный ключ как некластеризованный индекс? Если да, для чего это могло бы потребоваться?

  2. Рекомендуется иметь ключ кластеризованного индекса как можно меньших размеров. Какая основная причина делать это?

  3. Зачем используется конструкция WITH CHECK OPTION при создании представления?

  4. Какая разница между операторами JOIN и APPLY?

  5. Может ли хранимая процедура вызываться внутри пользовательской функции и наоборот? В чем разница между ними?

  6. Возможно ли использовать команды INSERT, UPDATE и DELETE внутри пользовательской функции? Если можно, приведите пример такого использования.

  7. Какие ошибки не могут быть обработаны в блоке CATCH? Дайте просто один пример такого типа ошибки.

  8. Какое главное отличие между триггерами AFTER и INSTEAD OF?

  9. Какие методы могут использоваться для проверки наличия активных транзакций в текущем подключении? Как можно проверить число активных транзакций в текущем подключении?

  10. Какие уровни изоляции транзакций предотвращают все проблемы транзакций (проблемы параллелизма), и как это достигается? Другими словами, какие наивысшие уровни изоляции транзакций в SQL Server, и чем они отличаются друг от друга?


Ответы


Ниже приводятся ответы на вышеприведенные вопросы. Я постарался ответить максимально полно за пару минут. Изучите ответы, и проверьте правильно ли вы ответили, или не забыли ли чего-нибудь.

1. Возможно ли создать первичный ключ как некластеризованный индекс? Если да, для чего это могло бы потребоваться?

Ответ на этот вопрос - да, это возможно.

Хотя создание первичного ключа как кластеризованного индекса (вариант по умолчанию) рекомендуется как лучшая практика, имеются ситуации, когда необходимо создать некластеризованный ключ.

Например, предположим, что некоторый столбец вашей таблицы интенсивно используется в запросах как ключевой столбец в соединениях. Кроме того, многие запросы сортируют эту таблицу по этому столбцу. Этот столбец является хорошим кандидатом для кластеризованного индекса. Но в силу логики вашего приложения этот столбец допускает NULL и дубликаты значений. Следовательно, вы не можете создать первичный ключ на этом столбце. Тем не менее, кластеризованный индекс может быть создан на этом столбце, но не первичный ключ. Так как первичный ключ также необходим для однозначной идентификации каждой строки в таблице, вы можете создать первичный ключ на автоинкрементируемом (identity) уникальном столбце, не допускающем NULL значений.

2. Рекомендуется иметь ключ кластеризованного индекса как можно меньших размеров. Какая основная причина делать это?

Это хорошая идея иметь кластеризованный индекс минимальных размеров, поскольку размер кластеризованного индекса влияет на размеры некластеризованных индексов в той же самой таблице. Это обусловлено тем фактом, что некластеризованные индексы кластеризованной таблицы используют кластеризованный ключ в качестве указателя для ссылок на соответствующие строки в таблице. Для обеспечения этого ключ кластеризованного индекса хранится в узлах листового уровня некластеризованных индексов наряду со значениями столбца некластеризованного индекса. В результате, чем больше размер ключа кластеризованного индекса, тем больше размеры некластеризованных индексов.

3. Зачем используется конструкция WITH CHECK OPTION при создании представления?

Если вы хотите обновить таблицу через представление и гарантировать, чтобы модифицированные данные были видны в представлении, используйте конструкцию WITH CHECK при создании представления. Если представление создается с WITH CHECK, результат любого оператора INSERT или UPDATE, выполненного с представлением, должен удовлетворять критерию в предложении WHERE для представления. Другими словами, если используется WITH CHECK, будет невозможно обновиться строку таким образом, чтобы она исчезла из представления. Любые попытки модификации строк, которые не удовлетворяют критерию WHERE будут завершаться неудачно с понятным сообщением об ошибке, а выполнение оператора будет прервано. Важно отметить, что эти ограничения относятся только к модификации данных с помощью представления. Однако эти строки могут быть успешно модифицированы при работе с соответствующей таблицей.

4. Какая разница между операторами JOIN и APPLY?

Оператор APPLY имеет две формы - CROSS APPLY и OUTER APPLY. Подобно оператору INNER JOIN, CROSS APPLY возвращает только те строки из левой (внешней) таблицы (соединенные с соответствующими строками из правой таблицы) для который выполняется условие. OUTER APPLY возвращает все строки из левой таблицы, независимо от условия. Однако строки, которые удовлетворяют условию сочетаются в результирующем наборе с соответствующими строками из правой таблицы. Те строки, для который условие не является истинным, содержат NULL в полях значений соответствующих столбцов. Таким образом, OUTER APPLY подобно оператору LEFT JOIN.

Разница заключается в том, что операторы APPLY могут использовать табличнозначные функции в качестве правой таблицы, которые могут принимать в качестве аргументов столбцы из левой таблицы, что невозможно в случае JOIN. Эта особенность делает операторы APPLY довольно гибкими для реализации сложной логики в рамках табличнозначной функции с последующим использованием её в операторе APPLY. Достижение того же эффекта с помощью лишь JOIN потребует значительно больших усилий, что сделает код более объемным и сложным (хотя производительность может оказаться лучше). Также стоит упомянуть, что в отличие от JOIN, в операторах APPLY отсутствует предложение ON, а условие определяется в правом табличном выражении.

5. Может ли хранимая процедура вызываться внутри пользовательской функции и наоборот? В чем разница между ними?

Хотя можно вызывать функции внутри хранимой процедуры, невозможно вызвать хранимую процедуру из функции. Способы вызова хранимых процедур и пользовательских функций также различны. Для вызова хранимой процедуры должна использоваться команда EXECUTE (или EXEC). Напротив, пользовательские функции должны вызываться как часть оператора SQL (например, в операторе SELECT). В отличие от хранимых процедур, функции не могут модифицировать данные. Если было бы возможно вызывать хранимые процедуры внутри функции, это означало бы возможность изменять данные с помощью функции. Поскольку логика DML может быть воплощена в хранимой процедуре, это означало бы изменение состояния базы данных посредством функции. Таким образом, логично, что вызов процедуры из функции невозможен. Однако можно вызывать другие хранимые процедуры из хранимых процедур (уровень вложения - 32). Также возможно вызвать функцию из другой пользовательской функции.

6. Возможно ли использовать команды INSERT, UPDATE и DELETE внутри пользовательской функции? Если можно, приведите пример такого использования.

Хотя это звучит странно и удивительно для многих разработчиков, но ответ - да, возможно модифицировать табличные переменные внутри функций. Хотя ни локальные, ни глобальные временные таблицы не допускаются в UDF, табличные переменные могут использоваться внутри функций. Операторы DML могут быть выполнены внутри UDF для модификации данных табличных переменных. Например, данные можно вставлять, изменять и удалять в табличных переменных внутри UDF. Важно заметить, что табличные переменные хранятся в tempdb, как и временные таблицы, а не в памяти. Тем не менее, временные таблицы не могут даже использоваться в UDF. Таким образом, модификация данных внутри табличных переменных не рассматривается как изменение состояния базы данных.

В общем, утверждение, что операторы DML не могут быть выполнены внутри пользовательских функций является неверным. Эти операторы не могут выполняться относительно постоянных объектов базы данных (таких как таблицы), но могут использованы для модификации табличных переменных.

7. Какие ошибки не могут быть обработаны в блоке CATCH? Дайте просто один пример такого типа ошибки.

Конструкция TRY…CATCH используется для воплощения механизма обработки ошибок в T-SQL. В случае возникновения ошибки в блоке TRY управление передается в блок CATCH. Обычно в блоке CATCH реализуется логика ответа на ошибку. Однако на некоторые ошибки влияния конструкция TRY…CATCH не оказывает.

Ошибки уровня тяжести 10 или ниже (которые присутствуют в информационных сообщениях) и ошибки уровня 20 или выше (которые указывают на системные проблемы и фатальные ошибки) служат такими примерами. Прерванный запрос клиента, разорванное соединение с клиентом и убитые сессии также не отлавливаются конструкцией TRY…CATCH. В случае ошибок перекомпиляции уровня оператора (например, ошибки разрешения имени объекта) и ошибок компиляции, препятствующих выполнению пакета (например, синтаксических ошибок) управление не передается в блок CATCH. Таблица в блоке TRY, которая не существует в базе данных, может рассматриваться в качестве общего примера ошибки, которая не перехватывается блоком CATCH. Так, если вы используете несуществующую таблицу в операторах блока TRY, будет генерироваться ошибка разрешения имени объекта , и управление не будет передаваться в блок CATCH.

8. Какое главное отличие между триггерами AFTER и INSTEAD OF?

Прежде чем обсуждать различие триггеров, дам их краткое описание. Триггер считается специальным типом хранимой процедуры. Однако в отличие от обычной хранимой процедуры триггеры не могут вызываться непосредственно. Они выполняются автоматически в ответ на специальные события, возникающие в базе данных. Есть три типа триггеров: триггеры DML - которые срабатывают в ответ на события языка манипуляции данными (операторы INSERT, UPDATE или DELETE), триггеры DDL - которые связаны с событиями языка определения данных (DDL), и Logon - которые связаны с событиями входа в систему.

Триггеры INSTEAD OF и AFTER являются триггерами DML, т.е. они срабатывают при соответствующем действии DML. Триггер AFTER выполняется только после успешного выполнения соответствующего оператора (операторов) на связанной таблице (операторы запускаются, проверяются соответствующие ограничения и успешно выполняются каскадные ссылочные действия). Например, если вы хотите отслеживать исторические данные и гарантировать, что все удаленные из таблицы строки будут попадать в другую таблицу, вы можете определить на этой таблице триггер AFTER DELETE и описать логику триггера для вставки удаленных строк в соответствующую "историческую" таблицу. Как отмечалось выше, триггер AFTER не будет выполнен, если соответствующие операторы завершаются неудачно (например, из-за нарушения ограничения и т.п.)

Триггеры INSTEAD OF выполняются вместо соответствующих команд DML, если имеют место особые события, определенные логикой триггера. Другими словами, в предопределенных случаях возможно выполнение отличных действий, определенных логикой триггера, вместо ожидаемого выполнения операторов DML. Например, если вы хотите ограничить вставку некоторых значений в столбец и отследить попытки такого рода неудачных вставок. Вы можете в таких случаях использовать триггер INSTEAD OF INSERT для выполнения вставки в другую (журнальную) таблицу. Таким образом, когда кто-то пытается вставить запрещенное значение в таблицу, эта попытка будет предотвращена и записана в другую таблицу или вообще не произойдет, в зависимости от логики триггера INSTEAD OF.

В то время как вы можете иметь на таблице множество триггеров AFTER на каждое действие DML, допускается только один триггер INSTEAD OF на каждую из команд INSERT, DELETE, UPDATE для отдельной таблицы. Триггеры INSTEAD OF могут определяться не только для таблиц, но также и для представлений, в отличие от триггеров AFTER, которые могут применяться только к таблицам.

9. Какие методы могут использоваться для проверки наличия активных транзакций в текущем подключении? Как можно проверить число активных транзакций в текущем подключении?

Системные функции @@TRANCOUNT and XACT_STATE() могут использоваться для определения наличия активных транзакций в текущем подключении.

Функция XACT_STATE() возвращает только 3 значения: 1, 0 и -1. Когда XACT_STATE()=1, это означает, что сессия имеет активные транзакции, 0 означает, что в текущей сессии нет транзакций (если XACT_STATE()=-1, то имеются незафиксированные транзакции). Таким образом, если условие XACT_STATE()<>0 истинно, то сессия содержит активные транзакции. Если вы хотите обнаружить только зафиксированные транзакции, вы можете использовать XACT_STATE()=1, и XACT_STATE()=-1 - для незафиксированных.

Каждый оператор BEGIN TRANSACTION увеличивает значение переменной @@TRANCOUNT на 1, а каждый оператор COMMIT уменьшает его на 1 (в начале новой сессии @@TRANCOUNT=0). Оператор ROLLBACK устанавливает значение @@TRANCOUNT в 0 (за исключением случая отката к точке сохранения, когда значение не меняется). Следовательно, значение @@TRANCOUNT>0 означает, что в текущем подключении имеются активные транзакции. Более того, значение @@TRANCOUNT показывает число вложенных транзакций. Например, если @@TRANCOUNT=3, это говорит о том, что имеется три активных транзакции в текущем подключении. Следовательно @@TRANCOUNT может использоваться для определения числа активных транзакций, которые имеет текущая сессия. Для сравнения, системная функция XACT_STATE() не применима для этой задачи, т.к. она выявляет только факт наличия активных транзакций, но не их число. В свою очередь, @@TRANCOUNT не может использоваться для обнаружения незафиксированных транзакций, в отличие от XACT_STATE().

10. Какие уровни изоляции транзакций предотвращают все проблемы транзакций (проблемы параллелизма), и как это достигается? Другими словами, какие наивысшие уровни изоляции транзакций в SQL Server, и чем они отличаются друг от друга?

Имеется пять уровней изоляции транзакции в SQL Server, и только два из них устраняют все три конфликта (грязные чтения, неповторяющиеся чтения, фантомы). Следовательно, эти два представляют высший уровень изоляции. Этими уровнями изоляции являются сериализуемый уровень и уровень снимка. Как упоминалось выше, в отличие от трех предшествующих уровней (чтение незафиксированных данных - грязное чтение, возможно и чтение зафиксированых данных - когда возможны неповторяющиеся чтения, и повторяющиеся чтения - когда возможны фантомные данные), эти уровни изоляции предотвращают все три явления. Для этих двух уровней изоляции невозможно чтение незафиксированных данных (грязных), значение строки, извлекаемой однажды в транзакции, не может быть изменено (предотвращение неповторяющихся чтений), и вставка строк, удовлетворяющих критерию выборки внутри транзакции устраняется предотвращением вставки фантомных данных.

Хотя оба уровня изоляции гарантируют высший уровень изоляции, они используют совершенно разные подходы. В случае сериализуемого уровня изоляции для изоляции данных, используемых текущей транзакцией, от других транзакций применяется блокировка. Это, в свою очередь, уменьшает конкуренцию. Уровень изоляции снимка устраняет все эти конфликты использованием версионности строк. Как и говорит название, уровень изоляции снимка сохраняет снимок (старая версия) строк, модифицированных другими транзакциями в базе данных tempdb. Только эти данные снимка видны внутри транзакции. Этот уровень определенно увеличивает конкуренцию, но, с другой стороны, создает дополнительную нагрузку на tempdb.

Заключение


Имеются различные подходы, методы, методологии и стили интервьюирования разработчиков баз данных. Одни предпочитают задавать сначала простые вопросы, переходя затем к более сложным, для проверки уровня кандидата. Другие задают вопросы разной сложности в произвольном порядке и по разным темам. Я обсудил мою версию вопросов для интервью опытных профессионалов.

Ключевым моментом здесь является такая разработка вопросов, которая делает процесс интервью незатратным по времени и поможет объективно оценить уровень кандидата. Вот почему предложенный список включает ограниченное число вопросов. В то же время, каждый из этих вопросов имеет отношение к одной из главных тем разработки баз данных. В результате все вопросы максимально полно охватывают все общие аспекты в области разработки баз данных.

Хотя каждый из этих вопросов может считаться сложным и трудным, он включает несколько более простых подвопросов. Таким образом, даже если кандидат не может дать полный ответ на вопрос, он имеет шанс дать ответ по некоторым ключевым концепциям в рамках вопроса. Поскольку здесь нет вопросов, связанных с написанием кода, и число вопросов невелико, этот список может быть использовать для телефонного интервью.
Категории: T-SQL

Обратные ссылки

Нет обратных ссылок

Комментарии

Показывать комментарии Как список | Древовидной структурой

Нет комментариев.

Автор не разрешил комментировать эту запись

Добавить комментарий

Enclosing asterisks marks text as bold (*word*), underscore are made via _word_.
Standard emoticons like :-) and ;-) are converted to images.

To prevent automated Bots from commentspamming, please enter the string you see in the image below in the appropriate input box. Your comment will only be submitted if the strings match. Please ensure that your browser supports and accepts cookies, or your comment cannot be verified correctly.
CAPTCHA

Form options

Добавленные комментарии должны будут пройти модерацию прежде, чем будут показаны.