Как видно из приведенных примеров, при обновлении базы данных в многопользовательском режиме существует возможность нарушения ее целостности. Чтобы исключить такую возможность, в SQL используется механизм транзакций. Помимо того, что реляционная СУБД обязана выполнять транзакции по принципу “либо все, либо ничего”, она имеет и другое обязательство по отношению к транзакциям:
“В ходе выполнения транзакции пользователь видит полностью непротиворечивую базу данных. Он не должен видеть промежуточные результаты транзакций других пользователей, и даже завершение таких транзакций не должно отражаться на данных, которые пользователь видит в течение транзакции”.
Таким образом, в реляционной базе данных транзакции играют ключевую роль как при восстановлении после сбоев, так и при параллельной работе нескольких пользователей. Приведенное выше правило можно сформулировать иначе, пользуясь концепцией параллельного выполнения транзакций:
“Когда две транзакции, A и B, выполняются параллельно, СУБД гарантирует, что результаты их выполнения будут точно такими же, как если бы вначале выполнялась транзакция А, а затем транзакция B; или вначале выполнялась транзакция B, а затем транзакция А”.
Данная концепция называется сериализацией транзакций. На практике это означает, что каждый пользователь может работать с базой данных так, как если бы не было других пользователей, работающих параллельно.
Однако тот факт, что СУБД изолирует пользователя от действий других пользователей, не означает, что о них можно забыть. Совсем наоборот. Поскольку другим пользователям также требуется обновлять базу данных параллельно с вами, ваши транзакции должны быть как можно более простыми и короткими, чтобы объем работы, выполняемой всеми, был больше.
Предположим, например, что вы запускаете программу, последовательно выполняющую три больших запроса на выборку. Так как программа не обновляет базу данных, может показаться, что нет необходимости использовать инструкцию COMMIT. Однако на самом деле программа должна выполнять эту инструкцию после каждого запроса, так как транзакция начинается автоматически вместе с первой инструкцией SQL в программе. Без инструкции COMMIT транзакция будет продолжаться до окончания программы. Кроме того, СУБД гарантирует, что данные, извлекаемые в течение транзакции, будут непротиворечивыми, и не будут зависеть от транзакции других пользователей. Это означает, что если ваша программа извлекла из базы данных определенную строку, то ни один пользователь, кроме вас, не сможет изменить эту строку до окончания вашей транзакции. Так происходит из-за того, что позднее в этой же транзакции вы можете снова извлечь ту же строку, а СУБД должна гарантировать, что в этой строке будут содержаться те же данные, что и при первой выборке. Поэтому, по мере того как ваша программа будет последовательно выполнять три запроса, другие пользователи не смогут изменять все большее количество данных.
Поэтому при написании программ для промышленных реляционных баз данных всегда необходимо учитывать транзакции. Транзакции должны быть как можно короче, т. е. использование инструкции COMMIT раньше и чаще – это хороший тон для всех, кто работает с программным SQL.