3
августа, 2001
Владимир Трухин
ведущий инженер-программист
ОАО "Воткинская ГЭС"
Fax: +7 (34241) 63297
E-mail: vlt@gesvt.permenergo.ru
vlt@votges.ru
Формирование
первичных ключей
Всё это от
того, что вновь и вновь
Решаю я одну свою задачу
Вильям
Шекспир, Сонет №76
Проблема
Наверное,
каждый разработчик приложений
баз данных на Visual FoxPro получал
сообщение «Unique key violation», которое
сигнализирует о попытке
повторного использования
значения первичного ключа
таблицы.
Разобравшись
в происхождении этого сообщения,
мы начинаем использовать INSERT INTO
вместо APPEND BLANK, чтобы явно
задавать значение первичного
ключа. Идея явного определения
ключа в команде правильная, но не
очень хорошая. В этом случае
вызовы функции формирования
ключа прячутся во многих
сегментах кода приложения, что
обязательно приведёт к ошибкам
при изменении правил
формирования ключа.
Более
надёжный способ – использование
вызова функции формирования
ключа, как значения по умолчанию
для ключевого поля таблицы.
Однако, написав несколько таких
функций, мы испытываем
непреодолимое желание иметь одну
единственную функцию, которая
формировала бы первичные ключи
для всех наших таблиц. Так делают
многие разработчики, создавая
функции, которые получают в
качестве параметров значения
алиасов таблиц, названия полей и
другие значения.
Такой
подход хорошо работает при
небольшом количестве таблиц и
небольшом количестве проектов, но
если разработка баз данных
поставлена на конвейер, то
наверняка в скором времени
захочется иметь функцию, которая
самостоятельно без всяких
указаний сможет сформировать
первичный ключ для любой таблицы.
Решение
Попробуем
определить частные проблемы,
которые необходимо решить:
-
Получение имени
поля первичного ключа.
-
Получение имени
тэга первичного ключа.
-
Обеспечение
поиска очередного значения
первичного ключа и сохранения
указателя записи в редактируемой
области.
-
Вычисление
значения для первичного ключа.
Прежде чем
приступить к решению, оговорим
условия работы функции
генерирования первичного ключа:
-
Мы определяем
функцию для базы данных, но не для
конкретной программы. Поэтому, мы
не можем знать режим буферизации
таблицы.
-
Добавление
записей в таблицу может
производиться даже без программы.
Таким образом, функция должна
самостоятельно открывать нужные
ей таблицы в соответствующих
алиасах.
-
Поскольку мы не
знаем, в какой программе будет
работать эта функция, она должна
сохранять неизменной среду любой
программы.
Итак,
начнём…
Получение имени поля и
имени тэга первичного ключа.
Visual FoxPro имеет
замечательную особенность. В
момент вычисления значения поля
по умолчанию, алиас этой таблицы
становится текущим.
Воспользуемся этим для получения
имени поля и имени тэга
первичного ключа. Поскольку эти
операции очень похожи, создадим
для этого одну общую функцию.
Function
_GetPrimaryKey(lcType)
** lcType – указывает, что должна
вернуть функция
** если lcType=NAME,
возвращается имя тега первичного
ключа
** если lcType=EXPR,
возвращается выражение
первичного ключа
** оно
совпадает с именем поля
первичного ключа для простых
ключей
local lcPrimaryKey, lnCount
lcPrimaryKey=''
** перебрать все теги
for
lnCount = 1 TO tagcount()
if !empty(tag(lnCount))
** найти тэг
первичного ключа
if primary(lnCount)
if lcType='NAME'
** получить имя тэга
lcPrimaryKey=TAG(lnCount)
else
** получить имя поля
lcPrimaryKey=sys(14,lnCount)
endif
exit
endif
else
exit
endif
endfor
return lcPrimaryKey
Обеспечение поиска
очередного значения
Производить
поиск очередного значения ключа
лучше всего в отдельном алиасе.
Это не приведёт к изменению
положения указателя записи. Как
бы мы не производили поиск
нужного нам значения, указатель
останется на добавленной записи.
В дальнейшем, разрабатывая
приложение, мы можем использовать
этот алиас для промежуточных
вычислений, таких как общие и
частные итоги.
Создадим
для этой цели отдельную функцию.
Она будет проверять
существование этой
дополнительной области для нашей
таблицы.
FUNCTION
_CheckArea(lcTable, lcAlias, lcTag)
LOCAL llSuccess,lnParNum
lnParNum=parameters()
llSuccess=.F.
if !used(lcAlias)
if
lnParNum=3
use (lcTable) in 0;
alias (lcAlias) ;
order tag (lcTag) ;
again shared
else
use (lcTable) in 0;
alias (lcAlias) ;
again shared
endif
llSuccess=used(lcAlias)
else
llSuccess=.T.
endif
return llSuccess
Вычисление значения
первичного ключа
Все
подготовительные операции
закончены, и мы можем приступить к
вычислению значения первичного
ключа. Для этого создадим
следующую функцию:
FUNCTION
_NewKey()
LOCAL lcKey, lcDeleted, lcAlias, lcPrimaryKey, lcField
** определим имя дополнительного
алиаса
** присвоим
ему дополнительный префикс TMP_
lcAlias='TMP_'+alias()
** определим имя поля первичного
ключа
lcField=lcAlias+'.'+_GetPrimaryKey('EXPR')
** определим имя тега первичного ключа
lcPrimaryKey=_GetPrimaryKey('NAME')
** проверим
открыта ли уже дополнительная
область
if !_CheckArea(dbf(),lcAlias,lcPrimaryKey)
return ''
endif
lcDeleted=set('DELETED')
set deleted off
** получаем значение последнего
используемого ключа
** наращиваем
это значение
go
bottom in (lcAlias)
if bof(lcAlias) or eof(lcAlias)
lcKey=padl(allt(str(1)),len(&lcField),'0')
else
lcKey=allt(str(val(&lcField)+1))
lcKey=padl(lcKey,len(&lcField),'0')
endif
set deleted &lcDeleted
return lcKey
Заключение
Код этих
трёх функций необходимо
поместить в хранимые процедуры
базы данных. Затем для каждой
таблицы определить значение по
умолчанию для поля первичного
ключа как вызов функции _NewKey().
После этого
мы свободно можем добавлять
записи командами INSERT INTO, APPEND BLANK и даже горячими
клавишами CTRL+Y.
Файл
с кодом функций и текст статьи в формате MS
Word (zip-файл,16KB)
Главная
страница | Технологии | Продукция | Материалы | Специалисты | Партнёры
Запись
в Гостевую Книгу |
Просмотр Гостевой Книги
|