[Вопросы для собеседования](README.md) # SQL + [Что такое _«SQL»_?](#Что-такое-sql) [Junior] + [Какие существуют операторы SQL?](#Какие-существуют-операторы-sql) [Junior] + [Что означает `NULL` в SQL?](#Что-означает-null-в-sql) [Junior] + [Что такое _«временная таблица»_? Для чего она используется?](#Что-такое-временная-таблица-Для-чего-она-используется) [Junior] + [Что такое _«представление» (view)_ и для чего оно применяется?](#Что-такое-представление-view-и-для-чего-оно-применяется) [Middle+] + [Каков общий синтаксис оператора `SELECT`?](#Каков-общий-синтаксис-оператора-select) [Junior] + [Что такое `JOIN`?](#Что-такое-join) [Junior] + [Какие существуют типы `JOIN`?](#Какие-существуют-типы-join) [Junior] + [Что лучше использовать `JOIN` или подзапросы?](#Что-лучше-использовать-join-или-подзапросы) [Junior] + [Для чего используется оператор `HAVING`?](#Для-чего-используется-оператор-having) [Junior] + [В чем различие между операторами `HAVING` и `WHERE`?](#В-чем-различие-между-операторами-having-и-where) [Junior] + [Для чего используется оператор `ORDER BY`?](#Для-чего-используется-оператор-order-by) [Junior] + [Для чего используется оператор `GROUP BY`?](#Для-чего-используется-оператор-group-by)[Junior] + [Как `GROUP BY` обрабатывает значение `NULL`?](#Как-group-by-обрабатывает-значение-null)[Junior] + [В чем разница между операторами `GROUP BY` и `DISTINCT`?](#В-чем-разница-между-операторами-group-by-и-distinct)[Junior] + [Перечислите основные агрегатные функции.](#Перечислите-основные-агрегатные-функции)[Junior] + [В чем разница между `COUNT(*)` и `COUNT({column})`?](#В-чем-разница-между-count-и-countcolumn)[Junior] + [Что делает оператор `EXISTS`?](#Что-делает-оператор-exists)[Middle+] + [Для чего используются операторы `IN`, `BETWEEN`, `LIKE`?](#Для-чего-используются-операторы-in-between-like)[Junior] + [Для чего применяется ключевое слово `UNION`?](#Для-чего-применяется-ключевое-слово-union)[Middle+] + [Какие ограничения на целостность данных существуют в SQL?](#Какие-ограничения-на-целостность-данных-существуют-в-sql)[Junior] + [Какие отличия между ограничениями `PRIMARY` и `UNIQUE`?](#Какие-отличия-между-ограничениями-primary-и-unique)[Junior] + [Может ли значение в столбце, на который наложено ограничение `FOREIGN KEY`, равняться `NULL`?](#Может-ли-значение-в-столбце-на-который-наложено-ограничение-foreign-key-равняться-null)[Junior] + [Как создать индекс?](#Как-создать-индекс)[Junior] + [Что делает оператор `MERGE`?](#Что-делает-оператор-merge)[Middle+] + [В чем отличие между операторами `DELETE` и `TRUNCATE`?](#В-чем-отличие-между-операторами-delete-и-truncate)[Junior] + [Что такое _«хранимая процедура»_?](#Что-такое-хранимая-процедура)[Junior] + [Что такое _«триггер»_?](#Что-такое-триггер)[Junior] + [Что такое _«курсор»_?](#Что-такое-курсор)[Middle+] + [Опишите разницу типов данных `DATETIME` и `TIMESTAMP`.](#Опишите-разницу-типов-данных-datetime-и-timestamp)[Junior] + [Для каких числовых типов недопустимо использовать операции сложения/вычитания?](#Для-каких-числовых-типов-недопустимо-использовать-операции-сложениявычитания)[Junior] + [Какое назначение у операторов `PIVOT` и `UNPIVOT` в Transact-SQL?](#Какое-назначение-у-операторов-pivot-и-unpivot-в-transact-sql)[Middle+] + [Расскажите об основных функциях ранжирования в Transact-SQL.](#Расскажите-об-основных-функциях-ранжирования-в-transact-sql)[Middle+] + [Для чего используются операторы `INTERSECT`, `EXCEPT` в Transact-SQL?](#Для-чего-используются-операторы-intersect-except-в-transact-sql)[Middle+] + [Напишите запрос...](#Напишите-запрос)[Middle+] ## Что такое _«SQL»_? [Junior] SQL, Structured query language («язык структурированных запросов») — формальный непроцедурный язык программирования, применяемый для создания, модификации и управления данными в произвольной реляционной базе данных, управляемой соответствующей системой управления базами данных (СУБД). [к оглавлению](#sql) ## Какие существуют операторы SQL? [Junior] __операторы определения данных (Data Definition Language, DDL)__: + `CREATE` создает объект БД (базу, таблицу, представление, пользователя и т. д.), + `ALTER` изменяет объект, + `DROP` удаляет объект; __операторы манипуляции данными (Data Manipulation Language, DML)__: + `SELECT` выбирает данные, удовлетворяющие заданным условиям, + `INSERT` добавляет новые данные, + `UPDATE` изменяет существующие данные, + `DELETE` удаляет данные; __операторы определения доступа к данным (Data Control Language, DCL)__: + `GRANT` предоставляет пользователю (группе) разрешения на определенные операции с объектом, + `REVOKE` отзывает ранее выданные разрешения, + `DENY` задает запрет, имеющий приоритет над разрешением; __операторы управления транзакциями (Transaction Control Language, TCL)__: + `COMMIT` применяет транзакцию, + `ROLLBACK` откатывает все изменения, сделанные в контексте текущей транзакции, + `SAVEPOINT` разбивает транзакцию на более мелкие. [к оглавлению](#sql) ## Что означает `NULL` в SQL? [Junior] `NULL` - специальное значение (псевдозначение), которое может быть записано в поле таблицы базы данных. NULL соответствует понятию «пустое поле», то есть «поле, не содержащее никакого значения». `NULL` означает отсутствие, неизвестность информации. Значение `NULL` не является значением в полном смысле слова: по определению оно означает отсутствие значения и не принадлежит ни одному типу данных. Поэтому `NULL` не равно ни логическому значению `FALSE`, ни _пустой строке_, ни `0`. При сравнении `NULL` с любым значением будет получен результат `NULL`, а не `FALSE` и не `0`. Более того, `NULL` не равно `NULL`! [к оглавлению](#sql) ## Что такое _«временная таблица»_? Для чего она используется?[Junior] __Временная таблица__ - это объект базы данных, который хранится и управляется системой базы данных на временной основе. Они могут быть локальными или глобальными. Используется для сохранения результатов вызова хранимой процедуры, уменьшение числа строк при соединениях, агрегирование данных из различных источников или как замена курсоров и параметризованных представлений. [к оглавлению](#sql) ## Что такое _«представление» (view)_ и для чего оно применяется? [Middle+] __Представление__, View - виртуальная таблица, представляющая данные одной или более таблиц альтернативным образом. В действительности представление – всего лишь результат выполнения оператора `SELECT`, который хранится в структуре памяти, напоминающей SQL таблицу. Они работают в запросах и операторах DML точно также как и основные таблицы, но не содержат никаких собственных данных. Представления значительно расширяют возможности управления данными. Это способ дать публичный доступ к некоторой (но не всей) информации в таблице. Чтобы создать представление в SQL, необходимо выполнить следующие шаги: + Определить запрос, который будет использоваться для создания представления. Запрос должен быть допустимым SELECT-запросом, который извлекает данные из одной или нескольких таблиц в базе данных. + Используя ключевое слово CREATE VIEW, создайте представление и задайте ему имя. Например, следующий запрос создаст представление "customers_view", которое будет извлекать данные из таблицы "customers": ```sql CREATE VIEW customers_view AS SELECT customer_id, customer_name, customer_email FROM customers; ``` + После того, как представление создано, можно использовать его в SQL-запросах как обычную таблицу. Например, чтобы выполнить запрос, который извлекает данные из представления "customers_view", можно использовать следующий запрос: ```sql SELECT * FROM customers_view WHERE customer_name LIKE 'John%'; ``` Важно отметить, что представления не хранят данные, поэтому они не занимают место в базе данных. Они являются виртуальными таблицами, которые создаются на основе запросов к другим таблицам в базе данных. Кроме того, представления можно использовать для упрощения сложных запросов и для обеспечения безопасности данных. [к оглавлению](#sql) ## Чем отличаются временные таблицы и представления? Временные таблицы и представления - это два разных концепта в SQL, которые имеют свои отличительные особенности. Временные таблицы - это таблицы, которые создаются во временной памяти или на диске и используются для хранения данных в течение сеанса работы пользователя с базой данных. Эти таблицы могут быть использованы для выполнения сложных операций, которые требуют временного хранения промежуточных результатов. Временные таблицы автоматически удаляются по завершении сеанса или при выполнении команды DROP TABLE. Представления, с другой стороны, являются виртуальными таблицами, созданными на основе запроса к одной или нескольким таблицам в базе данных. Представления не хранят данные, а предоставляют логическое представление данных, хранящихся в таблицах. Они используются для упрощения доступа к данным и для обеспечения безопасности данных. Основное отличие между временными таблицами и представлениями заключается в том, что временные таблицы хранят данные, тогда как представления не хранят данные. Кроме того, временные таблицы могут использоваться для выполнения сложных операций, которые требуют временного хранения промежуточных результатов, тогда как представления используются для упрощения доступа к данным и для обеспечения безопасности данных. ## Каков общий синтаксис оператора `SELECT`? [Junior] `SELECT` - оператор DML SQL, возвращающий набор данных (выборку) из базы данных, удовлетворяющих заданному условию. Имеет следующую структуру: ```sql SELECT [DISTINCT | DISTINCTROW | ALL] select_expression,... FROM table_references [WHERE where_definition] [GROUP BY {unsigned_integer | column | formula}] [HAVING where_definition] [ORDER BY {unsigned_integer | column | formula} [ASC | DESC], ...] ``` [к оглавлению](#sql) ## Что такое `JOIN`? [Junior] __JOIN__ - оператор языка SQL, который является реализацией операции соединения реляционной алгебры. Предназначен для обеспечения выборки данных из двух таблиц и включения этих данных в один результирующий набор. Особенностями операции соединения являются следующее: + в схему таблицы-результата входят столбцы обеих исходных таблиц (таблиц-операндов), то есть схема результата является «сцеплением» схем операндов; + каждая строка таблицы-результата является «сцеплением» строки из одной таблицы-операнда со строкой второй таблицы-операнда; + при необходимости соединения не двух, а нескольких таблиц, операция соединения применяется несколько раз (последовательно). ```sql SELECT field_name [,... n] FROM Table1 {INNER | {LEFT | RIGHT | FULL} OUTER | CROSS } JOIN Table2 {ON | USING (field_name [,... n])} ``` [к оглавлению](#sql) ## Какие существуют типы `JOIN`? [Junior] __(INNER) JOIN__ Результатом объединения таблиц являются записи, общие для левой и правой таблиц. Порядок таблиц для оператора не важен, поскольку оператор является симметричным. __LEFT (OUTER) JOIN__ Производит выбор всех записей первой таблицы и соответствующих им записей второй таблицы. Если записи во второй таблице не найдены, то вместо них подставляется пустой результат (`NULL`). Порядок таблиц для оператора важен, поскольку оператор не является симметричным. __RIGHT (OUTER) JOIN__ `LEFT JOIN` с операндами, расставленными в обратном порядке. Порядок таблиц для оператора важен, поскольку оператор не является симметричным. __FULL (OUTER) JOIN__ Результатом объединения таблиц являются все записи, которые присутствуют в таблицах. Порядок таблиц для оператора не важен, поскольку оператор является симметричным. __CROSS JOIN (декартово произведение)__ При выборе каждая строка одной таблицы объединяется с каждой строкой второй таблицы, давая тем самым все возможные сочетания строк двух таблиц. Порядок таблиц для оператора не важен, поскольку оператор является симметричным. [к оглавлению](#sql) ## Что лучше использовать `JOIN` или подзапросы? [Junior] Обычно лучше использовать `JOIN`, поскольку в большинстве случаев он более понятен и лучше оптимизируется СУБД (но 100% этого гарантировать нельзя). Так же `JOIN` имеет заметное преимущество над подзапросами в случае, когда список выбора `SELECT` содержит столбцы более чем из одной таблицы. Подзапросы лучше использовать в случаях, когда нужно вычислять агрегатные значения и использовать их для сравнений во внешних запросах. [к оглавлению](#sql) ## Для чего используется оператор `HAVING`? [Junior] `HAVING` используется для фильтрации результата `GROUP BY` по заданным логическим условиям. Оператор `HAVING` может использоваться только вместе с оператором `GROUP BY`. Он не может использоваться в запросах, которые не содержат агрегированные функции. [к оглавлению](#sql) ## В чем различие между операторами `HAVING` и `WHERE`? [Junior] Основное отличие 'WHERE' от 'HAVING' заключается в том, что 'WHERE' сначала выбирает строки, а затем группирует их и вычисляет агрегатные функции (таким образом, она отбирает строки для вычисления агрегатов), тогда как 'HAVING' отбирает строки групп после группировки и вычисления агрегатных функций. Как следствие, предложение 'WHERE' не должно содержать агрегатных функций; не имеет смысла использовать агрегатные функции для определения строк для вычисления агрегатных функций. Предложение 'HAVING', напротив, всегда содержит агрегатные функции. (Строго говоря, вы можете написать предложение 'HAVING', не используя агрегаты, но это редко бывает полезно. То же самое условие может работать более эффективно на стадии 'WHERE'.) [к оглавлению](#sql) ## Для чего используется оператор `ORDER BY`? [Junior] __ORDER BY__ упорядочивает вывод запроса согласно значениям в том или ином количестве выбранных столбцов. Многочисленные столбцы упорядочиваются один внутри другого. Возможно определять возрастание `ASC` или убывание `DESC` для каждого столбца. По умолчанию установлено - возрастание. [к оглавлению](#sql) ## Для чего используется оператор `GROUP BY`? [Junior] `GROUP BY` используется для агрегации записей результата по заданным признакам-атрибутам. [к оглавлению](#sql) ## Как `GROUP BY` обрабатывает значение `NULL`? [Junior] При использовании `GROUP BY` все значения `NULL` считаются равными. + Если столбец, указанный в операторе GROUP BY, содержит значение NULL, то все строки с таким значением будут группироваться вместе и будут отображаться в результирующей таблице как одна группа с значением NULL. + Если столбец, не указанный в операторе GROUP BY, содержит значение NULL, то значения NULL будут сгруппированы вместе, но будут исключены из результирующей таблицы. Это происходит потому, что оператор GROUP BY не сгруппирует строки, если один из столбцов имеет значение NULL. [к оглавлению](#sql) ## В чем разница между операторами `GROUP BY` и `DISTINCT`? [Junior] `DISTINCT` указывает, что для вычислений используются только уникальные значения столбца. `NULL` считается как отдельное значение. `GROUP BY` создает отдельную группу для всех возможных значений (включая значение `NULL`). Если нужно удалить только дубликаты лучше использовать `DISTINCT`, `GROUP BY` лучше использовать для определения групп записей, к которым могут применяться агрегатные функции. [к оглавлению](#sql) ## Перечислите основные агрегатные функции. [Junior] __Агрегатных функции__ - функции, которые берут группы значений и сводят их к одиночному значению. SQL предоставляет несколько агрегатных функций: `COUNT` - производит подсчет записей, удовлетворяющих условию запроса; `SUM` - вычисляет арифметическую сумму всех значений колонки; `AVG` - вычисляет среднее арифметическое всех значений; `MAX` - определяет наибольшее из всех выбранных значений; `MIN` - определяет наименьшее из всех выбранных значений. [к оглавлению](#sql) ## В чем разница между `COUNT(*)` и `COUNT({column})`? [Junior] `COUNT (*)` подсчитывает количество записей в таблице, не игнорируя значение NULL, поскольку эта функция оперирует записями, а не столбцами. `COUNT ({column})` подсчитывает количество значений в `{column}`. При подсчете количества значений столбца эта форма функции `COUNT` не принимает во внимание значение `NULL`. [к оглавлению](#sql) ## Что делает оператор `EXISTS`? [Middle+] `EXISTS` берет подзапрос, как аргумент, и оценивает его как `TRUE`, если подзапрос возвращает какие-либо записи и `FALSE`, если нет. Оператор EXISTS в SQL используется для проверки наличия хотя бы одной записи, соответствующей подзапросу. Он возвращает значение TRUE, если подзапрос возвращает хотя бы одну запись, и FALSE в противном случае. Оператор EXISTS может использоваться в качестве фильтра в основном запросе, чтобы выбрать только те строки, для которых существуют связанные записи в другой таблице. Важно отметить, что оператор EXISTS возвращает TRUE или FALSE, но не возвращает реальные данные из подзапроса. Он просто проверяет наличие записей в подзапросе и используется для фильтрации результатов основного запроса. [к оглавлению](#sql) ## Для чего используются операторы `IN`, `BETWEEN`, `LIKE`? [Junior] `IN` - определяет набор значений. ```sql SELECT * FROM Persons WHERE name IN ('Ivan','Petr','Pavel'); ``` `BETWEEN` определяет диапазон значений. В отличие от `IN`, `BETWEEN` чувствителен к порядку, и первое значение в предложении должно быть первым по алфавитному или числовому порядку. ```sql SELECT * FROM Persons WHERE age BETWEEN 20 AND 25; ``` `LIKE` применим только к полям типа `CHAR` или `VARCHAR`, с которыми он используется чтобы находить подстроки. В качестве условия используются _символы шаблонизации (wildkards_) - специальные символы, которые могут соответствовать чему-нибудь: + `_` замещает любой одиночный символ. Например, `'b_t'` будет соответствовать словам `'bat'` или `'bit'`, но не будет соответствовать `'brat'`. + `%` замещает последовательность любого числа символов. Например `'%p%t'` будет соответствовать словам `'put'`, `'posit'`, или `'opt'`, но не `'spite'`. ```sql SELECT * FROM UNIVERSITY WHERE NAME LIKE '%o'; ``` [к оглавлению](#sql) ## Для чего применяется ключевое слово `UNION`? [Middle+] В языке SQL ключевое слово `UNION` применяется для объединения результатов двух SQL-запросов в единую таблицу, состоящую из схожих записей. Оба запроса должны возвращать одинаковое число столбцов и совместимые типы данных в соответствующих столбцах. Если столбцы в разных запросах имеют разное имя, можно использовать оператор `AS` для переименования столбцов в обоих запросах. Необходимо отметить, что `UNION` сам по себе не гарантирует порядок записей. Записи из второго запроса могут оказаться в начале, в конце или вообще перемешаться с записями из первого запроса. В случаях, когда требуется определенный порядок, необходимо использовать `ORDER BY`. [к оглавлению](#sql) ## Какие ограничения на целостность данных существуют в SQL? [Junior] `PRIMARY KEY` - набор полей (1 или более), значения которых образуют уникальную комбинацию и используются для однозначной идентификации записи в таблице. Для таблицы может быть создано только одно такое ограничение. Данное ограничение используется для обеспечения целостности сущности, которая описана таблицей. `CHECK` используется для ограничения множества значений, которые могут быть помещены в данный столбец. Это ограничение используется для обеспечения целостности предметной области, которую описывают таблицы в базе. `UNIQUE` обеспечивает отсутствие дубликатов в столбце или наборе столбцов. `FOREIGN KEY` защищает от действий, которые могут нарушить связи между таблицами. `FOREIGN KEY` в одной таблице указывает на `PRIMARY KEY` в другой. Поэтому данное ограничение нацелено на то, чтобы не было записей `FOREIGN KEY`, которым не отвечают записи `PRIMARY KEY`. `NOT NULL` - это ограничение, которое гарантирует, что столбец не может содержать значение `NULL`. Если столбец имеет ограничение `NOT NULL`, то в этот столбец должно быть введено значение при вставке новой записи. [к оглавлению](#sql) ## Какие отличия между ограничениями `PRIMARY` и `UNIQUE`? [Junior] По умолчанию ограничение `PRIMARY` создает кластерный индекс на столбце, а `UNIQUE` - некластерный. Другим отличием является то, что `PRIMARY` не разрешает `NULL` записей, в то время как `UNIQUE` разрешает одну (а в некоторых СУБД несколько) `NULL` запись. [к оглавлению](#sql) ## Может ли значение в столбце, на который наложено ограничение `FOREIGN KEY`, равняться `NULL`? [Junior] Может, если на данный столбец не наложено ограничение `NOT NULL`. [к оглавлению](#sql) ## Как создать индекс? [Junior] Индекс можно создать либо с помощью выражения `CREATE INDEX`: ```sql CREATE INDEX index_name ON table_name (column_name) ``` либо указав ограничение целостности в виде уникального `UNIQUE` или первичного `PRIMARY` ключа в операторе создания таблицы `CREATE TABLE`. [к оглавлению](#sql) ## Что делает оператор `MERGE`? [Middle+] `MERGE` позволяет осуществить слияние данных одной таблицы с данными другой таблицы. При слиянии таблиц проверяется условие, и если оно истинно, то выполняется `UPDATE`, а если нет - `INSERT`. При этом изменять поля таблицы в секции `UPDATE`, по которым идет связывание двух таблиц, нельзя. Он выполняет операцию обновления (`UPDATE`), вставки (`INSERT`) и/или удаления (`DELETE`) строк в таблице-назначении на основе данных в таблице-источнике. Оператор MERGE позволяет обновлять строки в целевой таблице, которые соответствуют определенному условию, а также вставлять новые строки, которые отсутствуют в целевой таблице, но есть в таблице-источнике. Если данные в таблице-источнике не соответствуют никаким строкам в целевой таблице, то эти данные не будут вставлены. Синтаксис оператора MERGE выглядит следующим образом: ```sql MERGE INTO target_table USING source_table ON merge_condition WHEN MATCHED THEN UPDATE SET column1 = value1, column2 = value2, ... WHEN NOT MATCHED THEN INSERT (column1, column2, ...) VALUES (value1, value2, ...) WHEN NOT MATCHED BY SOURCE THEN DELETE; ``` где target_table - название целевой таблицы, source_table - название таблицы-источника, merge_condition - условие сопоставления для слияния таблиц, `UPDATE SET` - столбцы, которые необходимо обновить, `INSERT` - столбцы, которые необходимо вставить, `DELETE` - удаление строк, которые не существуют в таблице-источнике. [к оглавлению](#sql) ## В чем отличие между операторами `DELETE` и `TRUNCATE`? [Junior] `DELETE` - оператор DML, удаляет записи из таблицы, которые удовлетворяют критерию `WHERE` при этом задействуются триггеры, ограничения и т.д. Оператор `DELETE` не сбрасывает идентификаторы строк и не сокращает размер таблицы, поэтому удаленные строки могут быть восстановлены, если это необходимо. `TRUNCATE` - DDL оператор (удаляет таблицу и создает ее заново. Причем если на эту таблицу есть ссылки `FOREGIN KEY` или таблица используется в репликации, то пересоздать такую таблицу не получится). Оператор `TRUNCATE` удаляет все строки из таблицы без использования условий `WHERE`. Он сбрасывает идентификаторы строк и освобождает пространство в таблице, что может существенно сократить размер таблицы. Оператор `TRUNCATE` нельзя использовать для удаления отдельных строк или групп строк, а также он не может быть отменен. Важно отметить, что при использовании оператора `TRUNCATE` все данные в таблице будут удалены без возможности восстановления. Поэтому перед использованием этого оператора необходимо убедиться, что все необходимые данные были сохранены. [к оглавлению](#sql) ## Что такое _«хранимая процедура»_? [Junior] __Хранимая процедура__ — объект базы данных, представляющий собой набор SQL-инструкций, который хранится на сервере. Хранимые процедуры очень похожи на обыкновенные процедуры языков высокого уровня, у них могут быть входные и выходные параметры и локальные переменные, в них могут производиться числовые вычисления и операции над символьными данными, результаты которых могут присваиваться переменным и параметрам. В хранимых процедурах могут выполняться стандартные операции с базами данных (как DDL, так и DML). Кроме того, в хранимых процедурах возможны циклы и ветвления, то есть в них могут использоваться инструкции управления процессом исполнения. Хранимые процедуры позволяют повысить производительность, расширяют возможности программирования и поддерживают функции безопасности данных. В большинстве СУБД при первом запуске хранимой процедуры она компилируется (выполняется синтаксический анализ и генерируется план доступа к данным) и в дальнейшем её обработка осуществляется быстрее. Хранимые процедуры могут принимать параметры и возвращать результаты, что позволяет повторно использовать код в разных частях приложения. Они могут использоваться для выполнения сложных операций с базой данных, таких как создание отчетов, обновление и проверка целостности данных, а также для ускорения выполнения запросов. Создание хранимой процедуры в SQL можно выполнить с помощью команды `CREATE PROCEDURE`. Синтаксис команды выглядит следующим образом: ```sql CREATE PROCEDURE procedure_name [ @parameter1 datatype = default_value1, @parameter2 datatype = default_value2 ] AS BEGIN -- тело процедуры END ``` где procedure_name - название хранимой процедуры, @parameter1, @parameter2 - параметры процедуры, datatype - тип данных параметров, default_value1, default_value2 - значения параметров по умолчанию. Например, чтобы создать хранимую процедуру, которая выбирает список клиентов из таблицы "customers" по географическому региону, можно использовать следующий запрос: ```sql CREATE PROCEDURE sp_select_customers_by_region @region varchar(50) AS BEGIN SELECT * FROM customers WHERE region = @region; END ``` В этой хранимой процедуре есть один параметр @region, который определяет географический регион, по которому нужно выбрать клиентов. Хранимая процедура выполняет запрос SELECT и возвращает список клиентов, соответствующих заданному региону. Для работы с хранимыми процедурами есть свой класс `CallableStatwment`, синтаксис: ```sql CallableStatement callableStatement = connection.prepareCall("{call <имя_процедуры>(?)}"); ``` [к оглавлению](#sql) ## Что такое _«триггер»_? [Junior] __Триггер (trigger)__ — это хранимая процедура особого типа, которую пользователь не вызывает непосредственно, а исполнение которой обусловлено действием по модификации данных: добавлением, удалением или изменением данных в заданной таблице реляционной базы данных. Триггеры применяются для обеспечения целостности данных и реализации сложной бизнес-логики. Триггер запускается сервером автоматически и все производимые им модификации данных рассматриваются как выполняемые в транзакции, в которой выполнено действие, вызвавшее срабатывание триггера. Соответственно, в случае обнаружения ошибки или нарушения целостности данных может произойти откат этой транзакции. Момент запуска триггера определяется с помощью ключевых слов `BEFORE` (триггер запускается до выполнения связанного с ним события) или `AFTER` (после события). В случае, если триггер вызывается до события, он может внести изменения в модифицируемую событием запись. Кроме того, триггеры могут быть привязаны не к таблице, а к представлению (VIEW). В этом случае с их помощью реализуется механизм «обновляемого представления». В этом случае ключевые слова `BEFORE` и `AFTER` влияют лишь на последовательность вызова триггеров, так как собственно событие (удаление, вставка или обновление) не происходит. Синтаксис команды CREATE TRIGGER выглядит следующим образом: ```sql CREATE TRIGGER trigger_name ON table_name AFTER INSERT, UPDATE, DELETE AS BEGIN -- тело триггера END ``` где trigger_name - название триггера, table_name - название таблицы, на которую создается триггер, `AFTER INSERT, UPDATE, DELETE` - тип действия, при котором триггер должен быть выполнен, `BEGIN...END` - тело триггера, которое содержит код, который будет выполнен при выполнении определенных действий. Например, чтобы создать триггер, который будет обновлять дату изменения записи в таблице "employees" при каждом обновлении записи, можно использовать следующий запрос: ```sql CREATE TRIGGER tr_update_employee ON employees AFTER UPDATE AS BEGIN UPDATE employees SET modified_date = GETDATE() WHERE employee_id IN (SELECT employee_id FROM inserted); END ``` Этот триггер будет выполнен после каждого обновления записи в таблице "employees". Он обновит столбец "modified_date" в каждой обновленной записи на текущую дату и время. [к оглавлению](#sql) ## Что такое _«курсор»_? [Middle+] __Курсор__ — это объект базы данных, который позволяет приложениям работать с записями «по-одной», а не сразу с множеством, как это делается в обычных SQL командах. Порядок работы с курсором такой: + Определить курсор (`DECLARE`) + Открыть курсор (`OPEN`) + Получить запись из курсора (`FETCH`) + Обработать запись... + Закрыть курсор (`CLOSE`) + Удалить ссылку курсора (`DEALLOCATE`). Когда удаляется последняя ссылка курсора, SQL освобождает структуры данных, составляющие курсор. __Курсор__ (cursor) в SQL - это механизм, который позволяет перемещаться по результатам выполнения запроса и получать доступ к отдельным строкам в результате. Курсоры могут быть использованы для манипулирования и обработки данных в процессе выполнения запроса. Курсор создается на основе запроса `SELECT` и может быть использован для выполнения действий над каждой строкой в результате. Курсоры могут быть объявлены и открыты в SQL с помощью команды `DECLARE CURSOR`. Синтаксис команды DECLARE CURSOR выглядит следующим образом: ```sql DECLARE cursor_name CURSOR FOR SELECT column1, column2, ... FROM table_name WHERE condition; ``` где cursor_name - название курсора, column1, column2, ... - список столбцов таблицы, которые должны быть доступны в курсоре, table_name - название таблицы, condition - условие для выборки строк из таблицы. Например, чтобы объявить курсор для выборки списка имен клиентов из таблицы "customers", можно использовать следующий запрос: ```sql DECLARE cur_customers CURSOR FOR SELECT customer_name FROM customers; ``` После объявления курсора, его нужно открыть с помощью команды `OPEN`: ```sql OPEN cur_customers; ``` Затем можно перемещаться по результатам запроса и получать доступ к отдельным строкам с помощью команды `FETCH`: ```sql FETCH NEXT FROM cur_customers; ``` Когда обработка курсора завершена, его нужно закрыть с помощью команды `CLOSE`: ```sql CLOSE cur_customers; ``` Курсоры являются мощным механизмом, который позволяет манипулировать и обрабатывать данные в процессе выполнения запроса, но они также могут снизить производительность и затруднить поддержку кода. Поэтому использование курсоров следует ограничивать только в тех случаях, когда они действительно необходимы. [к оглавлению](#sql) ## Опишите разницу типов данных `DATETIME` и `TIMESTAMP`. [Junior] `DATETIME` предназначен для хранения целого числа: `YYYYMMDDHHMMSS`. И это время не зависит от временной зоны, настроенной на сервере. Размер: 8 байт `TIMESTAMP` хранит значение равное количеству секунд, прошедших с полуночи 1 января 1970 года по усреднённому времени Гринвича. При получении из базы отображается с учётом часового пояса. Размер: 4 байта [к оглавлению](#sql) ## Для каких числовых типов недопустимо использовать операции сложения/вычитания? [Junior] В качестве операндов операций сложения и вычитания нельзя использовать числовой тип `BIT`. [к оглавлению](#sql) ## Какое назначение у операторов `PIVOT` и `UNPIVOT` в Transact-SQL? [Middle+] `PIVOT` и `UNPIVOT` являются нестандартными реляционными операторами, которые поддерживаются Transact-SQL. Оператор `PIVOT` разворачивает возвращающее табличное значение выражение, преобразуя уникальные значения одного столбца выражения в несколько выходных столбцов, а также, в случае необходимости, объединяет оставшиеся повторяющиеся значения столбца и отображает их в выходных данных. Оператор `UNPIVOT` производит действия, обратные `PIVOT`, преобразуя столбцы возвращающего табличное значение выражения в значения столбца. Операторы `PIVOT` и `UNPIVOT` в Transact-SQL используются для преобразования данных из строк в столбцы (пивотирования) и из столбцов в строки (разворота). Эти операторы позволяют выполнить динамическую трансформацию данных, что может быть полезно при анализе больших объемов данных. Оператор `PIVOT` преобразует строки в столбцы на основе значения, хранящегося в столбце таблицы. Оператор `UNPIVOT` преобразует столбцы в строки на основе значения, хранящегося в каждом столбце. [к оглавлению](#sql) ## Расскажите об основных функциях ранжирования в Transact-SQL. [Junior] __Ранжирующие функции__ - это функции, которые возвращают значение для каждой записи группы в результирующем наборе данных. На практике они могут быть использованы, например, для простой нумерации списка, составления рейтинга или постраничной навигации. __Функции ранжирования__ (ranking functions) в SQL - это функции, которые используются для расчета порядковых номеров строк в результирующем наборе на основе определенного столбца или набора столбцов. Функции ранжирования могут быть полезны для анализа данных и сравнения результатов на основе порядковых номеров строк. Некоторые из основных функций ранжирования в SQL: `ROW_NUMBER()` - функция, которая присваивает каждой строке в результирующем наборе уникальный порядковый номер. Порядковый номер увеличивается на 1 для каждой следующей строки в наборе. `RANK()` - функция, которая присваивает каждой строке в результирующем наборе порядковый номер, который соответствует ее рангу. Если несколько строк имеют одно и то же значение, то им будет присвоен один и тот же ранг, и следующий порядковый номер будет пропущен. `DENSE_RANK()` - функция, которая присваивает каждой строке в результирующем наборе порядковый номер, который соответствует ее плотному рангу. Если несколько строк имеют одно и то же значение, то им будет присвоен один и тот же плотный ранг, и следующий порядковый номер не будет пропущен. `NTILE()` - функция, которая разбивает результирующий набор на равные группы и присваивает каждой строке номер группы, к которой она относится. Количество групп определяется аргументом функции. К примеру, у нас имеется набор данных следующего вида: ![ ](images/SQL/image.png) `ROW_NUMBER` – функция нумерации в Transact-SQL, которая возвращает просто номер записи. Например, запрос ```sql SELECT Studentname, Subject, Marks, ROW_NUMBER() OVER(ORDER BY Marks) RowNumber FROM ExamResult; ``` Вернёт набор данных следующего вида: ![ ](images/SQL/row_number-sql-rank-function.png) А запрос вида ```sql SELECT Studentname, Subject, Marks, ROW_NUMBER() OVER(ORDER BY Marks desc) RowNumber FROM ExamResult; ``` Вернёт набор ![ ](images/SQL/row_number-example.png) `RANK` возвращает ранг каждой записи. В данном случае, в отличие от `ROW_NUMBER`, идет уже анализ значений и в случае нахождения одинаковых возвращает одинаковый ранг с пропуском следующего. Например: ```sql SELECT Studentname, Subject, Marks, RANK() OVER(PARTITION BY Studentname ORDER BY Marks DESC) Rank FROM ExamResult ORDER BY Studentname, Rank; ``` Результат: ![ ](images/SQL/ranksql-rank-function.png) Ещё пример: ```sql SELECT Studentname, Subject, Marks, RANK() OVER(ORDER BY Marks DESC) Rank FROM ExamResult ORDER BY Rank; ``` Результат: ![ ](images/SQL/output-of-rank-function-for-similar-values.png) `DENSE_RANK` так же возвращает ранг каждой записи, но в отличие от `RANK` в случае нахождения одинаковых значений возвращает ранг без пропуска следующего. Например: ```sql SELECT Studentname, Subject, Marks, DENSE_RANK() OVER(ORDER BY Marks DESC) Rank FROM ExamResult ORDER BY Rank; ``` Результат: ![ ](images/SQL/dense_ranksql-rank-function.png) Ещё пример: ```sql SELECT Studentname, Subject, Marks, DENSE_RANK() OVER(PARTITION BY Subject ORDER BY Marks DESC) Rank FROM ExamResult ORDER BY Studentname, Rank; ``` Результат: ![ ](images/SQL/output-of-dense_rank-function.png) Ну, и на последок, продемонстрируем разницу между `DENSE_RANK` и `RANK`: ```sql SELECT Studentname, Subject, Marks, RANK() OVER(PARTITION BY StudentName ORDER BY Marks ) Rank FROM ExamResult ORDER BY Studentname, Rank; ``` ```sql SELECT Studentname, Subject, Marks, DENSE_RANK() OVER(PARTITION BY StudentName ORDER BY Marks ) Rank FROM ExamResult ORDER BY Studentname, Rank; ``` ![ ](images/SQL/difference-between-rank-and-dense_rank.png) ![ ](images/SQL/difference-between-rank-and-dense_rank-functio.png) `NTILE` – функция Transact-SQL, которая делит результирующий набор на группы по определенному столбцу. Например: ```sql SELECT *, NTILE(2) OVER( ORDER BY Marks DESC) Rank FROM ExamResult ORDER BY rank; ``` Результат: ![ ](images/SQL/ntilen-sql-rank-function.png) Пример 2: ```sql SELECT *, NTILE(3) OVER( ORDER BY Marks DESC) Rank FROM ExamResult ORDER BY rank; ``` Результат: ![ ](images/SQL/ntilen-function-with-partition.png) Пример 3: ```sql SELECT *, NTILE(2) OVER(PARTITION BY subject ORDER BY Marks DESC) Rank FROM ExamResult ORDER BY subject, rank; ``` Результат: ![ ](images/SQL/output-of-ntilen-function-with-partition.png) [к оглавлению](#sql) ## Для чего используются операторы `INTERSECT`, `EXCEPT` в Transact-SQL? [Middle+] Оператор `EXCEPT` возвращает уникальные записи из левого входного запроса, которые не выводятся правым входным запросом. Оператор `INTERSECT` возвращает уникальные записи, выводимые левым и правым входными запросами. Операторы `INTERSECT` и `EXCEPT` используются для выполнения операций над множествами в SQL. Они могут быть полезны при сравнении двух или более наборов данных. + `INTERSECT` - оператор, который возвращает только те строки, которые присутствуют в обоих результирующих наборах. Другими словами, этот оператор возвращает пересечение двух множеств. + `EXCEPT` - оператор, который возвращает только те строки, которые присутствуют в первом результирующем наборе и отсутствуют во втором. Другими словами, этот оператор возвращает разность двух множеств. [к оглавлению](#sql) ## Напишите запрос... [Middle+] ```sql CREATE TABLE table ( id BIGINT(20) NOT NULL AUTO_INCREMENT, created TIMESTAMP NOT NULL DEFAULT 0, PRIMARY KEY (id) ); ``` Требуется написать запрос, который вернет максимальное значение `id` и значение `created` для этого `id`: ```sql SELECT id, created FROM table where id = (SELECT MAX(id) FROM table); ``` --- ```sql CREATE TABLE track_downloads ( download_id BIGINT(20) NOT NULL AUTO_INCREMENT, track_id INT NOT NULL, user_id BIGINT(20) NOT NULL, download_time TIMESTAMP NOT NULL DEFAULT 0, PRIMARY KEY (download_id) ); ``` Напишите SQL-запрос, возвращающий все пары `(download_count, user_count)`, удовлетворяющие следующему условию: `user_count` — общее ненулевое число пользователей, сделавших ровно `download_count` скачиваний `19 ноября 2010 года`: ```sql SELECT DISTINCT download_count, COUNT(*) AS user_count FROM ( SELECT COUNT(*) AS download_count FROM track_downloads WHERE download_time="2010-11-19" GROUP BY user_id) AS download_count GROUP BY download_count; ``` [к оглавлению](#sql) # Источники + [Википедия](https://ru.wikipedia.org/wiki/SQL) + [Quizful](http://www.quizful.net/interview/sql) [Вопросы для собеседования](README.md)