О генерации уникальных первичных ключей

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

Проблема подобного рода может быть решена с помощью генерации уникальных первичных ключей как в клиентском приложении, так и на сервере. В приложении генерация первичного ключа осуществляется, например, путем создания длинной случайной символьной строки с помощью какого-либо генератора, использующего в качестве параметров время с точностью до долей секунды и какие-либо уникальные характеристики рабочей станции так, чтобы вероятность повторов была чрезвычайно мала. Этот способ обычно используется с теми СУБД, которые сами не поддерживают возможность генерации таких ключей (например, dBase). Он неудобен, так как при наличии нескольких различных приложений, использующих одну и ту же базу данных, требуется в каждом из них повторить код, отвечающий за генерацию первичных ключей.

Более предпочтительным способом является использование возможностей генерации первичных ключей, содержащихся в самой СУБД. Эту возможность поддерживает большинство серверных СУБД, а также некоторые настольные СУБД (например, Paradox, позволяющий создавать в таблицах автоинкрементные поля).

В качестве примера рассмотрим, как можно использовать возможности генерации первичных ключей, предоставляемые Oracle, для автоматического заполнения поля TAG таблицы VISIT. С этой целью воспользуемся генератором последовательностей Oracle, создающим последовательности уникальных целых чисел, используемых клиентскими приложениями в качестве значений первичных ключей. Создадим такую последовательность с помощью следующего набора операторов (исполнить их можно с помощью SQL Plus или с помощью утилиты Database Explorer):

	create sequence seqtag
	start with 200
	increment by 1
	nominvalue
	nomaxvalue
	nocycle
	nocache

На главной форме приложения заменим компонент TDBEdit, в который ранее вводилось значение поля TAG, компонентом TDBText, чтобы исключить возможность ввода этого значения пользователем. В модуль данных нашего клиентского приложения поместим компонент TUpdateSQL, выберем его имя в качестве значения свойства UpdateObject компонента TTable, с помощью которого вводятся данные в таблицу VISIT, сгенерируем операторы SQL для вставки, удаления и модификации записи и изменим оператор SQL для вставки следующим образом:

	Insert into VISIT
	(TAG, STAT_ID, DIAG_ID, PURP_ID, PAT_ID, DOCT_ID, DATE_VIZ)
	values
	(seqtag.NEXTVAL, :STAT_ID, :DIAG_ID, :PURP_ID, :PAT_ID, :DOCT_ID, :DATE_VIZ)

Далее изменим свойство CachedUpdates этого компонента TTable на true и обеспечим возможность сохранять в базе данных изменения, накопленные в кэше, переопределив реакцию на нажатие кнопок компонента TDBNavigator либо введя для этой цели либо дополнительные интерфейсные элементы (например, кнопку):

	void __fastcall TMAINFORM::Button1Click(TObject *Sender)
	{
	DataModule2->Table1->ApplyUpdates();
	DataModule2->Table1->CommitUpdates();
	}

Метод ApplyUpdates() нужен для сохранения изменений в базе данных, а метод CommitUpdates() - для очистки кэша.

Скомпилировав и выполнив приложение, можно убедиться в том, что первичные ключи в базе данных генерируются автоматически (например, с помощью Database Explorer). Окончательный вид приложения представлен на рис.11.

Рис. 11. Окончательный вид главной формы АРМ медстатистика.

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

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

	CREATE TRIGGER MEDIC1.VI_INS BEFORE INSERT 
	ON MEDIC1.VISIT
	FOR EACH ROW 
	BEGIN
	UPDATE VISIT SET TAG=seqtag.nextval where TAG=10;
	END;

Триггер можно создать, например, с помощью утилиты SQL Plus, и с помощью SQL Explorer можно просматривать его текст (рис.12):

Рис.12. Просмотр текста созданного триггера с помощью SQL Explorer.

В этом случае компонент TUpdateSQL уже не требуется, но при этом нужно добавить в приложение код, присваивающий значение полю TAG, так как в противном случае клиентское приложение не допустит отправки запроса на сервер:

	void __fastcall TDataModule2::Table1BeforeInsert(TDataSet *DataSet)
	{
	Table1TAG->Value=10; 
	}

Назад | Содержание | Вперед