Что такое первичный ключ?

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

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

Например, рассмотрим таблицу заказов, состоящую из столбцов:

  • order_id (уникальный номер заказа — первичный ключ)
  • order_date (дата совершения заказа)
  • order_customer (покупатель, сделавший покупку)
  • order_sum (сумма заказа).

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


Что такое внешний ключ?

Когда поле таблицы ссылается на другое поле в другой таблице, оно называется внешним ключом. Поле, на которое он ссылается, является первичным ключом.

Например, в таблице заказов, очевидно, должно быть поле order_customer, которое определяет покупателя сделавшего покупку. Мы могли бы в это поле записывать просто ФИО покупателя. Но как быть, если нам нужно выбрать все заказы, сделанные конкретным покупателем? Велика вероятность, что в таблице могут находится люди, у которых полностью совпадает ФИО.

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

Например, в таблице заказов будет строка, где в поле order_customer содержится внешний ключ — 3246. Далее в таблице customers необходимо отыскать строку с первичным ключом, равным 3246, в этой строке и будут содержаться все данные покупателя, включая и ФИО.


Что такое нормализация БД?

Процесс приведения базы данных к виду, в котором она будет соответствовать правилам нормальных форм, называется нормализацией базы данных.

Нормализация базы данных сводит к минимуму количество избыточной информации. Ее целью является сохранять данные только один раз, но в нужном месте.

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

Первоначально доктором Эдгаром Коддом были определены только 3 нормальные формы. Дальнейшая разработка реляционной теории привела к появлению еще нескольких форм и на данный момент их насчитывается 6.

На практике соответствие базы данных правилам 3-ей нормальной формы вполне достаточно.


Что такое денормализация БД? Для чего она нужна?

Денормализация — это процесс осознанного приведения базы данных к виду, в котором она не будет соответствовать правилам нормализации. Обычно это необходимо для повышения производительности и скорости извлечения данных, за счет увеличения избыточности данных.

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

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

Прежде чем приступать к денормализации, необходимо убедится, что ожидаемые результаты оправдывают издержки, с которыми придется столкнуться.


Что такое кластерный и некластерный индекс?

Индексы делятся на 2 вида — кластерный и некластерный.

Обычно СУБД хранит строки в том порядке, в котором они добавляются в таблицу.

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

Некластерный индекс хранит указатели на строки в таблице. Такой способ хранения подразумевает после обращения к индексу обращение к самой таблице для получения строк.

Наиболее часто используемые типы индексов:
  • B-деревья
  • Хеши
  • R-деревья
  • Полнотекстовые

В отличие от некластерного индекса, таблица может иметь только один кластерный индекс.


Какие типы соединений (join) таблиц существуют? В чем их разница?

Существуют следующие типы соединений таблиц:
  • INNER JOIN
  • FULL OUTER JOIN
  • LEFT OUTER JOIN
  • RIGHT OUTER JOIN
  • CROSS JOIN

Опишем каждый тип соединений на примере запросов. Рассмотрим 2 таблицы A и B:

A:
IDNAME
1Вася
2Петя
3Федя
4Маша

B:
IDNAME
1Иван
2Вася
3Лена
4Федя

Посмотрим на результаты следующих запросов:

SELECT * FROM A INNER JOIN B ON A.NAME = B.NAME
IDNAMEIDNAME
1Вася2Вася
3Федя4Федя

SELECT * FROM A FULL OUTER JOIN B ON A.NAME = B.NAME
IDNAMEIDNAME
1Вася2Вася
2Петяnullnull
3Федя4Федя
4Машаnullnull
nullnull1Иван
nullnull3Лена

SELECT * FROM A LEFT OUTER JOIN B ON A.NAME = B.NAME
IDNAMEIDNAME
1Вася2Вася
2Петяnullnull
3Федя4Федя
4Машаnullnull

SELECT * FROM A RIGHT OUTER JOIN B ON A.NAME = B.NAME
IDNAMEIDNAME
3Федя4Федя
nullnul3Лена
1Вася2Вася
nullnull1Ivan

SELECT * FROM A CROSS JOIN B
4Masha4Fedya
3Fedya4Fedya
2Petya4Fedya
1Vasya4Fedya
4Masha3Lena
3Fedya3Lena
2Petya3Lena
1Vasya3Lena
4Masha2Vasya
3Fedya2Vasya
2Petya2Vasya
1Vasya2Vasya
4Masha1Ivan
3Fedya1Ivan
2Petya1Ivan
1Vasya1Ivan

CROSS JOIN является полным декартовым произведением, т.е. возвращает все возможные сочетания записей таблиц A и B. В стандартном SQL CROSS JOIN выполняется без условия ON, однако, например, в MySQL этого ограничения нет и CROSS JOIN является аналогом INNER JOIN.


Что такое SQL курсор?

Иногда бывает нужно в результирующем наборе SQL-запроса обрабатывать строки по одной. Для этого обычно используются циклы и объект, называемый курсором.

SQL курсор можно представить как указатель в результирующем наборе. Его можно перемещать, обрабатывая каждую строку, пока не закончится результирующий набор.


Опишите шаги по созданию и использованию курсора?

Продемонстрируем использование курсора на простом примере:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
CURSOR cselectitem
IS
	SELECT id, name FROM items; /* Объявляем курсор, привязывая к нему SQL — запрос. */
BEGIN
	OPEN cselectitem; /* Далее перед использованием курсор необходимо открыть */
	LOOP
		FETCH cselectitem
			INTO item_id, item_name; /* В цикле вытаскиваем каждую строку, 
                                                    используя конструкцию FETCH, и  значения 
                                                    полей выборки записываем в переменные, 
                                                    которые должны быть заранее объявлены */
		EXIT WHEN cselectitem%NOTFOUND; /*  Выходим из цикла, когда достигнут конец 
                                                    результирующего набора */
	END LOOP;
 
	CLOSE cselectitem; /* Закрываем курсор */
END

Что такое транзакция?

По определению, транзакция — это логическая единица работы.

Транзакция предполагает, что все, входящие в нее SQL-запросы будут обязательно выполнены, причем одновременно.

Начать транзакцию можно при помощи команды BEGIN TRANSACTION. Завершить можно либо сохранив изменения в базе данных при помощи команды COMMIT, либо удалив все сделанные транзакцией изменения командой ROLLBACK

Приведем пример транзакции, которая снимает деньги с одного пользовательского счета и переводит их на другой:
1
2
3
4
5
6
BEGIN TRANSACTION
UPDATE user_account SET balance=balance-100 WHERE account_id=1 /* Снимаем деньги со счета,
                                                                  ID которого равен 1 */
UPDATE user_account SET balance=balance+100 WHERE account_id=2 /* И переводим эти деньги на 
                                                                  счет, ID которого равен 2 */
COMMIT

Понятно какие-бы проблемы могли бы возникнуть, если бы одна часть этой транзакции была бы выполнена успешно, а другая нет. Однако использование транзакций не допускает подобных ситуаций.

При работе с транзакциями возможны 2 варианта:
  • результаты выполнения всех запросов, входящих в транзакцию, будут применены к базе данных
  • все внесенные изменения в базу данных будут отменены (откатаны).

Пока результаты выполнения запросов, входящих в транзакцию не будет применены к базе данных при помощи оператора COMMIT, они остаются невидимы для других пользователей базы данных.


Что такое блокировка?

Существует 2 вида взаимодействий с базой данных, которые требуют блокировки 2-х разных типов:
  • Разделяемая блокировка
    Когда пользователь считывает данные, его совершенно не волнует кто эти данные считывает еще.
    При подобных операциях на данные накладывается разделяемая блокировка, которая говорит о том, что любая транзакция может выполнять эту же операцию с данными, т.к. она не изменяет их и не сможет привести ни к каким побочным эффектам.
  • Монопольная блокировка
    В случае, если выполняемая операция изменяет данные, на них накладывается монопольная блокировка, которая позволяет изменять данные только одному пользователю.
    СУБД не может устанавливать монопольную блокировку на данные, на которые уже установлена разделяемая блокировка. Такое ограничение имеет смысл как для пользователя, который записывает данные, так и для пользователя, который их считывает.

Что такое deadlock?

Взаимная блокировка (или deadlock) — это ситуация, при которой возник конфликт между несколькими операциями записи.

Рассмотрим пример:
Операция 1Операция 2
Необходимо изменить данные A и B
Происходит изменение данных A 
 Происходит изменение данных B
 Ожидание снятия блокировки с данных A
Ожидание снятия блокировки с данных B 

В описанном примере обе операции могут снять блокировку с данных только после полного завершения своей работы. Но для завершения работы операции 1 нужно снятие блокировки с данных B, а для операции 2 снятие блокировки с данных A. Обе операции вступают в бесконечное ожидание снятия блокировки с данных.

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

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

Другими словами команда COMMIT должна выполняться сразу, как только операция записи станет возможна, для сокращения времени, в течение которого данные заблокированы.


Что такое триггер? (Какие типы триггеров вы знаете?)

Триггеры — это части кода, запускаемые автоматически и основанные на каком-либо действии или событии в таблице базы данных.

Триггеры могут применять в таблице во время выполнения операторов INSERT, UPDATE или DELETE, а затем работать или перед, или после этих действий.

Некоторые СУБД также позволяют работать триггерам на уровне оператора или на уровне каждой строки данных во время ее изменения.


В чем разница между WHERE и HAVING?

Конструкцию HAVING можно использовать только при наличии конструкции GROUP BY. Она действует по отношению к результатам конструкции GROUP BY аналогично тому, как конструкция WHERE отсеивает строки, возвращаемые оператором SELECT, в соответствии с указанным условием.


Что такое подзапрос (sub-query)?

Подзапрос — это запрос, вложенный в другой запрос.

Несколько примеров подзапросов:
1
2
3
4
SELECT * FROM tableA WHERE id IN (SELECT id FROM tableB)
SELECT * FROM tableA WHERE id > (SELECT AVG(id) FROM tableA)
SELECT tA.* FROM (SELECT * FROM tableA WHERE col IS NOT NULL) tA, tableB tB 
WHERE tA.id=tB.id