Дополнительные сведения по работе с генераторами

Примеры

Синтаксис

SET TERM string ;

Следующий пример показывает текстовый файл, который использует SET TERM при создании процедуры. Первый SET TERM определяет ##, как завершающие символы; соответствующий SET TERM восстанавливает точку с запятой (;), как завершающий символ.

CREATE PROCEDURE ADD_EMP_PROJ (EMP_NO SMALLINT, PROJ_ID CHAR(5))

INSERT INTO employee_project (emp_no, proj_id)

VALUES (:emp_no, :proj_id);

WHEN SQLCODE -530 DO

EXCEPTION unknown_emp_id;

Генератор - ϶ᴛᴏ специальный объект базы данных, который генерирует уникальные последовательные числа. Эти числа бывают использованы в качестве идентификаторов (к примеру код клиента͵ номер счета и т. п.). Важно заметить, что для создания генератора крайне важно использовать оператор DDL

CREATE GENERATOR generatorname;

При выполнении такой команды происходит 2 действия:

На специальной странице БД отводится 4 байта для хранения значения генератора

В системной таблице RDB$GENERATORS заводится запись, куда помещается имя генератора и его номер (фактически смещение на странице генераторов).

После создания генератора его значения можно получать при помощи функции

GEN _ ID ( generatorname , inc _ value ), где inc_value – число, на ĸᴏᴛᴏᴩᴏᴇ крайне важно прирастить значение генератора.

Генераторы возвращают значения (и сохраняют свои значения на диске) вне контекста транзакции пользователя. Это означает, что если генератора было увеличено с 10 до 11 (инкремент 1), то даже при откате транзакции (ROLLBACK) значение генератора не вернется к предыдущему . Вместе с этим гарантируется, что каждому пользователю будет возвращено уникальное значение генератора.

При выборке значения генератора запросом вида select gen_id(genname, x) from ... следует учитывать буферизацию выборки на клиенте. Т. е. в многопользовательской среде при выполнении двух таких запросов значения генератора будут увеличиваться ʼʼпачкамиʼʼ, а не на величину x для каждой выбираемой записи.

Использование генераторов в триггерах и хранимых процедурах

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

Создадим генератор для уникальной идентификации клиентов:

CREATE GENERATOR NEWCLIENT;

Создадим триггер для таблицы CLIENTS:

CREATE TRIGGER TBI_CLIENTS FOR CLIENTS

ACTIVE BEFORE INSERT POSITION 0

AS BEGIN

В результате при создании новой записи полю CLIENT_ID будет автоматически присваиваться новое значение.

При этом при использовании генератора в триггере возникает проблема на клиентской стороне (к примеру в BDE, используемом в Delphi, C++Builder ...), когда клиентское приложение пытается перечитать только что вставленную запись. Поскольку триггер меняет значение первичного ключа вставляемой записи, BDE ʼʼтеряетʼʼ такую запись и чаще всœего выдает сообщение ʼʼRecord/Key deletedʼʼ. Поскольку SQL-сервер не может сообщить клиентскому приложению о новом значении ключевого поля, крайне важно сначала запросить уникальное значение с сервера, и только затем использовать его во вставляемой записи. Сделать это можно при помощи хранимой процедуры:

CREATE PROCEDURE GETNEWCLIENT

RETURNS ( NID INTEGER )

AS BEGIN

NID = GEN_ID(NEWCLIENT, 1);

В Delphi, вы можете поместить компонент TStoredProc на форму, подсоединить его к данной процедуре, и к примеру в методе таблицы BeforePost написать следующее

StoredProc1.ExecProc;

StoredProc1.Params.asInteger;

После этого вышеприведенный триггер TBI_CLIENTS можно либо удалить, либо переписать так, чтобы генератор использовался только когда поле первичного ключа случайно приобрело значение NULL (к примеру когда к таблице CLIENTS доступ осуществляется не через ваше приложение):

ALTER TRIGGER TBI_CLIENTS

IF (NEW.CLIENT_ID IS NULL) THEN

NEW.CLIENT_ID = GEN_ID(NEWCLIENT, 1);

При этом использование хранимой процедуры не всœегда удобно – BDE может решить, что процедура вероятно изменяет какие-то данные на сервере, и в режиме autocommit завершит текущую транзакцию, что вызовет перечитывание данных TTable и TQuery. Более простым способом является получение значения генератора при помощи запроса:

SELECT GEN_ID(NEWCLIENT, 1) FROM RDB$DATABASE;

При этом, в случае если запрос помещен к примеру в Query2, текст в BeforePost будет следующим:

begin if DataSource.State = dsInsert then

Query2.Open;

ClientTable.FieldByName("CLIENT_ID").asInteger:=

Query2.Fields.asInteger;

Query2.Close;

end ;

end ;

Такой способ более предпочтителœен, чем использование хранимой процедуры для получения значения генератора, особенно при большом количестве генераторов.

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

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

В InterBase отсутствует аппарат автоинкрементных столбцов. Вместо этого для установки уникальных значений столбцов можно использовать аппарат генераторов.

Генератором называется хранимый на сервере БД механизм, возвращающий уникальные значения, никогда не совпадающие со значениями, выданными данным генератором в прошлом. Для создания генератора используется оператор

CREATE GENERATORИмяГенератора;

Для генератора необходимо установить стартовое значение при помощи оператора

SETGENERATOR ИмяГенератора ТО СтартовоеЗначение;

При этом СтартовоеЗначение должно быть целочисленным. Для получения уникального значения к генератору можно обратиться с помощью функции

GEN_ID(ИмяГенератора, шаг);

Эта функция возвращает увеличенное на шаг предыдущее значение, выданное генератором (или увеличенное на шаг стартовое значение, если ранее обращений к генератору не было).

ЗАМЕЧАНИЕ. Не рекомендуется переустанавливать стартовое значение генератора или менять шаг при разных обращениях к GEN_ID. В противном случае генератор может выдать неуникальное значение и, как следствие, будет возбуждено исключение "Дублирование первичного или уникального ключа" при попытке запоминания новой записи в ТБД.

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

CREATE GENERATOR RASHOD_N_RASH;

SET GENERATOR RASHOD_N_RASH TO 20;

Обращение к генератору непосредственно из оператора

INSERT INTO RASHOD (N_RASH, DAT_RASH, KOLVO, TOVAR, POKUP) VALUES (GEN_ID (RASHOD_N_RASH, 1) ,"10-JAN-1997",100, "Сахар", "Лира, TOO") .

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



CREATE TRIGGER BI­­­­_RASHOD FOR RASHOD

NEW .N_RASH = GEN_ID (RASHOD_N_RASH, 1);

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

INSERT INTO RASHOD (DAT_RASH, KOLVO, TOVAR, POKUP)

VALUES (: DAT_RASH, : KOLVO, : TOVAR, : POKUP)

Как можно заметить, столбец N_RASH в операторе не упоминается: все необходимые действия по заполнению этого столбца уникальным значением выполняет триггер BI_RASHOD.

"Обмануть" BDE можно, присваивая столбцу N_RASH любое значение, которое затем будет заменяться триггером на значение, полученное при помощи функции GEN_ID. Однако более корректным будет в данном случае использование не триггера, а процедуры:

CREATE PROCEDURE GET_N_RASH

RETURNS (NR INTEGER)

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

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

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

Эффект электромагнитной индукции

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

Над этим вопросом в первой половине XIX века работали ученые Эрстед и Фарадей. Они же и открыли это физическое явление. В последствии на основе электромагнитной индукции были созданы генераторы тока и электродвигатели. Интересно, что эти машины легко могут быть преобразованы друг в друга.

Как работают генераторы постоянного и переменного тока

Понятно, что генератор электрического тока - это электромеханическая машина, вырабатывающая ток. Но на самом деле она есть преобразователь энергии: ветра, воды, тепла, чего угодно в ЭДС, которая уже вызывает ток в проводнике. Устройство любого генератора принципиально ничем не отличается от замкнутого проводящего контура, который вращается между полюсами магнита, как в первых опытах ученых. Только намного больше величина магнитного потока, создаваемого мощными постоянными или чаще электрическими магнитами. Замкнутый контур имеет вид многовитковой обмотки, которых в современном генераторе не одна, а минимум три. Все это сделано для того, чтобы получить как можно большую ЭДС.

Стандартный электрический генератор переменного тока (или постоянного) состоит из:

  • Корпуса . Выполняет функцию рамы, внутри которой крепят статор с полюсами электромагнита. В нем установлены подшипники качения роторного вала. Его изготавливают из металла, он также защищает всю внутреннюю начинку машины.
  • Статора с магнитными полюсами. На нем закреплена обмотка возбуждения магнитного потока. Его выполняют из ферромагнитной стали.
  • Ротора или якоря. Это подвижная часть генератора, вал которой приводит во вращательное движение посторонняя сила. На сердечнике якоря располагают обмотку самовозбуждения, где и образуется электрический ток.
  • Узла коммутации. Этот элемент конструкции служит для отведения электричества с подвижного вала ротора. Он включает в себя проводящие кольца, которые подвижно соединены с графитовыми токосъемными контактами.

Создание постоянного тока

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

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

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

Схема генератора переменного тока

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

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

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

газового типа

Конструкции генераторов тока, где вместо металлической рамки как носитель зарядов используют токопроводящую плазму, жидкость или газ, получили название МГД-генераторов. Вещества под давлением прогоняют в поле магнитной напряженности. Под воздействием все той же ЭДС индукции заряженные частицы обретают направленное движение, создавая электрический ток. Величина тока прямо пропорциональна скорости прохождения через магнитный поток, а также его мощности.

Генераторы МГД имеют более простое конструктивное решение - в них отсутствует механизм вращения ротора. Такие источники питания способны выдавать большие мощности энергии в короткие промежутки времени. Их применяют в качестве резервных устройств и в условиях экстренных аварийных ситуаций. Коэффициент, определяющий полезное действие (КПД) этих машин выше, чем имеет электрический генератор переменного тока.

Генератор синхронный переменного тока

Существуют такие типы генераторов переменного тока:

  • Машины синхронные.
  • Машины асинхронные.

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

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

Возбуждение генератора переменного тока реализуют двумя методами:

  1. Контактным.
  2. Бесконтактным.

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

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

Асинхронный генератор

Существует асинхронный генератор переменного тока. Устройство его отличается от синхронного. В нем нет точной зависимости ЭДС от частоты с которой вал ротора вращается. Присутствует такое понятие как «скольжение S», которое характеризует эту разницу влияния. Величина скольжения определяется вычислением, так что неправильно думать, будто бы нет закономерности электромеханического процесса в асинхронном двигателе.

Если генератор, работающий вхолостую, нагрузить, то протекающий в обмотках ток будет создавать магнитный поток, препятствующий вращению ротора с заданной частотой. Так образуется скольжение, что, естественно, влияет на выработку ЭДС.

Современный асинхронный генератор переменного тока устройство подвижной части имеет в трех разных исполнениях:

  1. Полый ротор.
  2. Короткозамкнутый ротор.
  3. Фазный ротор.

Такие машины могут иметь само- и независимое возбуждение. Первая схема реализуется за счет включения в обмотку конденсаторов и полупроводниковых преобразователей. Возбуждение независимого типа создается дополнительным источником переменного тока.

Схемы включения генераторов

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

Любой генератор переменного тока устройство обмоток имеет стандартное, но подключение к нагрузке бывает двух типов:

  • звездой;
  • треугольником.

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

Соединяя треугольником обмотки генератора, их замыкают друг с другом последовательно в один контур. Из точек их соединения выводят линии к потребителю. Тогда вообще не нужен нулевой провод, а напряжение на каждой линии будет одинаковым независимо от нагрузки.

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

Где применимы генераторы постоянного и переменного тока

Генераторы постоянного тока значительно меньше по размерам и массе, чем машины переменного напряжения. Имея более сложное конструктивное исполнение чем последние, они все же нашли применение во многих отраслях промышленности.

Основное распространение они получили в качестве высокооборотных приводов в машинах, где требуется регулирование частоты вращения, например, в металлообрабатывающих механизмах, подъемниках шахт, прокатных станах. В транспорте такие генераторы установлены на тепловозах, различных судах. Множество моделей ветрогенераторов собраны на базе источников постоянного напряжения.

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

Назначение генератора переменного тока - вырабатывать электроэнергию в промышленных масштабах. Такой вид энергии подарил человечеству Никола Тесла. Почему именно изменяющий полярность ток, а не постоянный нашел широкое применение? Это связано с тем, что при передаче постоянного напряжения идут большие потери в проводах. И чем длиннее провод, тем потери выше. Переменное напряжение можно транспортировать на огромные расстояния при гораздо меньших затратах. Причем легко можно преобразовывать переменное напряжение (понижая и повышая его), который выработал генератор 220 В.

Заключение

Человек до конца не познал который пронизывает все вокруг. И электрическая энергия - это лишь малая часть открытых тайн мироздания. Машины, которые мы называем генераторами энергии, по сути очень просты, но то, что они могут нам дать, просто поражает воображение. Все же настоящее чудо здесь не в технике, а в мысли человека, которая смогла проникнуть в неисчерпаемый резервуар идей, разлитых в пространстве!

Большинство SQL-серверов имеет специальные механизмы для создания уникальных идентификаторов - autoincrement, identity, sequence, и т. п. В InterBase и Firebird для этого существует механизм генераторов.

В данной статье будут рассмотрены следующие темы:

Введение

Генераторы предназначены для получения последовательностей уникальных чисел. Например, 1, 2, 3..., 10, 20, 30 и т. п. Такие числа обычно используются как идентификаторы записи в таблицах, имеющих суррогатный (абстрактный)LINK первичный ключ. Например, в таблице клиентов для их нумерации введен столбец CLIENT_ID INTEGER, по которому построен первичный ключ. Столбец можно заполнять значениями генератора.

Нужно сразу заметить, что сами по себе генераторы не обеспечивают сохранение последовательности номеров в случае удаления записей - генератор всего лишь выдает числа по очереди увеличивая их на некоторую величину и обеспечивая уникальность выданных значений. То есть, генератор выглядит как переменная типа integer (в первом диалекте, или int64 в третьем диалекте), которая находится в памяти, и над которой можно выполнять операции Inc и Dec. Если вам требуется обеспечить непрерывные последовательности идентификаторов записей даже в случае их удаления или модификации, то вам нужно обратиться к статье Auditable series of numbers (непрерывные последовательности чисел). В любом случае это задача приложения и сервера, и выходит за рамки данного описания.

Создание генераторов

Генератор - это специальный объект базы данных, который генерирует уникальные последовательные числа. Эти числа могут быть использованы в качестве идентификаторов (например код клиента, номер счета и т. п.). Для создания генератора необходимо использовать оператор DDL

CREATE GENERATOR generatorname;


В Firebird 2.5 альтернативой слову generator является слово sequence.

При выполнении такой команды происходит 2 действия:

  1. На специальной странице БД для хранения значения генератора отводится
    • 4 байта (в первом диалекте или до InterBase 6.0) или
    • 8 байт (в третьем диалекте и во всех современных версиях InterBase/Firebird/Yaffil)
  2. В системной таблице RDB$GENERATORS создается запись, куда помещается имя генератора и его номер (фактически смещение на странице генераторов).
После создания генератора его значения можно получать при помощи функции

GEN_ID(generatorname, inc_value)

Где inc_value - число, на которое необходимо прирастить значение генератора.

Внимание! Получить новое значение генератора можно
1. оператором select, выбрав значение gen_id из таблицы с одной записью, например, системной rdb$database:
select gen_id(my_gen, 1) from rdb$database
2. в триггерах и процедурах - просто присвоив значение gen_id переменной:
myvar=gen_id(my_gen, 1);
3. в запросах - просто вызовом функции gen_id(my_gen, 1)

Генераторы возвращают значения (и сохраняют свои значения на диске) вне контекста транзакции пользователя. Это означает, что если значение генератора было увеличено с 10 до 11 (инкремент 1), то даже при откате транзакции (ROLLBACK) значение генератора, выданное в этой транзакции, не вернется к предыдущему. Вместе с этим гарантируется, что каждому пользователю будет всегда возвращено уникальное значение генератора (вызов функции gen_id всегда происходит "монопольно", то есть непараллельно для любого числа одновременных вызовов, чтобы исключить возможность получения разными пользователями одного и того же значения).

Примечание. Приращение значения генератора производится монопольно, аналогично функции InterlockedIncrement в Win32 API. То есть, даже если 1000 пользователей одновременно вызовут gen_id(x, 1), то они получат каждый свое (уникальное) значение (от x+1 до x+1+1000).

При выборке значения генератора запросом вида select gen_id(genname, x) from ... следует учитывать буферизацию выборки на клиенте. Т.е. в многопользовательской среде при выполнении двух таких запросов значения генератора будут увеличиваться "пачками", а не на величину x для каждой выбираемой записи.

Примечание. Иногда находятся умельцы, которые пишут
select gen_id(a, 0) + 1 from rdb$database
или
tmp=gen_id(a, 0);
tmp=tmp+1;
и др. аналогичные варианты. Если непонятно, почему так делать нельзя, прочитайте примечание выше еще раз.

Использование генераторов

Работа с генератором возможна только при помощи встроенной функции gen_id, а значит работать с генераторами можно в запросах, процедурах и триггерах. Давайте создадим генератор, и посмотрим, как его можно использовать.

CREATE GENERATOR NEWID;


В этот момент в базе создан новый генератор. Его значение равно 0. Если вам нужно использовать другое начальное значение, то можно установить (изменить) значение генератора следующей командой

SET GENERATOR NEWID 1000;


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

Обычно "начальное" значение генератора устанавливают в отличное от нуля, когда база данных является "распределенной", то есть в разных филиалах могут создавать новые записи в какой-либо таблице. Например, в главном офисе нумерация начинается с 0, в первом филиале - с 100000, во втором - с 200000, и так далее. Такой подход позволит избежать ситуаций, когда в двух филиалах созданы разные записи с одинаковым идентификатором.

Другой случай - наличие готового справочника с фиксированными идентификаторами. В этом случае вам нужно установить значение генератора больше максимального существующего идентификатора такого справочника.

В общем, установка начального значения генератора <> 0 зависит только от специфических требований приложения.

Увеличение значения генератора

Выше уже было сказано, что функция GEN_ID действует аналогично функции Inc, то есть увеличивает значение генератора на заданное число. Обычно инкремент значения используют равный 1

GEN_ID(NEWID, 1);


В определенных случаях может потребоваться больший шаг значений, например 10, если в системе возможны ситуации, когда нужно "вставить" идентификатор между двумя соседними. В этом случае шаг генератора выбирается > 1, а для вставки идентификатора "посередине" ему присваивается промежуточное число, без использования генератора. Например, в таблице автоматически назначены идентификаторы 10, 20 и 30. Для "вставки" нового идентификатора между 10 и 20 ему присваивается номер 15 (уже без использования генераторов, естественно).

Увеличение значения генератора функцией GEN_ID производится в монопольном режиме. Это означает, что одновременный вызов GEN_ID(NEWID, 1) двумя приложениями вернет каждому только свой идентификатор. Пример:

генератор имеет значение 7
приложение1: gen_id(newid, 1) -- выдан номер 8
приложение2: gen_id(newid, 1) -- выдан номер 9
...

Select

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

SELECT GEN_ID(NEWID, 1) FROM RDB$DATABASE


rdb$database здесь используется как таблица, содержащая только одну запись. В результате этим запросом также будет выдана 1 запись со значением генератора, увеличенным на 1.

Такой же способ используется в компонентах IBX LINK и FIBPlus - у IBDataSet есть метод GeneratorField, который позволяет обеспечить автоматическое присвоение нового номера столбцу первичного ключа записи как раз при помощи указанного оператора select, выполняемого библиотекой компонент "прозрачно". В FIBPlus та же самая функциональность устанавливается при помощи FIBDataSet.AutoUpdateOptions (GeneratorName).

Если вы по каким то причинам не хотите или не можете пользоваться методом GeneratorName, то можно взять отдельный компонент IBSQL (или IBQuery), прописать в нем запрос получения нового значения генератора, и вызывать такой запрос перед вставкой новой записи в таблицу.

Триггер

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

CREATE TRIGGER TBI_CLIENTS FOR CLIENTS
ACTIVE BEFORE INSERT POSITION 0
AS

BEGIN

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

Конечно, триггер можно оставить, изменив лишь код

IF (NEW.CLIENT_ID IS NULL) THEN

NEW.CLIENT_ID = GEN_ID(NEWID, 1);

Чтобы если никакое значение столбца CLIENT_ID при вставке записи из приложения не передано, то оно было сгенерировано автоматически.

Примечание. Первая попытка перенести ответственность за автоматическую нумерацию столбца первичного ключа таблицы обычно проваливается из-за компонент доступа. Поскольку такой столбец объявлен как not null, и компоненты автоматически считывают характеристики столбцов, у TField будет установлено в True свойство Required. Это не дает возможности оставить столбец "пустым" при передаче с клиента на сервер. Установите свойство Required у такого столбца в False.

Примечание. Данная проблема может быть решена в Firebird 2.0, при помощи оператора INSERT...RETURNING. Однако если вручную (через IBSQL, IBQuery) такой оператор можно выполнить и получить из него результат, то для IBDataSet это может оказаться невозможным, поскольку IBDataSet или IBQuery+IBUpdateSQL просто не будут знать, что оператор вставки что-то возвращает. Если от FIBPlus можно ожидать поддержки insert...returning после выхода Firebird 2.0, то от IBX - вряд ли, если только аналогичная функциональность будет реализована в InterBase.

Процедура

Использование генераторов в процедурах ничем не отличается от использования генераторов в триггерах. Вот пример процедуры, которая возвращает новое значение генератора:

CREATE PROCEDURE GETNEWCLIENT
RETURNS (NID INTEGER)
AS

BEGIN

NID = GEN_ID(NEWCLIENT, 1);
SUSPEND;


Чтобы получить результат такой процедуры, можно ее выполнить как

SELECT NID FROM GETNEWCLIENT


Также, если не указывать в процедуре SUSPEND, можно получить значение из процедуры путем

EXECUTE PROCEDURE GETNEWCLIENT RETURNING_VALUES:param


Этот способ отличается от обращения к rdb$database тем, что вам приходится создавать новую процедуру на каждый генератор. Если вдруг потребовалось создать одну процедуру для получения новых значений из разных генераторов, то можно воспользоваться двумя способами. Первый способ - просто обработка if

CREATE PROCEDURE GETNEWID
(GENID INTEGER)
RETURNS (NID INTEGER)
AS
BEGIN

IF (:GENID = 1) THEN

NID = GEN_ID(NEWCLIENT, 1);

IF (:GENID = 2) THEN

NID = GEN_ID(NEWORDER, 1);

IF (:GENID = 3) THEN

NID = GEN_ID(NEWSALE, 1);


Можно такую процедуру более "универсализировать" при помощи оператора EXECUTE STATEMENT в Firebird 1.5, который позволяет выполнять запросы динамически в процедурах и триггерах.

CREATE PROCEDURE GETNEWID
(GEN VARCHAR(30))
RETURNS (NID BIGINT)
AS
DECLARE VARIABLE SQL VARCHAR(60);
BEGIN

SQL = "SELECT GEN_ID("||GEN||", 1) FROM RDB$DATABASE;
EXECUTE STATEMENT SQL INTO:NID;
SUSPEND

END

Обратите внимание, что возвращаемая переменная NID имеет тип BIGINT, в отличие от предыдущих примеров - execute statement требует точного соответствия типов переменных. Если требуется получить результат в тип integer, то следует применить операцию cast(var as integer) либо сразу в запросе, либо к выходной переменной после того, как значение получено из execute statement.

Следует помнить, что execute statement производит динамическое выполнение запроса, а значит это будет чуть медленнее, чем выполнение такого запроса с клиента. Пример вызова процедуры (suspend в процедуре опять же указан для возможности ее вызова как select):

SELECT * FROM GETNEWID("NEWCLIENT");
SELECT * FROM GETNEWID("NEWORDER");
SELECT * FROM GETNEWID("NEWSALE");


Как видите, этот изощренный способ опять же ничем не лучше, чем просто вызовы

SELECT GEN_ID(NEWCLIENT, 1) FROM RDB$DATABASE;
SELECT GEN_ID(NEWORDER, 1) FROM RDB$DATABASE;
...

Экзотические "глюки"

  1. Экзотическим применением генератора является например его указание в default для столбца таблицы. В результате при restore сервер будет вызывать default столько раз, сколько есть записей в таблице, и значение генератора после restore окажется равным старому (до backup) плюс число записей в таблице. Данное поведение справедливо для всех версий InterBase/Firebird/Yaffil, и будет исправлено в Firebird 2.0.
  2. "Двоение" генераторов, то есть увеличение не на +1, а на +2, можно элементарно получить, если
    • вы сначала использовали генератор в триггере (без условия if)
    • прочитав эту статью, решили использовать select gen_id(x, 1) from rdb$database

то есть, сначала генератор будет инкрементироваться приложением, а потом еще и триггером. Поэтому, проверьте, содержит ли данный триггер проверку столбца на is null, как это указано ранее в статье.

  1. Вызов gen_id(x, y) в процедуре или триггере всегда возвращает 0.

Это может быть связано с тем, что генератор удален и создан снова. При этом в коде blr процедуры или триггера, которая явно ссылается на имя генератора, осталась ссылка на старый идентификатор генератора, в то время как в rdb$generators этот генератор уже имеет новый идентификатор.
Для решения этой проблемы нужно сделать alter trigger/procedure, чтобы перекомпилировался blr.

Примечание. Данная проблема вовсю существовала во времена повсеместного использования SQLExplorer (инструмента BDE). Он при попытке изменить значение генератора почему то удалял и создавал генератор снова.


Сброс значения генератора

Переустановка значения генератора на какое то новое значение, которое отличается от его обычного инкремента, может производиться теми же способами, которые были указаны выше - как при помощи оператора SET GENERATOR так и при помощи функции GEN_ID.

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

SET GENERATOR NEWCLIENT TO 0;


Это обычный ddl оператор. Он не может быть выполнен в процедуре или триггере (за исключением определенных версий Yaffil). Если все же сброс генератора требуется совершить в некоей процедуре (как уже говорилось, в триггерах такие действия выполнять категорически не рекомендуется), то это можно выполнить при помощи gen_id:

...
TEMPVAR = GEN_ID(NEWCLIENT, -GEN_ID(NEWCLIENT, 0));
...

То есть, генератор увеличивается на его же текущее отрицательное значение.

Получение "текущего" значения генератора

Под "текущим" имеется в виду сиюминутное значение генератора. Но если работа производится в многопользовательской среде, то слово "текущее" приобретает и второй смысл - то, которое постоянно меняется. Конечно, раз можно увеличить значение генератора на любое число, почему бы его не увеличить на 0?

SELECT GEN_ID(NEWCLIENT, 0) FROM RDB$DATABASE


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

Удаление генераторов

Оператор DROP GENERATOR X появился только в Firebird 1, Yaffil и InterBase 7.1. В предыдущих версиях в языке DDL оператора для удаления генератора не было. Тем не менее, серьезной проблемы это не представляло. В самом начале статьи было упомянуто, что запись о генераторе создается в таблице RDB$GENERATORS. Эту запись, безусловно, можно удалить. Однако место, распределенное на странице генераторов, освобождено не будет. Оно будет освобождено (точно так же и для drop generator) только после того, как вы сделаете вашей БД BACKUP/RESTORE.

Backup/Restore

Значения генераторов сохраняются при backup, и восстанавливаются при restore. Однако, если делать бэкап только метаданных (или restore только метаданных), то в некоторых версиях Interbase/Firebird/Yaffil значения генераторов могут оказаться "мусорными". Эта ошибка исправлена в самых последних версиях Firebird и Yaffil.

Если backup/restore метаданных используется для создания "клона" базы данных, используемого в другой фирме, офисе и т. п., то вам придется самостоятельно или обнулить генераторы, или установить их в требуемые значения.

Сбои в работе

Как уже говорилось выше, генераторы не подвержены влиянию транзакций, и казалось бы, должны инкрементироваться всегда. Увы, иногда при сбоях сервера (выключение питания, падение ОС и т. п.) страницы генераторов не успевают сохраниться на диск, хотя данные, записанные по commit, сохраняются. Более вероятна такая ситуация при Forced Write = Off.

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

Это исправлено в Firebird 1.5.2, то есть страницы генераторов записываются на диск до записи страниц с данными. На других версиях InterBase/Firebird/Yaffil с данной проблемой можно бороться только написав специальную программу "восстановления" или контроля значений генераторов после сбоя, которая будет содержать проверку на select max(id) from table и установку соответствующего генератора в это значение.

Типичные ошибки в использовании генераторов

При использовании генераторов в процедурах и триггерах разработчик иногда забывает, что в них может быть использован процедурный язык, и постоянно получает новое значение генератора путем обращения к rdb$database

declare variable i int;
begin

SELECT GEN_ID(X, 1) FROM RDB$DATABASE
INTO:I;

...


В данном случае в select нет никакой необходимости. Текст можно сильно упростить

declare variable i int;
begin

I = GEN_ID(X, 1);
INSERT INTO TBL VALUES (:I...
...


А если новое значение генератора не требуется использовать дальше по ходу процедуры, то можно и еще проще

begin

INSERT INTO TBL VALUES (GEN_ID(X, 1), ...
...


Собственно, ошибкой первый пример кода не является.

Встречаются случаи избыточного преклонения перед примером с rdb$database:


values (select gen_id(mygen, 1)from rdb$database, :param...)


Помилуйте, gen_id - это функция, поэтому здесь достаточно такого:

insert into table (field1, field2...)
values (gen_id(mygen, 1), :param...)


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

Более существенная и грубая ошибка в использовании генераторов - это увеличение "текущего" значения генератора вне контекста gen_id. Схематично выглядит как

I = GEN_ID(X, 0);
I = I + 1;

Или еще более безумный вариант

SELECT GEN_ID(X, 0) + 1 FROM RDB$DATABASE


Понятно, что время между получением текущего значения генератора и его увеличением очень мало, но все равно нельзя исключать, что в многопользовательской среде два таких блока кода, выполняемые параллельно, могут получить одно и то же значение генератора (в во втором случае вероятность куда больше, чем в первом, потому что неизвестно, когда ваш код поместит полученное таким образом значение на сервер). В начале статьи было указано, что функция GEN_ID гарантирует получение уникального значения при инкременте <> 0 путем "монополизации" вызова функции с использованием объектов операционной системы типа mutex. В приведенных вариантах защиты от параллельного вызова нет.

Как интересный пример самостоятельного использования mutex при необходимости обеспечения сложной последовательности увеличения значений, можете посмотреть udfdemox . Используемая в нем функция mutex упоминается дальше по тексту статьи.

Нестандартное применение генераторов

Вы уже видели, что функцию GEN_ID можно использовать в операторе SELECT. Вот как можно получить количество записей, выбранных запросом:

SET GENERATOR MYGEN TO 0;
SELECT GEN_ID(MYGEN, 1), FIELD1, FIELD2, FIELD3, ... FROM MYTABLE.


Такой запрос вернет в качестве первого поля порядковый номер записи, и после выполнения запроса генератор MYGEN будет содержать количество возвращенных записей. Кроме этого, во время выполнения этого запроса любой другой пользователь этой же БД может получить текущее значение генератора MYGEN и узнать сколько записей уже выбрано запросом на текущий момент (нечто вроде ProgressBar, однако число записей все-равно неизвестно до окончания выполнения запроса).

Понятно, что такой способ невозможно использовать для нескольких приложений одновременно, т. к. в этом случае значения генератора будут увеличиваться скачками.

Функцию GEN_ID можно также использовать и как "выключатель" длительных запросов. Пример приведен для БД EMPLOYEE.GDB.
Фактически такой запрос означает - "выбирать записи пока значение генератора = 0". Как только другой пользователь или ваше приложение в другом коннекте выполнит операцию

SET GENERATOR EMP_NO_GEN TO 1;

Запрос прекратится, т. к. условие WHERE станет равным FALSE.

Обязательно учтитывайте буферизацию записей клиентской частью (gds32.dll) или сервером при выполнении подобных запросов. Например, приведенный выше запрос с проверкой генератора в where "выключится" не сразу, а через некоторое время. Перед применением такого метода в вашей системе сначала следует проверить, подходит ли он вообще для выполняемого вами запроса.

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

  • Клиентское приложение при запуске определяет, есть ли для него (например в Registry или INI-файле) "именной" генератор.
  • Если нет, то оно операцией SELECT GEN_ID(GlobalGen, 1) FROM RDB$DATABASE получает идентификатор (например 150), создает на сервере собственный генератор операцией CREATE GENERATOR USER_N; (например, USER150). После чего сохраняет имя этого генератора на локальном диске.
  • Если да, то приложение обнуляет "именной" генератор операцией SET GENERATOR ... TO 0; (в нашем примере SET GENERATOR USER150 TO 0;) и выдает запросы с использованием данного генератора.
Этот способ не может быть рекомендован для систем с большим числом пользователей или частыми коннектами-дисконнектами. В ранних версиях InterBase был баг, который приводил к ошибке при определенном числе генераторов в базе данных (128 при размере страницы 1К). Даже и без бага этот способ приведет к постоянному росту числа страниц, выделенных под генераторы.

При помощи генераторов можно также решить проблему с отсутствием временных таблиц в вашей версии сервера (временные таблицы появились в InterBase 7.5LINK). Вы создаете таблицу, например TEMP_TBL, и в качестве первого поля, входящего в первичный ключ, указываете поле типа INTEGER. Пользователь при соединении с сервером получает собственный идентификатор у некоторого общего генератора, и затем использует его при помещении записей в такую "временную" таблицу. В результате, даже если несколько пользователей будут работать с такой таблицей, они никогда не "пересекутся" по значению первого поля первичного ключа "временной" таблицы..

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

create procedure GENREPORT
as
declare variable i int;
begin

i = GEN_ID(REPGEN, 1); -- проверим, запущена ли процедура
if (i > 1) then -- да, процедура уже работает

begin

i = GEN_ID(REPGEN, -1); -- возвращаем значение обратно
EXCEPTION REPORT_ALREADY_RUNNING;

Обработка отчета
i = GEN_ID(REPGEN, -1); -- отчет закончен, возвращаем значение обратно


Смысл очень прост. Исходное значение генератора = 0. При запуске процедуры она его увеличивает на 1. Если при проверке значения генератора окажется, что он больше 1, то это означает что копия процедуры уже запущена и работает.

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

  1. обработка ошибок по when any в процедуре, с "возвратом" значения генератора
  2. обработка ошибки из процедуры на клиенте, и явная выдача запроса

select gen_id(repgen, -1) from rdb$database с клиента в случае неуспешного выполнения процедуры.


Разумеется, на всякий случай следует еще проверять, не остался ли генератор в значении < 1 после его увеличения на 1 в начале процедуры. И обязательно предусмотреть обработку ситуации, когда используется 2-ой случай решения проблемы с ошибкой в процедуре, и в результате ошибки потеряно соединение с сервером.

Если вам не нравится использовать генератор для данных целей, то вы можете точно так же использовать функции WaitForAccess и ReleaseAccess из udfdemox . Обработку ошибок при использовании mutex нужно планировать так же, как и для генераторов.

)
Источники: печатная документация и справочная информация по Borland InterBase, переписка листсервера esunix1.
последние изменения: 2 июля 1999.

Большинство SQL-серверов имеет специальные механизмы для создания уникальных идентификаторов. В Borland Interbase для этого существует механизм генераторов.
В данной статье будут рассмотрены следующие темы:
Создание генераторов

Получение текущего значения генератора
Удаление генераторов

Создание генераторов
Генератор - это специальный объект базы данных, который генерирует уникальные последовательные числа. Эти числа могут быть использованы в качестве идентификаторов (например код клиента, номер счета и т.п.). Для создания генератора необходимо использовать оператор DDL

CREATE GENERATOR generatorname;

При выполнении такой команды происходит 2 действия:
1. На специальной странице БД отводится 4 байта для хранения значени генератора
2. В системной таблице RDB$GENERATORS заводится запись, куда помещаетс имя генератора иего номер (фактически смещение на странице генераторов).
После создания генератора его значения можно получать при помощи функции
GEN_ID(generatorname, inc_value)
где inc_value - число, на которое необходимо прирастить значение генератора.
Генераторы возвращают значения (и сохраняют свои значения на диске) вне контекста транзакции пользователя. Это означает, что если генератора было увеличено с 10 до 11 (инкремент 1), то даже при откате транзакции (ROLLBACK) значение генератора не вернется к предыдущему. Вместе с этим гарантируется что каждому пользователю будет возвращено уникальное значение генератора.
При выборке значения генератора запросом вида select gen_id(genname, x) from ... следует учитывать буферизацию выборки на клиенте. Т.е. в многопользовательской среде при выполнении двух таких запросов значения генератора будут увеличиваться "пачками", а не на величину x для каждой выбираемой записи.
Использование генераторов в триггерах и хранимых процедурах
Пример триггера, автоматически присваивающего уникальное значение ключевому полю таблицы:
создадим генератор для уникальной идентификации клиентов:

CREATE GENERATOR NEWCLIENT;

создадим триггер для таблицы CLIENTS:

CREATE TRIGGER TBI_CLIENTS FOR CLIENTS

ACTIVE BEFORE INSERT POSITION 0

BEGIN

В результате при создании новой записи полю CLIENT_ID будет автоматически присваиваться новое значение.
Однако при использовании генератора в триггере возникает проблема на клиентской стороне (например в BDE, используемом в Delphi, C++Builder ...), когда клиентское приложение пытается перечитать только-что вставленную запись. Поскольку триггер меняет значение первичного ключа вставляемой записи, BDE "теряет" такую запись и чаще всего выдает сообщение "Record/Key deleted". Поскольку SQL-сервер не может сообщить клиентскому приложению о новом значении ключевого поля, необходимо сначала запросить уникальное значение с сервера, и только затем использовать его во вставляемой записи. Сделать это можно при помощи хранимой процедуры

CREATE PROCEDURE GETNEWCLIENT

RETURNS (NID INTEGER )

BEGIN

NID = GEN_ID(NEWCLIENT, 1 ) ;

В Delphi, вы можете поместить компонент TStoredProc на форму, подсоединить его к данной процедуре, и например в методе таблицы BeforePost написать следующее

begin

begin

StoredProc1.ExecProc ;

StoredProc1.Params [ 0 ] .asInteger ;

end ;

end ;

После этого вышеприведенный триггер TBI_CLIENTS можно либо удалить, либо переписать так, чтобы генератор использовался только когда поле первичного ключа случайно приобрело значение NULL (например когда к таблице CLIENTS доступ осуществляется не через ваше приложение):

ALTER TRIGGER TBI_CLIENTS

BEGIN

IF (NEW .CLIENT_ID IS NULL) THEN

NEW .CLIENT_ID = GEN_ID(NEWCLIENT, 1 ) ;

Однако использование хранимой процедуры не всегда удобно - BDE может решить, что процедура вероятно изменяет какие-то данные на сервере, и в режиме autocommit завершит текущую транзакцию, что вызовет перечитывание данных TTable и TQuery. Более простым способом является получение значения генератора при помощи запроса:
SELECT GEN_ID(NEWCLIENT, 1) FROM RDB$DATABASE
При этом, если запрос помещен например в Query2, текст в BeforePost будет следующим:

begin

if DataSource.State = dsInsert then

begin

Query2.Open ;

ClientTable.FieldByName ("CLIENT_ID" ) .asInteger :=

Query2.Fields [ 0 ] .asInteger ;

Query2.Close ;

end ;

end ;

Такой способ более предпочтителен, чем использование хранимой процедуры для получения значения генератора, особенно при большом количестве генераторов.
Изменение значения генератора
Значение генератора можно переустановить при помощи оператора DDL

SET GENERATOR generatorname TO value;

Однако вы не сможете использовать такое выражение в теле триггера или хранимой процедуры, т.к. там можно использовать только операторы DML (а не DDL).
Если вы хотите обнулить генератор, или присвоить ему определенное значение в теле хранимой процедуры, то вы можете это сделать используя функцию GEN_ID:
(В данном примере генератор NEWCLIENT увеличивается на свое-же значение с отрицательным знаком.)
...
TEMPVAR = GEN_ID(NEWCLIENT, -GEN_ID(NEWCLIENT, 0);
...
Будьте внимательны при выполнении таких операций в многопользовательских средах. Приложения, процедуры и триггеры, которые в данный момент используют этот генератор, могут предполагать что он не будет "обнулен". Обязательно проверяйте "обнуление" генератора на возникновение конфликтных ситуаций при работе 2-х и более пользователей.
Получение текущего значения генераторов
Текущее значение генератора можно получить, вызвав функцию GEN_ID с нулевым увеличением значения генератора. Это можно сделать не только в триггере или хранимой процедуре, но и оператором SELECT

SELECT GEN_ID(NEWCLIENT, 0 ) FROM RDB$DA TABASE

Результатом выполнения запроса будет одна запись с одним полем, содержащим текущее значение генератора. Таблица RDB$DATABASES выбрана как содержаща в большинстве случаев одну запись, хотя использовать можно и любую другую таблицу.
При работе в многопользовательских средах будьте внимательны - в то время как вы получили "текущее" значение генератора, другое приложение может его изменить, и таким образом "текущее" значение окажется устаревшим. Тем более не рекомендуется использовать "текущее" значение генератора для его последующего изменения.
Удаление генераторов
В языке DDL Borland Interbase нет оператора для удаления генератора. Неизвестно, чем это вызвано, но серьезной проблемы не представляет. В самом начале статьи было упомянуто, что запись о генераторе создается в таблице RDB$GENERATORS. Эту запись, безусловно, можно удалить. Однако место, распределенное на странице генераторов, освобождено не будет. Оно будет освобождено только после того, как вы сделаете вашей БД BACKUP/RESTORE.
Нестандартное применение генераторов
Вы уже видели, что функцию GEN_ID можно использовать в операторе SELECT.
Вот как можно получить количество записей, выбранных запросом:

SET GENERATOR MYGEN TO 0 ;

SELECT GEN_ID(MYGEN, 1 ) , FIELD1, FIELD2, FIELD3, ... FROM MYTABLE.

Такой запрос вернет в качестве первого поля порядковый номер записи, и после выполнения запроса генератор MYGEN будет содержать количество возвращенных записей. Кроме этого, во время выполнения этого запроса любой другой пользователь этой-же БД может получить текущее значение генератора MYGEN и узнать сколько записей уже выбрано запросом на текущий момент (нечто вроде ProgressBar, однако число записей все-равно неизвестно до окончания выполнения запроса).
Функцию GEN_ID можно также использовать и как "выключатель" длительных запросов. Пример приведен для БД EMPLOYEE.GDB.

Фактически такой запрос означает - "выбирать записи пока значение генератора = 0". Как только другой пользователь или ваше приложение в другом коннекте выполнит операцию

SET GENERATOR EMP_NO_GEN TO 1 ;

запрос прекратится, т.к. условие WHERE станет равным FALSE.
(то-же самое, и даже в более сложных вариантах, можно делать при помощи UDF в Borland InterBase 4.2. см "Особенности версии 4.2")
примечание: обязательно учтитывайте буферизацию записей клиентской частью (gds32.dll) или сервером при выполнении подобных запросов. Например, приведенный выше запрос с проверкой генератора в where "выключится" не сразу, а через некоторое время.
Безусловно, в многопользовательской среде невозможно использовать в таких целях один и тот-же генератор. Для решения этой проблемы можно завести глобальный генератор, который будет выдавать уникальные идентификаторы пользователям при коннекте, а клиентское приложение будет запоминать его номер и хранить на локальном компьютере для последующего использования. Логика работы может быть следующая:
Клиентское приложение при запуске определяет, есть-ли для него (например в Registry или INI-файле) "именной" генератор.
Если нет, то оно операцией SELECT GEN_ID(GlobalGen, 1) FROM RDB$DATABASE получает идентификатор (например 150), создает на сервере собственный генератор операцией CREATE GENERATOR USER_N; (например USER150). После чего сохраняет имя этого генератора на локальном диске.
Если да, то приложение обнуляет "именной" генератор операцией SET GENERATOR ... TO 0; (в нашем примере SET GENERATOR USER150 TO 0;), и выдает запросы с использованием данного генератора.