Hare.ru @ Коллективный разум / Hare.ru @ Дикое место

Архив hare.ru 
Мысли, конвертированные в текст

Концептуальные работы


Все статьи раздела

Доступ к данным V7 из других систем. Импорт данных. Документы.

Андрей Малкин (где-то в 2001)

Импорт данных из документов


ПРИЛАГАЕМЫЙ СОФТ
Издатель этой статьи, а я далее буду именовать WildHare именно так, назвал эту публикацию «концептуальной». Он попал в самую точку! Исследование структуры V7, которая, в свою очередь, отражает движение абстракций, да ещё и вводит собственные – есть не что иное, как самый махровый концептуализм (представителем которого, кстати, был цитируемый мною ранее Оккам).

Для того, чтобы продолжить строительство модели доступа к системе хранения информации в V7, придётся вернуться немного назад. Итак, что получено в результате предыдущей работы? Главное, на мой взгляд, это возможность представить метаданные, описывающие хранимую информацию, в виде таблиц с которыми можно работать стандартными методами той или иной среды.

В первой (MetaObjects) хранится список объектов хранения (наименования, внутренние и десятичные идентификаторы, наименование класса). Во второй (metaObjectsProps), связанной с первой отношением 1:M – свойства (properties) каждого объекта. Функция, отдающая значение PropertyValue свойства с именем PropertyName объекта ObjectID, может тогда выглядеть примерно так:

SELECT metaObjectsProps.PropValue AS [PropertyValue] (Q1)
FROM MetaObjects INNER JOIN metaObjectsProps
ON MetaObjects.ObjKey = metaObjectsProps.ObjKey
WHERE (metaObjectsProps.PropName=[PropertyName])
AND (MetaObjects.ID=[ObjectID]);

Получить иерархию самих объектов можно с помощью ссылки на поле-родитель. Так, например, получить идентификатор объекта-родителя (Parent_ID) для объекта ObjectID можно следующим образом:

SELECT Parent.ID AS [Parent_ID] (Q2)
FROM _MetaObjects AS Child INNER
JOIN _MetaObjects AS Parent
ON Child.ParKey = Parent.ObjKey
WHERE Child.ID=[ObjectID];
а список объектов-потомков (Child_ID) так:
SELECT Child.ID AS [Child_ID] (Q3)
FROM _MetaObjects AS Child INNER
JOIN _MetaObjects AS Parent
ON Child.ParKey = Parent.ObjKey
WHERE Parent.ID= [Parent_ID];

Фактически, эти запросы и есть тот мост между метаданными (которые и определяют структуру хранения) и данными из DBF (SQL). Остаётся только надеяться, что данных из MD хватит для имитации (повторения) действий V7 при построении 1cv7.dd (dds) и использовании его в работе.

В качестве первого примера для доступа я решил выбрать класс Документы. Из всего того, что есть в метаданных, он явно выделяется своей логической стройностью (продуманностью?). В самом деле:

  • единый ключ (IDDOC) для всех таблиц 1SJOURN, DHxxx, DTxxx;
  • для таблиц 1SJOURN и DHxxx он является первичным ключом;
  • для таблиц DTxxx IDDOC выступает как foreign key;
  • таблица 1SJOURN нигде явно не выступает в отношениях 1:M со стороны M;
  • записи таблиц DTxxx уникально идентифицируются через IDDOC+ LINENO;
  • реквизиты документов не могут быть периодическими.
Файлы (таблицы) 1SOPER и 1SENTRY, в которых хранятся операции и проводки в компоненте Бухгалтерский учет, фактически являются вырожденными DHxxx и DTxxx. При организации доступ к данным этих классов будут использоваться те же методы, что и при работе с документами.

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

Если речь идет о «Доступе к данным V7 из других систем», то этой другой системой совершенно не обязательно должен быть MS Access (он используется только для отработки технологии). Более того, совершенно необязательно, чтобы эта другая система работала под Win32! Нет никаких видимых препятствий тому, чтобы, предположим, реализовать задуманное на связке Perl+MySQL или под чем-нибудь GNU-тым.

Именно по этой причине избрана методика, при которой данные V7 будут «выгребаться» автоматически формируемыми на основе метаданных запросами. Ну а сами запросы – естественно будут написаны на «родном» языке структурированных запросов, т.е. на SQL.

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

  1. столбцы (поля) таблиц должны иметь осмысленные наименования, по которым можно представить их содержимое;
  2. должна существовать возможность получить из системы более полную информацию (условно – комментарий) о характере данных, хранящихся в таблицах. То есть должны существовать какие-то метаданные, с нормальным механизмом доступа. Естественно – храниться они тоже должны в таблицах и быть доступны через SQL. Если бы такое было в V7, то и не было бы нужды заваривать всю эту кашу ;-).
  3. информация в таблицах должна быть представлена в не в виде ссылок на данные, а в виде непосредственно самих данных (в терминах предметной области).
Пункт (c) необходимо прокомментировать. Данные V7 хранятся лишь в частично нормализованном виде (в терминах реляционных БД). Хранение документов, о котором сказано ранее, нормализовано лишь внешне, на уровне ключей таблиц. Стоит только взглянуть на содержимое полей – и иллюзии по этому поводу исчезают. Поэтому, никакой нормализации при импорте/присоединении не будет.

Сначала размежеваться – а потом соединиться. Сначала денормализовать, а уж потом приводить к первой, второй, и т.п. нормальным формам.

Небольшие комментарии к модели. Первым делом импортируем файлы 1SJOURN, DHxxx и DTxxx. На этом этапе не рассматривается окончательная технология (импорт или присоединение таблиц). В дальнейшем будет показано какой метод и по каким причинам предпочтителен (оптимален), а пока скажу, что для разных версий системы (7.5 и 7.7) он не одинаков.

Проблемы с кодировкой, с которыми сталкиваются многие при импорте данных V7, используя Jet ISAM (dbf в OEM в 7.5 и в ANSI в 7.7), решаются изменением значения параметра DataCodePage в HKLM\Software\Microsoft\Jet\xxx\Engines\Xbase на "OEM" или "ANSI". Причем, достаточно только инициализировать драйвер с нужными значениями, дальнейшие изменения значения ключа после инициализации значения не имеют.

Список DH (DT) файлов с их именами (как в V7) получается с помощью запроса:

SELECT "DH"& Trim(ID), "DT"& Trim(ID), Sname
FROM _MetaObjects WHERE ClassName="documents";

Для формирования запросов к файлам DH (DT) нужно знать имена полей этих файлов, типы хранящихся в них данных, настоящие имена полей, и некоторую другую информацию. Эту информацию можно получить используя запросы (Q3) или в более общем виде, одним запросом типа Pivot/Transform/Rollup (в зависимости от диалекта SQL).

Для документа с ключом [DocKey] этот запрос будет выглядеть так:

TRANSFORM t1.PropValue) (Q4)
SELECT t.ID_Int, t.ID, t.Sname, t.ClassName
FROM _MetaObjects AS t
INNER JOIN _metaObjectsProps AS t1 ON t.ObjKey = t1.ObjKey
WHERE ((t.parkey=[ DocKey]) AND t.ClassName = "Head Fields"))
(OR ((t.ClassName)="GenJrnlFldDef")
GROUP BY t.ID_Int, t.ID, t.Sname, t.ClassName PIVOT t1.PropName;"

Класс GenJrnlFldDef (общие реквизиты) включен из-за того, что в нем описана часть полей таблиц DHxxx (те, у которых не установлен признак отбора). Наиболее важными значений из полученных являются:

  • id – десятичный ID поля (имя поля в таблице DHxxx = "SP"&[id]),
  • Sname – имя поля,
  • Type – тип поля,
  • Source_Id – внешний источник данных для поля нескалярного типа,
  • Size – размер поля.
В получаемых при доступе представлениях полей таблиц документа есть определенные особенности. Дело в том, что поля нескалярного типа – справочник, перечисление, счёт – должны отображаться в «правильном», содержательном виде. Но ни одна система, кроме V7, естественно, не имеет базовых типов Справочник (а уж тем более Субконто), поэтому отображаться и храниться эти значения будут в виде текста.

По-другому, думаю, и невозможно сделать по принципиальным соображениям. Если значения поля типа «определенный справочник» (справочник контрагентов, например) можно представить как множество значений, определенных на домене «организации», а поле типа «справочник сотрудников« – на домене «люди», то как быть со справочником «вообще«? Там всё смешалось, кони, люди…

Нет уже никакой предметной области. О полях типа типа «неопределенный вообще», где могут успешно сосуществовать не только объектные, но и скалярные типы, и говорить нечего. Это замечательно придумано и реализовано для реализации задач на V7, но является самым главным препятствием для решения поставленной задачи осмысленного доступа к данным. Строгости определений явно не хватает ;-).

В общем, такие поля будут исключительно текстовыми и будут содержать, помимо значения, ещё и какой-то текст, помогающий понять, что это за значения. Вот это то и есть концентрированное выражение концептуализма, о котором я говорил в начале главы.

Правила преобразования, применяющиеся при доступе к данным:

  • поля скалярных типов типов (N,S,D) – не преобразовываются;
  • у полей типа B берется Код (Code) либо Наименование (Descr), в зависимости от установленного в конфигураторе основного представления справочника (Code_Type из таблицы metaObjectsProps) и наличия или отсутствия этих полей (в 7.7 это стало возможно);
  • поле типа Перечисление представляется наименованием (не представлением!);
  • поля типа Документ – представляются [Наименованием документа] + номер документа (из 1SJOURN);
  • у полей типа Счет берется КодСчета из 1SACCS (наименование не берется из-за обычно большого количества этих полей и длинных наименований). Хотя, наименование, конечно, более информативно и вносит меньше путаницы при обилии различных планов счетов;
  • поле типа ПланСчетов представляется своим наименованием;
  • поле типа K (вид субконто) представляется как [Наименование субконто]&[Значение субконто];
  • поля типа Календарь и ВидРасчета представляются также наименованием.
Эти правила – не догма, ничто не может помешать изменить их в дальнейшем, но на данный момент они зафиксированы.

Реализация преобразования (доступа) очень сильно зависит от версии V7 и инструмента, с помощью которого это преобразование выполняется. Смысл вышесказанного заключается в коренном отличии ключей DBF в этих версиях: если в 7.5 они 64-ричные (интересно, а как это выговорить?), то в 7.7 – 36-ричные. Первые содержат строчные буквы, вторые – нет.

Но не все из существующих сегодня систем (по крайней мере настольных) могут использовать регистрозависимые первичные ключи. По крайней мере те, которые базируются на механизме Microsoft JET – не могут. А таких среди настольных систем немало. Подробности, возможно, позже, а пока ограничичусь только замечанием, что «поля вообще» в 7.7 преобразовываются как указано выше, а в 7.5 – As Is.

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

К наименованиям полей в формируемых запросах добавляется префикс – символ, соответствующий типу поля (B,T,…): строчный в случае поля опредёленного типа, и прописной в случае неопределенного типа. Это сделано исключительно в целях наглядности и не несет никакой другой нагрузки.

Запросы формируются по одному на каждый документ и сохраняются под именем соответствующей таблицы DBF – DHxxxx. Внутри каждого запроса – стандартные конструкции SQL. Все таблицы в запросах – под alias'ами. В запросах фактически отсутствуют конструкции WHERE, и максимально (может быть, даже избыточно) применяется JOIN. Связано это как с тем, что в V7 отсутствует поддержка неопределенных значений (NULL), так и с тем, что генерацию кода для реализации левого внешнего объединения на составных ключах (типа IDDOC+OBJID) проще реализовать через LEFT JOIN + дополнительный unique key.

Кроме этого, есть некоторые отличия в реализации последовательности применения JOIN и WHERE на внешних объединениях в разных SQL-системах (по крайней мере, отличия от ANSI92 существуют).

На самом деле, сам код проще, чем его описание ;-). Вся работа с генерацией запросов собрана в одном модуле (FormQuery), импорт/присоединение таблиц – в модуле DBFWork. Советую поэкспериментировать с прилагаемым софтом, только помните что это не коммерческое изделие, производительность которого не должна опускаться ниже определенных границ, а лишь его модель, от которой требуется в первую очередь функциональность, а лишь затем производительность.

В прилагаемой версии модели, помимо разбора MD и чтения DBF, реализован экспорт прочитанной структуры метаданных в ALS-файл. Файл с именем ALSfromMDB помещается в тот каталог, откуда был прочитан соответствующий ему 1cv7.md, автоматически, сразу после чтения метаданных.

В нём же содержится структура классов, на основе которой разбирается MD. А для доступа к данным ALS из других систем (ведь ALS – это тоже часть V7), я рекомендую воспользоваться конвертером als2html Дениса Абросимова. Кстати, по структуре и особенностям работы ALS-файлов V7 информации в Сети фактически гораздо меньше, чем по MD. А особенности есть. Вплоть до GPF.

И это наводит на очень грустные размышления: ведь визуальная структура ALS очень похожа на структуру MMS (а внутренняя – совпадает один в один), и его использование в качестве средства визуализации конфигураций просто напрашивается само собой.

В следующей части – работа со справочниками.

Партнеры:


Также может быть интересно:

Канал Россия 1 на http://spbtvonline.ru/
   
 Сайт поддерживается за счет партнеров:
:::... Сайт содержит архив двух версий hare.ru Карта сайта