Добавил:
ivanov666
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз:
Предмет:
Файл:MUMPS СУБД. Практика применения и опыт программирования.pdf
X
- •Предисловие
- •Введение
- •Среда исполнения
- •Команды
- •Команды присваивания
- •Условные команды
- •Команды передачи управления
- •Команды ввода-вывода
- •Служебные команды
- •Постусловия
- •Операторы
- •Переменные
- •Числа и строки
- •Функции
- •$DATA
- •$GET
- •$ORDER
- •$NEXT
- •$QUERY
- •$NAME
- •$QLENGTH
- •$QSUBSCRIPT
- •$ASCII
- •$CHAR
- •$EXTRACT
- •$PIECE
- •$LENGTH
- •$REVERSE
- •$FIND
- •$TRANSLATE
- •$JUSTIFY
- •$FNUMBER
- •$TEXT
- •$RANDOM
- •$VIEW
- •$SELECT
- •$STACK
- •Списковые функции
- •Битовые функции
- •Модули
- •Рутины
- •Передача параметров
- •Неопределенные значения
- •Шаблоны
- •Косвенность
- •Косвенность имени
- •Косвенность индексов
- •Косвенность метки
- •Косвенность аргумента
- •Косвенность шаблона
- •Интерпретатор
- •Голая ссылка
- •Очередность выполнения
- •Очередность вычисления выражений
- •Очередность вычисления имен
- •Стекование $test
- •Комментарий
- •Стандарт и расширения
- •Глобалы
- •B-дерево
- •Кодирование индексов
- •Размер блока
- •Кеширование блоков
- •Структуры
- •Индексация
- •Группировка
- •Каноничность индексов
- •Маппинг
- •Индексация данных
- •Общие принципы
- •Механизм поддержки индекса
- •Простой индекс
- •Составной индекс
- •Покрывающий индекс
- •Кластерный индекс
- •Хеш-индекс
- •Битмап индекс (bitmap)
- •Битслайс индекс (bitslice)
- •Нормирование значений
- •Выборки по индексу
- •Многоиндексная выборка (zig-zag)
- •Дифференциальное индексирование
- •Индексация длинных атрибутов
- •Межтабличный индекс
- •Индекс с условием на вставку
- •Индекс на вычисляемый атрибут
- •Индекс поиска по фрагменту
- •Индексация для шаблона (like)
- •Индексация уникального атрибута
- •Массовое перестроение индексов
- •Операции с древовидными индексами
- •Операции с битовыми индексами
- •Сортировка по индексу
- •Статистики и кардинальность
- •Конкурентный доступ
- •Параллельность выполнения
- •Блокировки
- •Функция $INCREMENT
- •Транзакции
- •Блокировки в транзакциях
- •Функция $BIT
- •Дедлоки
- •Обработка ошибок
- •Состояние ошибки
- •ZTRAP
- •GT.M
- •MiniM
- •ETRAP
- •Определение
- •$ETRAP
- •$ECODE
- •$ESTACK
- •Ошибки в обработчике ошибок
- •$STACK()
- •Трассировка
- •BREAK
- •MiniM Debugger
- •Serenji Debugger
- •Внешний мир
- •Общие принципы
- •Терминальный интерфейс
- •Сокеты
- •HTTP клиент
- •Вебсервер на MUMPS
- •WebLink
- •Проблемы HTTP
- •Поверх HTTP
- •Подключаемые DLL (SO)
- •Файлы
- •Внешние процессы
- •Порты
- •Практика применения
- •Терминальный режим
- •Редакторы рутин
- •Экспорт и импорт
- •Препроцессор
- •Формат $HOROLOG
- •Опции устройств
- •$X и $Y
- •Возврат результатов
- •Возврат по значению ($$)
- •Возврат по ссылке
- •Запись в предопределенную переменную
- •Возврат значений косвенно
- •Итеративный возврат
- •Потоковый возврат
- •%Z - рутины
- •Планирование файлов
- •Память и сборка мусора

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, имеют, с одной стороны
отсутствие простого декларативного объявления своих намерений, и, с
другой стороны, ничем не ограничены. Рассмотрим виды группирования
на языке М и их особенности, плюсы и минусы.
Группировка в общих словах - это операция выборки данных в таком
виде, в котором значения колонок рассматриваются в качестве критерия
объединения строк - строки с одинаковыми значениями в группирующих
колонках объединяются в одну строку или один ключ, в зависимости от
решаемой задачи.
Положим, что у нас есть набор исходных данных, на котором мы
можем провести демонстрацию. В качестве примера выберем условную
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]
