До настоящего момента мы рассматривали только локальные базы данных, не затрагивая многопользовательского доступа. Целью данного занятия является обсуждение особенностей, присущих удаленным базам данных.
Напомним, что при использовании технологии «клиент-сервер» база данных располагается на удаленном компьютере (сервере) и называется удаленной базой данных.
Приложение, обеспечивающее доступ к базе данных с компьютера пользователя, называется приложением-клиентом.
Взаимодействие клиента и сервера можно описать двумя фразами (рис. 9.1):
- клиент формирует SQL-запрос серверу, на котором расположена база данных, и отправляет его по сети;
- сервер выполняет запрос и возвращает клиенту запрашиваемые данные.
Преимущества такого метода работы очевидны:
- обработка запроса происходит на сервере, клиент же получает только необходимые данные;
- поскольку посылаются только необходимые данные, снижается сетевой трафик;
- приложение-клиент не может напрямую обратиться к файлам, расположенным на сервере, — таким образом обеспечивается высокая степень защиты данных;
- управление базой данных производит сервер, поэтому код приложения-клиента значительно уменьшается и упрощается.
- InterBase;
- Oracle;
- Sybase;
- DB2;
- Microsoft SQL Server.
- осуществление соединения с сервером и отключения от него;
- формулировка запроса и посылка его серверу;
- прием результата выполнения запроса от сервера;
- обработка полученных данных.
- триггеры;
- генераторы;
- хранимые процедуры;
- функции, определяемые пользователем;
- транзакции;
- кэшированные изменения;
- события.
Хранимая процедура — это небольшая программа, которая расположена на сервере базы данных и которую можно вызвать из приложения-клиента.
Хранимые процедуры позволяют ускорить работу с данными на сервере базы данных благодаря следующим преимуществам:
- при использовании хранимых процедур вместо длинного текста SQL-запроса серверу отправляется короткое обращение к хранимой процедуре. Таким образом уменьшается сетевой график;
- хранимая процедура выполняется непосредственно на сервере. В результате скорость доступа к данным не зависит от производительности машины-клиента;
- хранимая процедура, в отличие от SQL-запроса, не фебует предварительной проверки синтаксиса;
- хранимые процедуры являются общими для всех приложений-клиентов и реализуют единые правила работы с базой данных.
Для выполнения хранимой процедуры в Delphi был введен компонент TStoredProc. Рассмотрим его основные свойства.
Свойство DatabaseName типа String указывает на компонент TDatabase, используемый для установления соединения с базой данных. Это свойство аналогично одноименному свойству компонентов ТTable и TQuery.
Свойство StoredProcName типа String определяет хранимую процедуру, которая должна вызываться. Имя хранимой процедуры выбирается н выггядяющрм списке с помощью инспектора объектов.
После выбора хранимой процедуры можно устанавливать два других свойства. Свойство РагатэтипаТРагатзопределяет массив параметров компонента TStoredProc.
Свойство ParamBindMode типа TParamBindMode определяет, как будет установлено соответствие между параметрамикомпонента TStoredProc и параметрами процедуры, и может принимать одно из следующих значений:
- pbByName — соответствие будет установлено по именам, то есть имена параметров компонента TStoredProc и соответствующих параметров процедуры должны совпадать. Данное значение принимается по умолчанию;
- pbByNumber — соответствие будет установлено в порядке перечисления, то есть первый параметр компонентаTStoredProc сопоставлен первому параметру процедуры и т. д.
Выполнение выбранной хранимой процедуры осуществляется с помощью вызовов методов Prepare и ЕхесРгос:
- метод Prepare осуществляет подготовку хранимой процедуры, которая заключается в связывании параметров процедуры п компонента TStoredProc в соответствии с установленным значением свойства ParamBindMode;
- метод ЕхесРгос непосредственно выполняет хранимую процедуру.
Листинг 9.1 иллюстрирует вызов хранимой процедуры из приложения при нажатии кнопки Buttonl.
Листинг 9.1. Вызов хранимой процедуры
Для написания хранимых процедур и триггеров используется специальный язык хранимых процедур. Разные серверы используют разные диалекты. Мы не будем изучать их все, а кратко рассмотрим язык хранимых процедур, использующийся сервером InterBase, который имеет много общего с языком Pascal. Язык хранимых процедур включает в себя операторы для управления ходом вычислительного процесса (ветвления, цикла), а также некоторые функциональные возможности языка SQL.
Хранимая процедура создается оператором
После имени процедуры следует необязательный список входных параметров, с помощью которых из приложения в процедуру могут передаваться исходные данные. Список выходных параметров, посредством которых в приложение возвращаются результаты выполнения процедуры, указывается после слова RETURNS. Каждый параметр описывается своим именем и типом, разделенными пробелом. Между различными параметрами должны стоять запятые.
При использовании параметра в теле процедуры перед его именем необходимо ставить двоеточие.
В текст хранимой процедуры допускается вставлять комментарии. Для вставки комментариев используются комбинации символов /* и */.
Вновь созданную процедуру можно удалить или изменить. Для удаления процедуры служит оператор
DROP PROCEDURE <Имя процедуры>
Для изменения существующей процедуры используется оператор
ALTER PROCEDURE
Его список параметров идентичен списку параметров оператора
CREATE PROCEDURE
Рассмотрим некоторые из основных элементов языка хранимых процедур. Заметим, что все операторы обязательно должны заканчиваться точкой с запятой (кроме составного оператора).
Оператор объявления переменных имеет следующий вид:
Переменные могут быть только того типа, который допускается в InterBase.
Объявленные переменные являются локальными, то есть видимы исключительно внутри процедуры, в которой они были объявлены. Приведем пример объявления переменных:
Оператор присваивания описывается следующим образом: <Имя переменной> <Выражение>;
Перед знаком равенства двоеточие не ставится!
Переменная и выражение должны иметь одинаковый или совместимый тип, иначе возможна ошибка.
Проиллюстрируем сказанное несколькими примерами:
Оператор ветвления имеет вид:
Этот оператор аналогичен оператору ветвления, использующемуся в Delphi. Оператор цикла оформляется аналогично такому же оператору в Delphi:
Оператор выбора записи похож на инструкцию SELECT языка SQL, но дополнен следующим операндом:
Каждое имя после двоеточия указывает переменную или выходной параметр, которому должно быть присвоено значение столбцов строки, полученной в результате выполнения команды SELECT.
Например:
Здесь создается хранимая процедура pSelect, в которой для сотрудников из таблицы MyTable подсчитываются общая сумма заработной платы (по полю Zarplata) и среднее значение по организации. Полученные в результате выполнения процедуры значения будут присвоены выходным параметрам opSum и opSred. Входных параметров процедура не имеет.
Оператор выхода из процедуры служит для досрочного выхода из процедуры и передачи управления вызывающей программе млн процедуре. Оператор выхода представляет собой ключевое слово EXIT
Оператор вызова процедуры применяется для вызова из одной хранимой процедуры другой хранимой процедуры и записывается в общем виде так:
Оператор вызывает хранимую процедуру с указанным именем и параметрами. Приведем пример вызова хранимой процедуры:
Оператор посылки сообщения предназначен для уведомления о событии всех приложений-клиентов, связанных с сервером. Данный оператор имеет следующий вид: Триггер представляет собой процедуру, которая постоянно размещена на сервере базы данных (как и хранимая процедура) и вызывается автоматически при изменении записей базы данных. В отличие от хранимых процедур, триггеры нельзя вызывать из приложений-клиентов, а также передавать им параметры и получать от них результаты. По определению триггер похож на обработчик событий BeforeEdit, AfterEdit, Before-Delete, AfterDelete, Beforelnsert и Afterlnsert. Триггеры в основном используются для программной реализации бизнес-правил. С помощью триггеров накладываются различные ограничения (например, иа значения столбцов). Для создания триггера применяется оператор CREATE TRIGGER, который имеет следующий вид: Операнды ACTIVE и INACTIVE определяют, будет ли триггер активен сразу после его создания. По умолчанию ои будет активен (ACTIVE), то есть при наступлении определенного события триггер будет выполняться. Неактивный триггер (INACTIVE) при наступлении определенного события выполняться не будет. Таким образом, созданный триггер можно включать или выключать. Операнды BEFORE и AFTER указывают, когда будет выполняться триггер: до наступления определенного события (BEFORE) или после (AFTER). Операнды UPDATE, INSERT и DELETE определяют тип события, при возникновении которого будет выполняться триггер. Это такие типы событий, как изменение (UPDATE), добавление (INSERT) и удаление (DELETE) записей. Для каждого события можно создать несколько триггеров, которые будут выполняться (если они активны). Порядок их выполнения определяется операндом POSITION. Триггеры будут срабатывать в порядке возрастания чисел, указанных в данном операнде. Созданный триггер можно изменить или удалить. Удаление триггера осуществляется оператором Изменить уже существующий триггер можно с помощью оператора ALTER TRIGGER, который имеет такой же формат, что и оператор CREATE TRIGGER. Тело триггера программируется так же, как и тело хранимой процедуры. В таблицах InterBase отсутствует автоинкрементный тип. Для обеспечения уникальности значений ключевых столбцов совместно с триггерами используются генераторы. Генератор применяется для создания уникальных целочисленных значений. Генератор можно создать с помощью приведенного ниже оператора: Кроме непосредственного создания генератора, необходимо присвоить ему начальное значение. Для этого используется следующий оператор: Начальное значение — это число, начиная с которого будут генерироваться уникальные значения. К созданному генератору можно обращаться с помощью функции Данная (функция возвращает значение, увеличенное на целочисленный шаг относительно последнего сгенерированного значения. ВНИМАНИЕ: После определения начального значения и шага их изменять нельзя. В противном случае уникальность генерируемых значений может быть нарушена. Приведем пример создания генератора:POSTJVENT <Имя события>;
DROP TRIGGER <Имя триггера>
CREATE GENERATOR <Имя генератора>
SET GENERATOR <Имя генератора> ТО «Начальное значение>
GEN_ID (<Имя генератора>, <Шаг>)
CREATE GENERATOR MyGen: SET GENERATOR MyGen TO
2
:
Здесь создается генератор с именем MyGen, начальное значение которого равно 2. Пример обращения к этому генератору может быть таким:
GENJD(MyGen.
1
):
Все операции, выполняемые приложением-клиентом с данными на SQL-сервере, происходят в виде транзакций.
Транзакцией называется группа из нескольких операции над данными из таблиц удаленной базы данных.
Основным принципом транзакций является «либо все, либо ничего». Если во время выполнения набора действий (транзакции) па каком-то этапе невозможно произвести очередное действие, то нужно выполнить возврат базы данных к начальному состоянию (произвести откат транзакции). Таким образом (при правильном планировании транзакций) обеспечивается целостность базы данных. Далее мы расскажем, как начинать, завершать транзакции п управлять ими с помощью SQL-выражений. А также рассмотрим вопрос об использовании транзакций в приложениях, разработанных в Delphi. Вся приведенная информация относится к InterBase.
Для управления транзакциями в Delphi имеются три основные команды:
- SET TRANSACTION — начинает транзакцию и определяет се поведение;
- COMMIT — сохраняет изменения, внесенные транзакцией, в базе данных и завершает транзакцию;
- ROLLBACK — отменяет изменения, внесенные транзакцией, и завершает транзакцию. Рассмотрим эти команды более подробно.
Команда для запуска транзакции в общем виде выглядит следующим образом:
SET TRANSACTION [Access mode] [Lock Resolution] [Isolation Level] [Table Reservation]
Команда содержит четыре необязательных параметра (перечисленные в квадратных скобках). Если эти параметры опустить, то получившаяся инструкция SET TRANSACTION равносильна выражению
SET TRANSACTION READ WRITE WAIT ISOLATION LEVEL SNAPSHOT
Перечислим параметры команды и их возможные значения.
- Параметр Access mode (тип доступа) определяет тип доступа к данным. Он может принимать одно из двух значении:
- READ ONLY — транзакция может только читать данные н не в состоянии модифицировать их;
- READ WRITE — указывает, что транзакция может читать и модифицировать данные. Это значение устанавливается по умолчанию.
- Параметр Isolation Level (уровень изоляции) определяет порядок взаимодействия данной транзакции с другими в рабочей базе данных. Может принимать значения:
- SNAPSHOT — значение по умолчанию. Внутри транзакции будут доступны данные в том состоянии, в каком они находились на момент начала транзакции. Если по ходу дела в базе данных появилисьнзменепия, внесенные другими завершенными транзакциями, то начатая транзакция их не увидит. При попытке модифицировать такие записи будет выдано сообщение о конфликте;
- SNAPSHOT TABLE STABILITY — предоставляет транзакции исключительный доступ к таблицам, которые она использует. Другие транзакции смогут только читать данные из этих таблиц;
- READ COMMlT — позволяеттранзакцин видеть текущее состояние базы данных.
- Параметр Lock Resolution (разрешение блокировки) управляет интерпретацией событий при обнаружении конфликта блокировки. Может принимать одно из двух значений:
- WAIT — значение по умолчанию. Ожидает разблокирования требуемой записи. После этого пытается продолжить работу;
- NO WATT — немедленно возвращает ошибку блокировки записи и прекращает выполнение транзакции.
- Параметр Table Reservation (уровень доступа к таблице) позволяет транзакции получить гарантированный доступ необходимого уровня к указанным таблицам. Существует четыре уровня доступа:
- PROTECTED READ — запрещает обновление таблицы другими транзакциями, но позволяет им выбирать данные из таблицы;
- PROTECTED WRITE — блокируетобновление таблицы другими транзакциями, читать данные из таблицы могут только транзакции типа SNAPSHOT пли READ COMMITTED;
- SHARED READ — самый либеральный уровень. Читать могут все, модифицировать — транзакции типа READ WRITE;
- SHARED WRITE - транзакции типа SNAPSHOT или READ COMMITTED и READ WRITE могут модифицировать таблицу, остальные способны только выбирать данные.
Если все действия, составляющие транзакцию, успешно выполнены или возникла ошибка, транзакция должна быть завершена, для того чтобы база данных оказалась в непротиворечивом состоянии. Для этого в Delphi есть две SQL-команды:
- COMMIT — сохраняет внесенные транзакцией изменения в базу данных. Это означает, что транзакция завершена успешно;
- ROLLBACK — откат транзакции. Транзакция завершается и никаких изменений в базу данных не вносится. Операция выполняется в случае возникновения ошибки при выполнении операции (например, при невозможности обновить запись).
Все транзакции в Delphi можно условно разделить на явные и неявные.
Явная транзакция — это транзакция, начатая и завершенная с помощью методов компонента TDataBase: StartTransaction, Commit и RollBack. После начала явной транзакции все изменения, вносимые в данные, относятся к этой транзакции.
Другого способа начать явную транзакцию, кроме использования компонента TDataBase, нет. Следовательно, в рамках одного соединения нельзя начать две транзакции.
Неявная транзакция стартует при модификации данных, если в текущий момент нет явной транзакции. Неявная транзакция возникает, например, при выполнении метода Post для компонентов наборов данных ТТаblе и TQuery. Например, если вы отредактировали запись в TDBGrid и переходите на другую запись, то это влечет за собой выполнение метода Post, что, в свою очередь, приводит к началу неявной транзакции, обновлению данных внутри транзакции и ее завершению. Важно отметить, что неявная транзакция, начатая с помощью методов Post, Delete, Insert, Append и т. д., заканчивается автоматически.