Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
MUMPS СУБД. Практика применения и опыт программирования.pdf
Скачиваний:
0
Добавлен:
07.09.2026
Размер:
2 Мб
Скачать
2.5. СТРУКТУРЫ 201
любое время что угодно и прочитать это позднее. Не требуется давать объявление структуры ключей и их значений до того, как выполняется сама запись в глобал.
Общее правило записи в глобал таково: если значение по этому кючу не существовало, то оно создается. Если значение существовало, то оно перезаписывается. И операция записи не различает было ли по этому ключу ранее какое-либо значение. Существование или несуществование значения перед записью по ключу используется лишь при откате тран­закции для восстановления предыдущего состояния ключа.
Еще одно правило М систем состоит в том, что не требуется предва­рительная запись по ключу с меньшим числом индексов. Если требуется запись в глобал по ключу
^NAME(123,456)
то не требуется предварительная запись или существование данных по ключам
^NAME ^NAME(123)
Каждая пара ключ-значение хранятся независимо от того, есть ли в этой же базе данных пары с похожими ключами или нет.
Одновременно с тем М системы поддерживают три стандартные ко­манды
kill varname merge varname1=varname2 lock varname
Эти команды различают наличие или отсутствие у имен переменных вложенных индексов и рассматривают переменные как деревья. Если од­на из этих команд применена к имени переменной, то она автоматически применяется и к остальным переменным отличающимися от указанной большим числом индексов но совпадающими с исходной переменной на число ее индексов.
Например, если есть данные в глобале
^DATA(1)=12 ^DATA(1,"name")="Phoenix" ^DATA(1,"color")="green" ^DATA(2,"name")="Tornado" ^DATA(2,"color")="red"
202 ГЛАВА 2. ГЛОБАЛЫ
то команда удаления
kill ^DATA(1)
удалит записи
^DATA(1)=12 ^DATA(1,"name")="Phoenix" ^DATA(1,"color")="green"
но оставит записи
^DATA(2,"name")="Tornado" ^DATA(2,"color")="red"
При этом команда
kill ^DATA(1,"name")
удалит лишь запись
^DATA(1,"name")="Phoenix"
поскольку у нее более нет дочерних записей и оставит записи
^DATA(1)=12 ^DATA(1,"color")="green" ^DATA(2,"name")="Tornado" ^DATA(2,"color")="red"
Теми же правилами вложения имен пользуются команды lock и merge.
При проектировании структур разработчики на М традиционно счи­тают что в действительности оперируют не отдельными парами ключ­значение, а деревьями.
Если два имени совпадают на некоторую длину, то это вопринимается как вложенные записи, например структура записей
^DATA(2,"name")="Tornado" ^DATA(2,"color")="red"
воспринимается так: значения "name" и "color" входят в запись
^DATA(2)
И, по-другому, значения "name" и "color" существуют параллельно друг другу. Иногда это соглашение может обозначаться неполной запи­сью имени:
2.5. СТРУКТУРЫ 203
^DATA(2) = possible value of ^DATA(2)
,"name") = name of ....
,"color") = color of ...
Разработчики на М зачастую неявно оперируют такими соглашения­ми для различения понятий "ключ имеет значение", "вложенные записи" (иногда называемые структурированием по ключу), "однородные записи".
При использовании структуры вида "ключ имеет значение" традици­онно понимается естественное значение ключа, например
^DATA("Germany")="Berlin" ^DATA("France")="Paris"
Особенность такого строения в том, что в качестве значений ключа используется что-то содержательное, или естественный кл юч, относя­щийся к хранимой записи.
При использовании структуры вида "вложенная запись" традиционно понимается собственно вложение по дополнительному или нескольким дополнительным индексам, например
,"name") = name of ....
,"color") = color of ...
означает запись собственно интересующей разработчика части безот­носительно того, какие индексы будут предшествовать указанным. Эти записи могут быть вложены в разные по своей структуре переменные, например, в
^DATA(1) ^DATA(2) TMP ^TEMP($J,"select",1) ^TEMP($J,"select",2)
При этом для разработчика не суть важно, дочерними по отношению к чему являются эти записи, а важно что несколько записей с этими ключами существуют совместно.
При использовании структуры вида "однородные записи" применяется искусственный ключ для значения индекса, чтобы различить несколько по сути одинаково структурированных записей. В примере выше это последовательность
^DATA(1) ^DATA(2)
204 ГЛАВА 2. ГЛОБАЛЫ
Собственно содержательной частью в записях является то, что запи­сано после искусственно придуманного значения 1 или 2. Эти суррогат­ные ключи требуются лишь для разделения нескольких записей.
Кроме различения структуры записей по индексам, разработчики на М применяют также структурирование значений, записанных по ключу.
Само значение может рассматриваться не как одна последователь­ность байт, а в некоем определенном разработчиком формате. Традици­онно, М разработчики используют три вида структурирования значения:
1. Позиционное ($extract())
2. С разделителями ($piece())
3. Теговое ($list())
При позиционном структурировании в значении резервируется один или несколько байт по своему положению для хранения некой инфор­мации, например пусть некое значение занимает 2 символа, тогда мы можем хранить его в зарезервированных для него позициях, например
USER>s $e(var,3,5)="VS"
USER>w var=" VS"
USER>w $e(var,3,5) VS USER>s $e(var,3,5)="MK"
USER>w var=" MK"
Позиционное хранение данных довольно редко, но вероятность его применения возрастает при уменьшении длины одной порции и увели­чении вероятности неизменчивости структуры. Позиционный доступ в силу своего определения выполняется довольно быстро, но если он при­менен, то изменить структурирование впоследствии трудно.
При хранении данных с разделителями выбирается символ (или нес­колько), который используется для разделения порций записи на отдель­ные части. Само же значение разделителя не должно использоваться в хранимых фрагментах, например:
USER>s $p(var,"~",2)="Germany"
USER>s $p(var,"~",3)="Berlin"
2.5. СТРУКТУРЫ 205
USER>s $p(var,"~",1)="Europe"
USER>w var="Europe~Germany~Berlin"
USER>w $p(var,"~",3) Berlin
Использование разделителей позволяет хранить фрагменты перемен­ной длины. При этом, чтобы найти заданный по номеру фрагмент записи, система должна отсчитать разделители от начала значения.
Хранение значений в формате с разделителями традиционно исполь­зуется в больших системах, разработанных много лет назад. При таком способе можно использовать печатаемый символ в качестве разделите­ля и легко видеть что записано в различных позициях. При экспорте данных в текстовый файл его можно читать и изменять, не опасаясь за нарушение формата.
При теговом хранении данных в одном значении используется со­глашение что перед порцией данных записывается длина порции либо индикатор по которому можно эту длину вычислить. Система в этом случае отсчитывает положение данных, используя теги, и разработчи­ки могут использовать любые символы в данных, поскольку для такого формата нет зарезервированного символа.
Системы Cach´e и MiniM используют функции $list, разработанные изначально фирмой InterSystems для Cach´e. В этом формате вся после­довательность байт рассматривается как последовательность отдельных позиций, у каждой из которых есть тег. Таким образом, теговые струк­туры можно конкатенировать.
Например:
USER>s $list(var,2)="Germany"
USER>s $list(var,1)="Europe"
USER>s $list(var,3)="Berlin"
USER>w var=?Europe?Germany?Berlin"
USER>w $list(var,3) Berlin
USER>s list=$lb("Asia")_$lb("China")
206 ГЛАВА 2. ГЛОБАЛЫ
USER>w $li(list,1) Asia USER>w $li(list,2) China
Списковые структуры нельзя изменять в экспортированных данных или иными средствами, кроме набора функций $list, из-за довольно сложного определения тега одной части списка и из-за того, что теговая часть необязательно состоит только из печатных символов.
Автору в одном из проектов довелось встретить образцы кода на MUMPS, применявшие теговое структурирование самостоятельно, сред­ствами языка, до появления расширенных функций семейства $list. И, помнится, тогда это произвело впечатление своей простотой и элегант­ностью решения.
Разработчики на М традиционно используют различное структуриро­вание данных, как по именам так и по значениям, и М системы никоим образом им в этом не препятствуют. М системы не накладывают тре­бования на предварительное описание структуры, чтобы впоследствии проверять данные по образцу. В любое время, например, разработчик может добавить еще один или несколько фрагментов к структуре. Если использованные разработчикам соглашения об обращении к несуществу­ющим данным корректны с точки зрения разрабатываемой прикладной системы, то добавление структур не нарушает работу системы.
Например, в вышеприведенном примере со временем жизни системы к записям
^DATA(1)=12 ^DATA(1,"name")="Phoenix" ^DATA(1,"color")="green" ^DATA(2,"name")="Tornado" ^DATA(2,"color")="red"
могут добавиться записи
^DATA(1,"color","RGB")=... ^DATA(2,"city")="Berlin"
И разработчик может считать что вложенные уточнения "city" могут относиться к любой записи из набора, а вложенное уточнение "RGB" может относиться только к записям "color", ввести правило трактовки отсутствия такой дополнительной записи как значение по умолчанию или как правило чтения значения из иного источника, и так далее.
В отличие от систем жесткой типизации и структуризации, М систе­мы допускают мягкое расширение и дополнение структур на больших
2.6. ИНДЕКСАЦИЯ 207
данных. В отличие от sql-based или других типизованных систем, где операция alter по добавлению колонки может переформировать гигабай­ты данных, в М системах это просто не требуется, для хранения данных резервирования места в масштабах большой таблицы не требуется, и данные в базу будут добавлены лишь при их реальной записи.
Точно так же М система не будет препятствовать записи в локальную переменную большой сложной структуры без предварительного деклари­рования ее формата. Например, если выполняется команда merge
merge var("select",3)=^DATA(2)
то М система при копировании данных из ˆDATA(2) полностью воспро­изведет их структуру в переменной var("select",3), в ее дочерних ключах.
Для данных из вышеиспользуемого примера получим:
var("select",3,"name")="Tornado" var("select",3,"color")="red"
или, в укороченной записи:
var("select",3)
,"name")="Tornado" ,"color")="red"
Соответственно, если в глобале присутствовала запись
^DATA(2,"city")="Berlin"
то она также будет перенесена как есть:
var("select",3)
,"name")="Tornado" ,"color")="red" ,"city")="Berlin"

2.6 Индексация

Одной из наиболее важных тем для СУБД является построение индек­сов. Индексы - это параллельно поддерживаемые структуры данных, с использованием которых можно найти нужные данные или быстро вы­полнить некоторые операции, такие как проверка существования опре­деленных значений.
В отличие от других СУБД, М системы не имеют встроенных ме­ханизмов индексации и индексные структуры для М систем ничем не
208 ГЛАВА 2. ГЛОБАЛЫ
отличаются от просто данных. Разработчики самостоятельно решают, какие индексы необходимо поддерживать и в каких операциях их ис­пользовать, какой тип индекса и с какими алгоритмами применять.
М системы нисколько не препятствуют написанию прикладных си­стем на М таким образом, чтобы исполняющая часть автоматически могла использовать описанные разработчиками определения структур данных и индексов. С другой стороны, М системы не навязывают ника­ких методик, и все действия СУБД по индексации данных разработчики планируют самостоятельно.
Традиционно, при разработке прикладной системы на М, разработчи­ки либо используют уже готовые собственные наработки и библиотеки, либо прорабатывают один раз необходимый механизм и далее использу­ют его.
Б´ольшая часть индексных структур соответствует так называемым обратным, или инвертированным, спискам. Принцип их формирования в целом простой - если по идентификатору записи можно определить значение атрибута записи, то по записи в инвертированном списке по значению атрибута можно определить идентификатор записи. Это гру­бая формулировка в первом приближении, более развернуто индексация данных описана в главе "Индексация данных".
Если обобщенную запись обозначить структурой
^DATA(id)=attr1~attr2~attr3
то инвертированный список может быть или
^INDEX(attrN)=id
или
^INDEX(attrN,id)=""
Традиционно разработчики на М используют второй вариант как бо­лее общий и универсальный.
Соответственно, для поддержания индекса описываются функции, со­ответствующие операциям изменения индексируемых атрибутов - созда­ние, перезапись, удаление.
Общий принцип поддержки индекса состоит в следующем. При созда­нии новой записи добавляется еще одна запись в индексную структуру, при изменении записи удаляется индексная запись используя предыду­щее значение атрибута и добавляется новая индексная запись для нового значения атрибута, и при удалении записи удаляется индексная запись, используя текущее значение атрибута.
2.6. ИНДЕКСАЦИЯ 209
Алгоритмически для разработчиков это обычно выглядит как написа­ние соответствующего количества функций и обращение впоследствии к ним.
При этом, если разработчики видят повторяющиеся структуры, то, естественно, могут использовать один код, который автоматически опре­деляет имя глобала для хранения данных и индексов.
Например, если есть несколько справочников имеющих одинаковую структуру, то разработчики могут просто добавить функциям измене­ния одного справочника один параметр, идентифицирующий справочник. Этот параметр может использоваться на усмотрение разработчиками са­мым различным образом. Например, пусть есть два справочника City и Country с записями, имеющими один атрибут "Name" и исходные струк­туры
^City(id)=Name ^Country(id)=Name
с индексными структурами
^IndexCity(Name,id)="" ^IndexCountry(Name,id)=""
и с функциями добавления данных
AddCity(Name)
n id s id=$i(^City) s ^City(id)=Name s ^IndexCity(Name,id)="" q:$q id q
AddCountry(Name)
n id s id=$i(^Country) s ^Country(id)=Name s ^IndexCountry(Name,id)="" q:$q id q
Для исключения дублирования кода разработчик может добавить па­раметр в обобщенную функцию
AddDict(Dict,Name)
n id s id=$i(@("^"_Dict)) s @("^"_Dict)@(id)=Name s @("^Index"_Dict)@(Name)=id q:$q id q
210 ГЛАВА 2. ГЛОБАЛЫ
В этом случае при вызовах
d AddDict("City","Berlin") d AddDict("Country","Germany")
будут создаваться соответствующие записи в справочниках с индексны­ми записями для справочников City и Country. Конечно, эта же функция может быть использована для остальных справочников той же структу­ры.
На усмотрение разработчиков могут быть использованы различные методы, например, использование имени справочника в качестве значе­ния индекса структур, хранение перечня индексируемых полей в отдель­ном определении (метаданные) и многое другое.
Нужно понимать, что для М систем нет отдельного выделения за­писей по их назначению - будут это оригинальные данные, служебные индексы или временные структуры для преобразований. М система бу­дет выполнять ровно те операции и ровно таким образом, как их описал разработчик.

2.7 Группировка

Одна из наиболее востребованных практических методик после струк­турирования и индексирования данных - это операция группировки. В отличие от других СУБД, М системы позволяют ее выполнять макси­мально точно и в соответствии с решаемой задачей.
Операция группировки в базах SQL-типа объявляется опцией GROUP BY и зачастую сопровождается опцией ORDER BY. Те, кто имел дело с языком SQL, наверняка примерно представляют, что это такое и к чему приводит. Те же, кто не использует язык SQL, имеют, с одной стороны отсутствие простого декларативного объявления своих намерений, и, с другой стороны, ничем не ограничены. Рассмотрим виды группирования на языке М и их особенности, плюсы и минусы.
Группировка в общих словах - это операция выборки данных в таком виде, в котором значения колонок рассматриваются в качестве критерия объединения строк - строки с одинаковыми значениями в группирующих колонках объединяются в одну строку или один ключ, в зависимости от решаемой задачи.
Положим, что у нас есть набор исходных данных, на котором мы можем провести демонстрацию. В качестве примера выберем условную