1 июл. 2010 г.

Первый самостоятельно написанный EXECUTE BLOCK

EXECUTE BLOCK (id INTEGER = :id) 
  RETURNS (companykey INTEGER) 
AS 
  DECLARE VARIABLE ct INTEGER;
BEGIN 
  ct = 4; 
  companykey = 0; 

  WHILE (:ct = 4 AND NOT :companykey IS NULL) DO 
  BEGIN 
    companykey = NULL; 

    SELECT id, parent, contacttype 
    FROM gd_contact 
    WHERE id = :id 
    INTO :companykey, :id, :ct; 
  END 

  SUSPEND; 
END 
Возвращает организацию для заданного подразделения. Хотя, конечно, все можно было сделать простым запросом:
SELECT c.id 
FROM gd_contact c JOIN gd_contact d
  ON c.lb < d.lb AND c.rb >= d.rb
WHERE d.id = :ID AND c.contacttype = 3

16 июн. 2010 г.

Неявные JOIN-ы

Такие вот запросы практиковались в раннем Гедымине:
SELECT
   isg2.RDB$FIELD_NAME       FirstField
   , rc2.RDB$RELATION_NAME   NetTable
   , isg4.RDB$FIELD_NAME     SecondField
   , rc5.RDB$RELATION_NAME   TargetTable
   , isg6.RDB$FIELD_NAME     TargetField 
   , (SELECT 1
     FROM RDB$DATABASE
     WHERE
       EXISTS(SELECT *
              FROM RDB$RELATION_FIELDS rf
              WHERE rc5.RDB$RELATION_NAME = rf.RDB$RELATION_NAME
               AND rf.RDB$FIELD_NAME = 'LB')
       AND EXISTS(SELECT *
              FROM RDB$RELATION_FIELDS rf
              WHERE rc5.RDB$RELATION_NAME = rf.RDB$RELATION_NAME
               AND rf.RDB$FIELD_NAME = 'RB')) IsTree
 FROM
   RDB$INDEX_SEGMENTS isg1
   , RDB$RELATION_CONSTRAINTS rc1
   , RDB$REF_CONSTRAINTS rfc1
   , RDB$RELATION_CONSTRAINTS rc2
   , RDB$INDEX_SEGMENTS isg2
   , RDB$INDEX_SEGMENTS isg3
   , RDB$RELATION_CONSTRAINTS rc3
   , RDB$INDEX_SEGMENTS isg4
   , RDB$INDEX_SEGMENTS isg5
   , RDB$RELATION_CONSTRAINTS rc4
   , RDB$REF_CONSTRAINTS rfc2
   , RDB$RELATION_CONSTRAINTS rc5
   , RDB$INDEX_SEGMENTS isg6 
 WHERE
   rc1.RDB$RELATION_NAME = :tablename
   AND rc1.RDB$INDEX_NAME = isg1.RDB$INDEX_NAME
   AND rc1.RDB$CONSTRAINT_TYPE = 'PRIMARY KEY'
   AND rfc1.RDB$CONSTRAINT_NAME = rc2.RDB$CONSTRAINT_NAME
   AND rfc1.RDB$CONST_NAME_UQ = rc1.RDB$CONSTRAINT_NAME
   AND rc2.RDB$INDEX_NAME = isg2.RDB$INDEX_NAME
   AND rc1.RDB$RELATION_NAME <> rc2.RDB$RELATION_NAME
   AND isg3.RDB$FIELD_NAME = isg2.RDB$FIELD_NAME
   AND rc3.RDB$RELATION_NAME = rc2.RDB$RELATION_NAME
   AND rc3.RDB$CONSTRAINT_TYPE = 'PRIMARY KEY'
   AND rc3.RDB$INDEX_NAME = isg3.RDB$INDEX_NAME
   AND isg4.RDB$INDEX_NAME = isg3.RDB$INDEX_NAME
   AND isg2.RDB$FIELD_NAME <> isg4.RDB$FIELD_NAME
   AND isg5.RDB$FIELD_NAME = isg4.RDB$FIELD_NAME
   AND rc4.RDB$INDEX_NAME = isg5.RDB$INDEX_NAME
   AND rc4.RDB$CONSTRAINT_TYPE = 'FOREIGN KEY'
   AND rc4.RDB$RELATION_NAME = rc2.RDB$RELATION_NAME
   AND rfc2.RDB$CONSTRAINT_NAME = rc4.RDB$CONSTRAINT_NAME
   AND rfc2.RDB$CONST_NAME_UQ = rc5.RDB$CONSTRAINT_NAME
   AND rc5.RDB$INDEX_NAME = isg6.RDB$INDEX_NAME 
Возвращает список атрибутов типа множество для заданной таблицы.

8 июн. 2010 г.

Предварительные результаты по внешним ключам

Для тестирования была использована действующая база ВМК размером 29,912,520 Кб. Были конвертированы все внешние целочисленные ключи, удовлетворяющие следующим условиям:
  • В таблице более 10 000 записей.
  • Уникальных значений в ключе не более 200.
  • Ключ не ссылается на эту же таблицу.
Всего ключей, удовлетворяющих условиям, набралось 266. После их удаления и процедуры бэкапа-разбэкапа базы ее размер уменьшился до 23,752,136 Кб или на 21%.

После этого мы проверили скорость выполнения запроса на изменение данных на таблице AC_ENTRY, которая содержит более 9 млн. записей. Текст команды приводится ниже:
update ac_entry set
  companykey = 147048034,
  usr$gs_department = 147048036,
  currkey = 147021255,
  usr$gs_service = 147089059
Четыре изменяемых поля — это сконвертированные ссылки. Проверка целостности осуществляется с помощью триггера следующего вида:
CREATE TRIGGER xxx ON AC_ENTRY
  AFTER UPDATE
  POSITION 32000
BEGIN 
  IF (NEW.USR$GS_SERVICE IS NOT DISTINCT FROM 
    OLD.USR$GS_SERVICE) THEN EXIT;

  IF (NEW.USR$GS_SERVICE IS NOT NULL) THEN 
  BEGIN 
    IF (NOT EXISTS (SELECT id FROM gd_ref_constraint_data 
      WHERE value_data = NEW.USR$GS_SERVICE 
        AND constraintkey = 388626603)) THEN 
    BEGIN
      IF (NOT EXISTS (SELECT ID FROM GD_GOOD 
          WHERE ID = NEW.USR$GS_SERVICE)) THEN
        EXCEPTION gd_e_fkmanager 'message';

      RDB$SET_CONTEXT('USER_TRANSACTION', 
        'REF_CONSTRAINT_UNLOCK', '1');
      INSERT INTO gd_ref_constraint_data 
        (constraintkey, value_data, value_count)
        VALUES 
        (388626603, NEW.USR$GS_SERVICE, 1);
      RDB$SET_CONTEXT('USER_TRANSACTION', 
        'REF_CONSTRAINT_UNLOCK', '0');
    END
  END
END
Время выполнения запроса:
  • На новой базе — 1945 сек.
  • На старой базе — 2642 сек.
Получаем ускорение на 26%. Окончательные выводы можно будет сделать после тестирования на 2-3-х других базах.

7 июн. 2010 г.

Тестирование подсистемы отчетов

При тестировании изменений в подсистеме отчетов мы постарались исключить фактор пропускной способности сети и производительности дисковой подсистемы. Использовался встроенный сервер Firebird 2.5. Данные генерировались с помощью хранимых процедур. Пример такой процедуры можно видеть ниже:
CREATE PROCEDURE tst_p_gen_str_data (i INTEGER)
  RETURNS (id INTEGER,
    s1 VARCHAR(60),
    s2 VARCHAR(60),
    s3 VARCHAR(60),
    s4 VARCHAR(60))
AS
BEGIN
  s1 = 'ABC dfg';
  s2 = '123 567 9874 10';
  s3 = 'qwertyuiop asdfghjl; xzcvnbvmn,m.';
  s4 = '';
  WHILE (i > 0) DO
  BEGIN
    id = :i;
    i = :i - 1;
  END
END
Тестировались следующие отчеты:
  1. Отчет по одному набору данных со строковыми полями.
  2. Отчет по одному набору данных с целочисленными полями.
  3. Отчет по двум наборам данных, связанных связью мастер-дитэйл. Наборы данных со строковыми полями.
Замеры выполнялись на втором построении отчета. Учитывалось время построения отчета и информация об использовании оперативной памяти, полученная с помощью программы Process Explorer.

Таблица с результатами находится здесь.

Вывод:
  • для отчетов мастер-дитэйл на двух датасетах разница незначительна — увеличение потребления памяти на 2% и ускорение на 6%.
  • для отчетов, основанных на одном наборе данных, достигается существенное ускорение в 35% при умеренном росте потребления памяти — от 9 до 16%.
Стоит заметить, что Fast Report 4 позволяет строить отчеты с группировкой (фактически, мастер-дитэйл)  с использованием одного набора данных.

1 июн. 2010 г.

Проблема 1 июня 2010 года

В нашей истории случались забавные и не очень случаи, когда программа начинала работать неверно с определенной даты. Как выяснилось, мы не одиноки и проколы случаются с куда более крупными компаниями. Так с 1 июня 2010 г. перестал запускаться Corel X4. На официальном форуме техподдержки дается решение проблемы: "set back the clock 1-2 months".

Отстоим свой интернет!

Создан сайт ukaz60.net для сбора подписей за отмену Указа 60, фактически уничтожающего свободный интернет в Беларуси. Просьба всем небезразличным поддержать!

27 мая 2010 г.

Увеличиваем отрыв!

Обработана информация за 2009 г. по использованию программного обеспечения на предприятиях, подчиняющихся Министерству сельского хозяйства и продовольствия:
По сравнению с 2008 г. использование Нивы (Гедымина) увеличилось, а 1С — сократилось. Стоит заметить, что опросный лист содержал графу Нива, но не содержал отдельной графы для решений на Гедымине. Например, все наши мясокомбинаты дружно отметили используемое ПО в графе Другое. Но, мы-то знаем на чем они работают :) Таким образом, реальные цифры должны выглядеть еще более оптимистично.

PS: 42 предприятия до сих пор используют Анжелику!

26 мая 2010 г.

Как избежать циклических кольцевых ссылок в реляционной базе данных?

Системная таблица RDB$DEPENDENCIES хранит связи между метаданными в базе. Попытка вытянуть все зависимости для заданного объекта с помощью WITH RECURSIVE в общем случае разбивается о бесконечные циклы. Можно ли решить задачу одним запросом исходя из существующей структуры RDB$DEPENDENCIES? Ничего лучше, чем контролировать уровень вложенности и прерывать поиск при достижении заданной глубины, пока не приходит в голову:

WITH RECURSIVE dep_tree AS (
  SELECT
    0 as lvl, 
    rdb$dependent_name, 
    rdb$dependent_type, 
    rdb$depended_on_name
  FROM
    rdb$dependencies
  WHERE
    rdb$depended_on_name = :depname
  UNION ALL
  SELECT
    (t.lvl + 1) as lvl, 
    d.rdb$dependent_name, 
    d.rdb$dependent_type, 
    d.rdb$depended_on_name
  FROM
    rdb$dependencies d JOIN dep_tree t 
      ON t.rdb$dependent_name = d.rdb$depended_on_name
        AND t.lvl < 5 )
SELECT DISTINCT 
  rdb$dependent_name, 
  rdb$dependent_type, 
  rdb$depended_on_name
FROM
  dep_tree

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

25 мая 2010 г.

Тестирование объектов для работы с метаданными

Как выяснилось, Гедымин прекрасно переносит переподключение к БД прямо из кода программы:
IBLogin.LogOff;
IBLogin.Login(False, True);
// параметры метода Login управляют считыванием 
// информации о базе данных из реестра 
// и отображением окна ввода пароля
Что позволяет легко создать тесты для TgdcMetaData объектов. Начало уже положено. Тестируем создание и корректное удаление интервального дерева.

22 мая 2010 г.

Код в SubVersion

В ветке Stable все последние изменения и СТАРЫЕ отчеты, с копированием данных через поток. В Trunk все последние изменения и НОВЫЕ отчеты.

2 мая 2010 г.

Ниве 2.0 быть

Вроде бы согласован вопрос о финансировании НИВЫ 2.0. Если все будет Ок, то через годик выйдет в свет единый комплекс объединяющий в себе бухгалтерский и производственный учет, бизнес планирование, специфические задачи вроде расчетов рационов кормления скота. Конкурирующих продуктов такого рода просто нет.

Восьмая группа

Та, что планировалась в январе, но состоялась только в апреле из-за потопа в учебном центре.