Версия для печати

Firebird что еще делают с помощью генераторов

Оцените материал
(1 Голосовать)

Firebird что еще делают с помощью генераторов

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

Использование генераторов для получения, например, уникального номера передаваемого файла

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

Генераторы, как «счетчик использований» для SP, обеспечивающие основную статистику

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

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

Генераторы, эмулирующие «Select count(*) from...»

В СУБД InterBase и Firebird существует известная проблема, когда SELECT COUNT(*) (без выражения Where) для очень большой таблицы может очень долго выполняться, так как сервер должен посчитать «вручную», сколько строк есть в таблице во время получения запроса. Теоретически, вы легко можете решить эту проблему с помощью генераторов:

· создайте специальный генератор для «подсчета строк»;

· создайте триггер «перед вставкой» (Before Insert), который увеличивает его;

· создайте триггер «после удаления» (After Delete), который уменьшает его.

Это прекрасно работает и отменяет необходимость в подсчете «полного» количества записей: мы просто получаем текущее значение генератора. Я специально подчеркну слово «теоретически», поскольку в целом картина портится, когда какой-нибудь оператор Insert будет отменен, а генераторы не подчиняются механизму управления транзакциями. Вставка может провалиться из-за ограничений (по уникальному ключу, поля NOT NULL, указаного как NULL, и т. п.) или по другим ограничениями метаданных, или просто потому, что транзакция, выполнявшая вставку, будет откачена. У вас не будет строк в таблице, а ваш счетчик вставок увеличится.

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

Существуют ситуации, когда клиенты достаточно счастливо живут с информацией, наподобие «существует примерно 2,3 миллиарда записей», появляющейся немедленно по щелчку мыши, но они готовы застрелить вас, если они ждут 10 минут или больше для того, чтобы увидеть, что существует ровно 2,313,498,229 строк...

Генераторы для мониторинга и/или управления долго работающей SP

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

CREATE GENERATOR gen_spTestProgress;
CREATE GENERATOR gen_spTestStop;
 
set term ^;
 
CREATE PROCEDURE spTest (...)
AS
BEGIN
  (...)
  for select <огромное количество данных с огромным временем обработки>
  do begin
    GEN_ID(gen_spTestProgress, 1);
 
    IF (GEN_ID(gen_spTestStop, 0) > 0) THEN Exit;
 
    (...нормальная обработка данных...)
  end
END^

Это просто набросок, но вы должны уловить идею. С клиента вы можете выполнять GEN_ID(gen_spTestProgress, 0) асинхронно с реальной обработкой записей (например, в другом потоке [thread]), чтобы посмотреть, сколько записей было обработано, и отображать полученное значение в некотором окне прогресса. Так же вы можете выполнить GEN_ID(gen_spTestStop, 1), чтобы прекратить работу процедуры в любое время «извне» процедуры.

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

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

CREATE GENERATOR gen_spTestProgress;
CREATE GENERATOR gen_spTestStop;
CREATE GENERATOR gen_spTestLocked;
 
set term ^;
 
CREATE PROCEDURE spTest (...)
AS
DECLARE VARIABLE lockcount INTEGER;
BEGIN
  lockcount = GEN_ID(gen_spTestLocked, 1); 
    /* Самый первый шаг: инкремент блокирующего генератора */
 
  if (lockcount = 1) then /* _мы_ получили блокировку, продолжаем */
  begin
    (..."обычное" тело процедуры...)
  end
 
  lockcount = GEN_ID(gen_spTestLocked, -1); /* выполняем декремент */
 
  /* Убеждаемся, что генератор сброшен в самом конце, когда исключение было 
     выброшено внутри «обычного» тела процедуры: */
     
  WHEN ANY DO
    lockcount = GEN_ID(gen_spTestLocked, -1); /* декрементируем */
  exit;
END^

Примечание. Я не уверен на 100%, что это абсолютно безопасно для многопользовательской среды, но это позволит выполнить блокировку до тех пор, пока не произойдет ВЫХОД из тела процедуры, после чего процедура завершит свою работу, а до этого генератор остается инкрементированным. Выражение WHEN ANY обрабатывает исключения, но это не нормальный EXIT. При этом вы декрементируете генератор вручную - но вы должны декрементировать генератор непосредственно перед выходом (EXIT). Принимайте все меры предосторожности, я не могу воссоздать ситуацию, когда бы этот механизм не сработал... Если вы можете, дайте нам знать!

Прочитано 12906 раз