ВыходВход

Меню сайта

Категории статей
Теория [1]
Древовидные структуры данных. Методология, описание
Использование в SQL DB [0]
Решения используемые в SQL DB
Использование в Perl [7]
Применение технологии Nested Sets в Perl программировании
Использование в PHP [2]
Применение технологии Nested Sets в PHP программировании

Меню пользователя

Поиск по статьям

Друзья сайта

Модуль Perl, дополнение 1 - Формирование счетчиков
» Каталог статей » Использование в Perl
Модуль Perl, дополнение 1 - Формирование счетчиков

Модуль Perl, дополнение 1 - Формирование счетчиков

Цель и задача

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

В общем-то нас интересуют не все поля, в частности:

  • firm_category.id - идентификатор категории;
  • firm_category.left_key - левый ключ узла;
  • firm_category.right_key - правый ключ узла;
  • firm_category.level - уровень узла;
  • firm_category.firm - количество фирм в данной категории (включая подчиненные), наш счетчик;
  • firm_merge.category - идентификатор категории фирмы;
  • firm_merge.firm - идентификатор фирмы;

А таблица firm_main - использоваться и не будет...

Концепция

Процедуру обновления счетчиков мы добавим в наш модуль, но для начала определим концепцию и подводные камни:

  • Возьмем за правило, что все используемые в обработке поля имеют тип поля INT (ЦЕЛЫЙ)*. Во первых, это ускоряет обработку, а во вторых упрощает проверку передаваемых начальных данных;
  • Хотелось бы что бы процедура работала в двух режимах: обновление всего дерева и обновление только той части дерева, которое требуется (родительская ветка), но с учетом того, что один и тот же элемент списка может находится в различных частях дерева, то нам потребуется при обновлении каждого родительского узла проверять все подчиненные на предмет повтора элемента, что само по себе - не ускорит обработку, а в некоторых случаях даже замедлит, правда, это не относится структуре с прямым подчинением элемент => категория.Итак, ломать голову не будем, режим процедуры один - полное обновление дерева;
  • Метод должен позволять указывать дополнительные условия обновления дерева, то есть если у нас есть выключенные категории, то учитывать их не нужно.

ПРИМЕЧАНИЕ: * в одной из статей (не моих), о технологии NestedSets, описывался принцип формирования НЕ целых ключей дерева, что позволяет ускорить процесс вставки и перемещения узлов дерева, но данная схема была описана учень скудно и, хочу заметить, при таком формировании ключей, можно практически забыть о проверке целостности дерева, а так же быстрый подсчет количества подчиненных узлов.

Код

Итак, код. В нем не используются никакие из методов модуля, а только сам объект.

sub optimize_counter {
# Получаем данные, где:
#   $self          - объект
#   $child_table   - имя подчиненной таблицы [firm_merge]
#   $counter_field - имя поля счетчика в основной таблице [firm_category.firm]
#   $parent_field  - имя поля ID родителя в подчиненной таблице [firm_merge.category]
#   $id_field      - имя поля ID элемента списка в подчиненной таблице [firm_merge.firm]
#   $where         - дополнительное условие выборки для обоих таблиц [p => '...', c => '...']

    my ($self, $child_table, $counter_field, $parent_field, $id_field, $where) = @_;
    $id_field = 'id' unless $id_field;
# Формируем запрос на выборку узлов всего дерева, но сортировку производим в обратном порядке
    my $sql = 'SELECT '.$self->{'id'}.' AS id, '.$self->{'level'}.' AS level
               FROM '
.$self->{'table'}.
               ($where->{'p'} ? ' WHERE '.$where->{'p'} : '').'
               ORDER BY '
.$self->{'left'}.' DESC';
    my $sth = $self->{'DBI'}->prepare($sql); $sth->execute() || die $DBI::errstr;
# Определяем временные переменные цикла, где:
#   $prev    - уровень предыдущего узла
#   @counter - счетчики по уровням
#   @ids     - ссылки на хеши ID элементов по уровням

    my ($plev, @counter, @ids);
    while (my $parent = $sth->fetchrow_hashref()) {
# Обнуляем счетчики и ID элементов при переходе на уровень ниже,
# но обнуляются только счетчики уровней ниже текущего

        if ($$parent{'level'} >= $plev || !$plev) {
            for (my $i = $plev; $i < $$parent{'level'} + 1; $i++) {
                $counter[$i] = undef;
                $ids[$i] = undef;
            }
        }
# Выбираем ID элементов списка подчиненного узлу, и в случае отсутсвия
# совпадения обновляем счетчики до корня дерева

        my $sql = 'SELECT '.$id_field.' AS id
                   FROM
'.$child_table.'
                   WHERE '
.$parent_field.' = '.$$parent{'id'}.
                          ($where->{'c'} ? ' AND '.$where->{'c'} : '');
        my $sth = $self->{'DBI'}->prepare($sql); $sth->execute() || die $DBI::errstr;
        while (my $child = $sth->fetchrow_hashref()) {
            for (1..$$parent{'level'}) {
                $ids[$_]->{$$child{'id'}}++;
                $counter[$_]++ if $ids[$_]->{$$child{'id'}} < 2;
            }
        }
        $sth->finish();
# Обновляем счетчик текущего узла
        $self->{'DBI'}->do('UPDATE '.$self->{'table'}.'
                            SET
'.$counter_field.'='.($counter[$$parent{'level'}] || ' 0').'
                            WHERE '
.$self->{'id'}.'='.$$parent{'id'}.'
                            LIMIT 1
');
# Обновляем временную переменную - уровень предыдущего узла
        $plev = $$parent{'level'};
    }
    $sth->finish();     return 1
}

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

Категория: Использование в Perl | Добавил: phoinix (2005-12-18) | Автор: Сергей Томулевич (aka Phoinix)
Просмотров: 1852 | Рейтинг: 0.0 |

Комментарии

 

Бесплатный конструктор сайтов - uCoz