Документация Firebird → Документация по Firebird → Генераторы в Firebird → Основные положения о генераторах |
Думайте о генераторе, как о «потокобезопасном» («thread-safe») целочисленном счетчике, который расположен внутри базы данных Firebird. Вы можете создать его, задав имя:
CREATE GENERATOR GenTest;
Затем вы можете получать его текущее значение, увеличивать его или уменьшать точно так же, как и переменную «var i: integer» в Delphi, однако не всегда можно просто установить определенное значение, а затем получить то же самое значение, как вы ожидаете, - генератор находится внутри базы данных, но вне механизма управления транзакциями.
«Последовательность» («sequence») - это официальный термин SQL для обозначения того, что в СУБД Firebird называется генератором. Поскольку СУБД Firebird постоянно стремиться к большему соответствию стандарту SQL, то в СУБД Firebird 2 и более поздних версиях ключевое слово SEQUENCE может быть использован как синоним GENERATOR. Фактически, рекомендуется использовать синтаксис SEQUENCE во вновь создаваемом коде.
Хотя слово «последовательность» подразумевает серию генерируемых значений, в то время как «генератор» подразумевает прямую ссылку на фабрику по производству значений, в СУБД Firebird нет никаких различий между генератором и последовательностью. Просто это два названия для одного и того же объекта базы данных. Вы можете создавать генератор и получать доступ к нему с помощью синтаксиса последовательности, и наоборот.
Вот предпочтительный синтаксис для создания генератора/последовательности в СУБД Firebird 2:
CREATE SEQUENCE SeqTest;
Декларации генераторов хранятся в системной таблице RDB$GENERATORS. Однако, их значения хранятся на специальных зарезервированных страницах внутри базы данных. Вы никогда не получите доступ к этим значениям напрямую. Вы можете получить к ним доступ с помощью специальных встроенных функций, которые будут обсуждаться далее в этом руководстве.
Приведенная в этом разделе информация предназначена только для общего ознакомления. Общее правило таково: вы не должны обращаться к системным таблицам напрямую. Не пытайтесь создавать или изменять генераторы путем изменения таблицы RDB$GENERATORS. (Хотя оператор SELECT и не наделает бед.)
Структура системной таблицы RDB$GENERATORS следующая:
RDB$GENERATOR_NAME CHAR(31)
RDB$GENERATOR_ID SMALLINT
RDB$SYSTEM_FLAG SMALLINT
И для СУБД Firebird 2.0 и более старших версий:
RDB$DESCRIPTION BLOB subtype TEXT
Обратите внимание, что GENERATOR_ID является идентификатором для каждого генератора (о чем и говорит это имя), а не его значением. Также, не позволяйте вашему приложения хранить идентификатор для будущего использования в качестве маркера (handle) генератора. Это не имеет смысла (так как имя и является маркером), идентификатор может измениться после цикла резервного копирования/восстановления базы данных. Флаг SYSTEM_FLAG равен 1 для генераторов, используемых внутри СУБД, и NULL или 0 для всех, созданных вами.
Теперь давайте взглянем на таблицу RDB$GENERATORS, когда определен единственный генератор:
RDB$GENERATOR_NAME | RDB$GENERATOR_ID | RDB$SYSTEM_FLAG |
---|---|---|
RDB$SECURITY_CLASS | 1 | 1 |
SQL$DEFAULT | 2 | 1 |
RDB$PROCEDURES | 3 | 1 |
RDB$EXCEPTIONS | 4 | 1 |
RDB$CONSTRAINT_NAME | 5 | 1 |
RDB$FIELD_NAME | 6 | 1 |
RDB$INDEX_NAME | 7 | 1 |
RDB$TRIGGER_NAME | 8 | 1 |
MY_OWN_GENERATOR | 9 | NULL |
В СУБД Firebird 2 введен дополнительный системный генератор, называемый RDB$BACKUP_HISTORY. Он используется для новой утилиты NBackup.
Несмотря на то, что синтаксис SEQUENCE предпочтителен, системная таблица RDB$GENERATORS и ее поля не были переименованы в СУБД Firebird 2.
Генераторы хранят и возвращают 64-битные значения во всех версиях СУБД Firebird. Это дает диапазон значений:
-263 .. 263-1 или -9.223.372.036.854.775.808 .. 9.223.372.036.854.775.807
Таким образом, если вы используете генератор с начальным значением 0 для заполнения поля типа NUMERIC(18) или BIGINT (оба типа представлены 64-битным целым), и вы хотели бы добавлять 1000 строк в секунду, то пройдет около 300 миллионов лет (!), прежде чем значения выйдут за указанный диапазон. Поскольку довольно маловероятно, что человечество еще будет существовать на планете в это время (и оно все еще будет использовать базы данных СУБД Firebird), то не следует об этом беспокоиться.
Но хочу предупредить. СУБД Firebird понимает два «диалекта» SQL: диалект 1 и диалект 3. Новые базы данных должны всегда создаваться с диалектом 3, который является более мощным в ряде аспектов. Диалект 1 является диалектом совместимости, он используется только для полученных в наследство баз данных, которые были созданы в InterBase 5.6 или в более ранних версиях.
Одним из различий этих двух диалектов является то, что диалект 1 не имеет поддержки встроенных 64-битных целочисленных типов. Поля типа NUMERIC(18), например, хранятся во внутреннем представлении как DOUBLE PRECISION, который является типом с плавающей точкой. Наибольший целочисленный тип в диалекте 1 - это 32-битный INTEGER.
В диалекте 1, как и в диалекте 3, генераторы являются 64-битными. Но если вы присвоите сгенерированное значение полю типа INTEGER в базе данных диалекта 1, оно будет урезано до младших 32 бит, в результате давая диапазон:
-231 .. 231-1 или -2.147.483.648 .. 2.147.483.647
Хотя сам генератор может давать значения и 2.147.483.647, и 2.147.483.648, и так далее, урезанное значение вызовет повторение значений в этом месте, давая эффект 32-битного генератора.
В описанной выше ситуации, при 1000 вставках в секунду, заполняемое генератором поле переберет все значения через 25 дней (!!!), и за этим действительно нужно следить. 231 - это очень много, но насколько это много, зависит от ситуации.
В диалекте 3, если вы присваиваете значение генератора полю типа INTEGER, то все идет хорошо, если значение умещается в 32-битный диапазон. Но, как только этот диапазон будет превышен, вы получите ошибку переполнения: диалект 3 более строг в проверке диапазона по сравнению с диалектом 1!
При общении с сервером СУБД Firebird клиент может установить как диалект 1, так и диалект 3, независимо от того, к какой базе данных он подключен. Именно диалект клиента, а не диалект базы данных, определяет как СУБД Firebird передает значение генератора клиенту:
Если диалект клиента 1, сервер возвращает клиенту значение генератора в виде урезанного 32-битного целого. Но внутри базы данных сгенерированные значения остаются 64-битными, и они не зацикливаются после достижения 231-1 (даже если это так выглядит на стороне клиента). Это верно для баз данных и диалекта 1, и диалекта 3.
Если диалект клиента 3, сервер передает полное 64-битное значение клиенту. Опять-таки, это верно для баз данных и диалекта 1, и диалекта 3.
Начиная с СУБД Firebird версии 1.0, количество генераторов, которые вы можете создать в одной базе данных, ограничено только максимальным значением идентификатора (ID) в системной таблице RDB$GENERATORS. Так как это SMALLINT, то его максимальное значение равно 215-1 или 32767. Первое значение идентификатора всегда 1, так что полное количество генераторов не может превышать 32767. Как указывалось выше, в базе данных есть восемь или девять системных генераторов, таким образом остается, по крайней мере, 32758 для ваших собственных генераторов. Это должно быть достаточно для любого практического приложения. И так как количество декларируемых вами генераторов, не влияет на производительность, то вы можете чувствовать себя свободным и использовать столько генераторов, сколько хотите.
В ранних версия СУБД Firebird до версии 1.0, также, как и в СУБД InterBase, для хранения значений генераторов использовалась только одна страница базы данных. Поэтому, количество доступных генераторов было ограничено размером страницы базы данных. Следующая таблица перечисляет, сколько генераторов (включая системные) вы можете использовать в этих версиях СУБД InterBase и Firebird (спасибо Полу Ривзу [Paul Reeves] за предоставление оригинальной информации):
Версия | Размер страницы | |||
---|---|---|---|---|
1K | 2K | 4K | 8K | |
InterBase < v.6 | 247 | 503 | 1015 | 2039 |
IB 6 и версии Firebird до 1.0 | 123 | 251 | 507 | 1019 |
Все последующие версии Firebird | 32767 |
В СУБД InterBase до версии 6 генераторы были только 32-битные. Это объясняет, почему в старых версиях можно хранить ровно в два раза больше генераторов при одном и том же размере страницы.
СУБД InterBase, по крайней мере, до версии 6.01 включительно, успешно позволяет вам «создавать» генераторы до тех пор, пока их общее количество не достигнет 32767. Что произойдет, если вы обратитесь к генератору с идентификатором большим, чем номер, указанный в приведенной выше таблице, зависит от версии:
InterBase 6 генерирует ошибку «invalid block type» (неверный тип блока), поскольку вычисляемое значение расположено вне страницы, которая зарезервирована под генераторы.
В более ранних версиях, если вычисляемое место расположено вне страниц базы данных, возвращается ошибка. В противном случае, если генератор только читается (без инкремента), его значение представляет из себя то, что расположено по вычисленному месту (вычисленной странице базы данных). Если значение было записано, при этом перезапишутся данные, расположенные в вычисленном месте. Иногда это приводит к немедленной ошибке, но чаще всего это приводит к молчаливой порче вашей базы данных.
Как уже было сказано, генераторы находятся вне механизма управления транзакциями. Это означает, что вы не можете безопасно «откатить» генератор внутри транзакции. Одновременно с вашей транзакцией может существовать другая транзакция, выполняющая в то же самое время изменение значения генератора. Так что, если вы запросили значение генератора, думайте о нем, как об «ушедшем навсегда».
Когда вы запускаете транзакцию, а затем вызываете генератор и получаете значение (скажем, 5), генератор останется на том же самом значении, даже если вы выполните откат транзакции (!). Даже не смейте думать так: «Ну, хорошо, когда я выполню откат, я просто вместе с этим выполню GEN_ID(mygen,-1), чтобы снова установить генератор в значение 4». Чаще всего это может сработать, но это не безопасно, поскольку другая конкурентная транзакция может изменить значение генератора в период времи между вашим обращением к генератору и откатом транзакции. По этой же причине не имеет смысла получать текущее значение с помощью GEN_ID(mygen,0), а затем увеличивать это значение на стороне клиента.
Документация Firebird → Документация по Firebird → Генераторы в Firebird → Основные положения о генераторах |