В этой разработке я пользуюсь продуктами "Gupta (Centura)", как и любые инструменты, они имеют некоторые особенности (и недостатки)... вот этому я и посвящу сегодняшний рассказ...
Неприятная проблема, с которой нам пришлось бороться это длина number - как мы обнаружили, сервер поддерживает только 15 разрядов, по жизни такая длина нормальна. Но, если говорить о длине индексных полей, и соответственно, объеме индексного пространства, то, оно мне показалось недостаточным. (Для примера, длина number в Oracle 35 разрядов.) А если мы говорили о распределенной (сетевой) системе учета, в основе которой лежат "центры учета" и между которыми, по определенной технологии, осуществляется репликация данных, самым главным свойством индексов есть их уникальность в системе, и, как обеспечить их в "сети"? Проще всего добавить поле с индексом сервера, и "закатать" двойной "примари индекс" но, что - то мне в "двойных индексах" не понравилось, я решил пойти "другим путем". Меня заинтересовал вопрос, а можно ли два индекса заменить одним? понятно же, что количество ункальных серверов данных будет невпример меньше, чем размер индекса... Можно же и выделить для каждого сервера "секвенс"- диапазоны. Как? Да выделяем каждому серверу двухразрядный номер ( если серверов в сети может быть не более 99) или трехразрядный, для 999, берем текущий секвенс умножаем его на 100 (или 1000) и складываем с номером сервера вот вам и уникальные Идентификаторы. Но, что останется от секвенса при умножении? Ведь у нас всего 15 разрядов (а у меня мания переполнения :-)). Я решил перейти на символьные Индексы - и сделал индексную арифметику - по основанию 232. Да, именно, столько - дело в том, что первых 32-х символов в таблице символов сервера нет, и... вот и пришлось несколько ограничить аппетиты. Но, и в этом случае строкой в 18 символов я смоделировал размеры "Ораклового" индексного пространства.
Теперь я расскажу, как это работает. Табличка, совершенно необходимая для процесса:
CREATE TABLE SYSADM.NUR ( ID DOUBLE PRECISION ) /
ID - просто номер... им активно пользуемся...
Теперь пора описать процесс создания индексов.
Вот текст процедуры, которая создает новых "членов" в индексной последовательности.
STORE SYSADM.GETMAXID Procedure: GetMaxID static Parameters String: name_Tbl ! Это некий параметр, - название таблицы - не используется в ЭТОЙ версии... Receive String: strSequense ! а вот тут мы получим ГОТОВЫЙ новый секвенс... Local variables Sql Handle: hSqlUp Number: nOk Number: FchOk Number: IDBase Number: codeCurrent ! код текущего символа String: chCurrent ! текущий символ String: chNext ! текущий символ, с прибавленной еденицей (символ следующий в таблице символов за текущим) Number: IsGetNext ! если ожидаем увеличение разрядности. Number: Lang Number: nRow String: lRowid String: strIDBase Actions Call SqlConnect ( hSqlUp ) Set strSequense = '' Set nRow = 0 Set Lang = 100 When SqlError Set nOk = SqlError (hSqlUp) !на всякий случай, надо и на ошибки проверять... Return FALSE ! нам нужно получить новый символьный индекс. Как Это сделать? - проще всего (для меня) оказалось получить текущий индекс, разложить его на "составляющие символы, получить их номера, к каждому прибавить по единице, и снова превратить в символы... говорил я эти слова долго, но, не все так страшно - все эти действия делает один селект, и вот результат его работы я и разбираю.. Call SqlRetrieve(hSqlUp, 'sysadm.GetMaxIDCO', '', ':codeCurrent, :chCurrent, :chNext, :Lang, :lRowid, :strIDBase ' ) Call SqlExecute(hSqlUp) While nRow < Lang Call SqlFetchRow(hSqlUp,nRow, nOk) ! выбрав очередной символ из последовательности проверяю If ( nRow = 0 ) OR ( IsGetNext = 1 ) ! Если ЭТО начало, то, я могу "вместо здрасти" просто начать со "следующего" за текущим символа... или же, у нас "переход разряда".... Set strSequense = chNext || strSequense If ( codeCurrent = 255 ) Set IsGetNext = 1 Else Set IsGetNext = 0 Else ! в противном случае, просто добавляем текущий символ.... Set strSequense = chCurrent || strSequense Set IsGetNext = 0 If ( IsGetNext = 1 ) AND ( nRow = Lang-1 ) ! а вот если у нас "переход разряда" дошел до конца строки, то пора увеличить размер строки... (добавить разряд) Set strSequense = ' ' || strSequense Set nRow = nRow +1
Call SqlRetrieve(hSqlUp, 'sysadm.SetMaxIDCO', ' :strSequense, :lRowid', '' ) Call SqlExecute(hSqlUp) Set strSequense = strIDBase || strSequense Call SqlDisconnect ( hSqlUp ) /
Почему такой вызов процедуры (с параметром - именем таблицы)? - я заменил предыдущую версию, где имя текущей таблички было оправдано, сейчас же осталась просто совместимость с предыдущими версиями.
Вот и основная команда:
SYSADM.GETMAXIDCO select @code(@MID(a.SENSE, b.ID-1, 1)), @MID(a.SENSE, b.ID-1, 1), @if( @mod(@code(@MID(a.SENSE, b.ID-1, 1)), 255), @char(@code(@MID(a.SENSE, b.ID-1, 1))+1), @char (32)), @LENGTH ( a.SENSE), a.rowid, c.SENSE from CONSTCHAR a, NUR b, CONSTCHAR c where a.ID = ' z' and b.ID <= @length ( a.SENSE) and c.ID = ' 9'
Теперь и опишем, что мы получаем и с помощью чего:
По полученным значениям в процедуре "собираем" следующее значение "секвенса".
Его уже просто записываем в базу, на "старое" место.
SYSADM.SETMAXIDCO update CONSTCHAR set SENSE = :1 where rowid = :2
Добавляем к "секвенсу" идентификатор текущей базы данных, и все, новый "секвенс" создан, и готов к работе; вообще, тут подумалось, а может проще было бы написать на "С" маленькую "dll", подключить ее к серверу, и... все работает! - может, может именно так и надо сделать... но, подозреваю,чуть по- позже... Если это действительно будет нужно, и этот проект (в целом) станет чем-то большем, чем простая "разминка для мозгов"...
Далее, насколько я понимаю, особенностью работы SQLBase есть возможность использовать не только хранимые процедуры, но и хранимые команды. Пользуемся мы ими очень широко...
Естественно для работы с ними понадобилось написать маленький администратор и пользоваться следующей таблицей:
CREATE TABLE SYSADM.PRCLIST ( NAME VARCHAR(30) NOT NULL, IDTABLES VARCHAR(18), STRWHERE LONG VARCHAR STRINTO LONG VARCHAR, TEXT LONG VARCHAR, ISDELLDOCS VARCHAR(1) ) /
NAME - Имя команды.
IDTABLES - Идентификатор таблицы из списка таблиц
STRWHERE - Строка описание списка задающих параметров
STRINTO - Строка описания получаемых параметров
TEXT - Текст команды
ISDELLDOCS - идентификатор удаляемости, применяется для команд удаления данных в пользовательских документах, - нужны при больших удалениях - типа, удаления пользователя (со всеми его документами).
Ограничения:
ALTER TABLE SYSADM.PRCLIST FOREIGN KEY PRCLst220805 ( IDTABLES) REFERENCES SYSADM.LPATTABLES /
Естественно, нам нужно отслеживать "взаимосвязь" со списком табличек...(наших)
И, на последок. Приложения (документы (екзешники)), которые мы разрабатываем, иногда "нуждаются" в изменениях своих размеров - тех, что на экране... важно, что размеры меняют пользователи, и им нравится, когда документы "помнят" свои предыдущие размеры (и положение на экране). Как это сделать? Мы решили, что интереснее всего размеры (да и положение) каждого окошка на экране хранить в табличке.
CREATE TABLE SYSADM.LISTDOCSIZE ( ID_USER VARCHAR(18), NAMEFORM VARCHAR(38), NLEFTWIDTH DOUBLE PRECISION, NLEFTHEIGTH DOUBLE PRECISION, NSIZEWIDTH DOUBLE PRECISION, NSIZEHEIGTH DOUBLE PRECISION ) /
ID_USER - Идентификатор пользователя из таблицы со списоком пользователей
NAMEFORM - Имя окна, важно, чтобы все окошки в системе имели уникальные названия окон
NLEFTWIDTH, NLEFTHEIGTH - Положение левого верхнего угла
NSIZEWIDTH, NSIZEHEIGTH - Размер по горизонтали и вертикали
Естественно, при окончании работы (закрытии окон) данные записываются, а при старте приложения считываются, и, естественно, окошки принимают необходимые размеры.
Ну вот... осталась только "добавочка" - набор всего описанного в одном SQL файле, который Вы можете взять из проекта.