27 янв. 2016 г.

Поиск кольцевых ссылок в реляционной базе данных рекурсивным SQL запросом

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

Между тем, существует способ однозначного выявления циклов без "наматывания" кругов по графу. Рассмотрим граф, показанный на рисунке ниже:

В базе данных он хранится в виде матрицы смежностей в таблице t:

  CREATE TABLE t (
    a INTEGER,
    b INTEGER
  )

Соответственно, исходные данные:

  INSERT INTO t VALUES (1, 2);
  INSERT INTO t VALUES (2, 3);
  INSERT INTO t VALUES (3, 4);
  INSERT INTO t VALUES (3, 5);
  INSERT INTO t VALUES (3, 6);
  INSERT INTO t VALUES (2, 7);
  INSERT INTO t VALUES (6, 1);

Для поиска кольцевых ссылок напишем запрос:

with recursive tree as
  (
    select distinct
      a as initial, a, b, -1 as prev
    from
      t

    union all
    
    select
      iif(tr.initial <> tt.a, tr.initial, -1) as initial,
      tt.a,
      tt.b,
      tr.a as prev
    from
      t tt JOIN tree tr ON
        tr.b = tt.a and tr.initial > 0

  )
select
  *
from
  tree
where
  initial = -1

Запрос покажет все первые сегменты A - B (и предыдущие, т.е. сегменты непосредственно приведшие к циклу, PREV - A), всех возможных циклов в заданных данных. В нашем случае:

INITIAL A B PREV
-1 1 2 6
-1 2 3 1
-1 2 7 1
-1 3 4 2
-1 3 5 2
-1 3 6 2
-1 6 1 3

При необходимости проверить не входит ли в кольцо конкретный узел, следует прописать его ИД в первом запросе рекурсивного CTE:

with recursive tree as
  (
    select distinct
      a as initial, a, b, -1 as prev
    from
      t
    where 
      a = _some_id_

    union all
    
    select
      iif(tr.initial <> tt.a, tr.initial, -1) as initial,
      tt.a,
      tt.b,
      tr.a as prev
    from
      t tt JOIN tree tr ON
        tr.b = tt.a and tr.initial > 0

  )
select
  *
from
  tree
where
  initial = -1

8 янв. 2016 г.

Конвертация параметров складских документов

Параметры складских документов изначально хранились в двоичном поле OPTIONS, в таблице GD_DOCUMENTTYPE.

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

Новая версия gedemin.exe хранит параметры в реляционном виде в таблице GD_DOCUMENTTYPE_OPTION. Один параметр -- одна запись, связанная с GD_DOCUMENTTYPE.

Для просмотра параметров добавлен грид в окно со списком типов документов.

Правильный порядок конвертации такой:

  1. К эталонной БД, на которой идет разработка, однократно подключаемся из командной строки с параметром /cdo:

    gedemin.exe /cdo

  2. Произойдет автоматическая конвертация опций после чего следует сохранить измененные ПИ.
  3. Грузим обновленные ПИ на базы, где используются складские документы.
Мы прекрасно понимаем, что есть базы предприятий, где нельзя загрузить типовые ПИ без риска утерять изменения, сделанные "по месту". Если на таких базах нет необходимости в настройке параметров складских документов, то можно продолжать работать новым экзешником со старыми (двоичными) параметрами. Если настройка типов необходима, то следует сконвертировать параметры командой gedemin.exe /cdo.

При этом, следует учитывать, что одноименные с параметрами на эталонной базе параметры, при такой конвертации, получат другие РУИДы. В этом нет ничего страшного, если мы не собираемся распространять ПИ с базы предприятия на другие базы. Если же перенос потребуется -- следует загрузить параметры с эталонной БД для того, чтобы переписать РУИДы на типовые.

Последнее верно и для случая, когда разработка ведется на нескольких базах данных. Например, есть база ETALON, где разрабатывается типовой склад, и база ETALON_RESTORAN, где разрабатывается бэк-офис общепита. В этом случае последовательно выполняем:

  1. gedemin.exe /cdo на базе ETALON и сохраняем с нее ПИ.
  2. gedemin.exe /cdo на базе ETALON_RESTORAN и ЗАГРУЖАЕМ на неё общие ПИ с базы ETALON.
  3. Только после загрузки сохраняем ПИ с базы ETALON_RESTORAN.
Конвертация не изменяет двоичных опций в поле OPTIONS. Поэтому в непредвиденных случаях всегда можно вернуться к исходному состоянию просто удалив все записи из таблицы GD_DOCUMENTTYPE_OPTION.