Добавил:
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 - рутины
- •Планирование файлов
- •Память и сборка мусора

3.13. ДИФФЕРЕНЦИАЛЬНОЕ ИНДЕКСИРОВАНИЕ 271
q
InsertIndexRecords(id,RecordValues)
d InsertIndexRecord("Figure",id,$p((RecordValues),"~",1))
d InsertIndexRecord("Color",id,$p((RecordValues),"~",2))
d InsertIndexRecord("Count",id,$p((RecordValues),"~",3))
q
DeleteIndexRecords(id,RecordValues)
d DeleteIndexRecord("Figure",id,$p((RecordValues),"~",1))
d DeleteIndexRecord("Color",id,$p((RecordValues),"~",2))
d DeleteIndexRecord("Count",id,$p((RecordValues),"~",3))
q
InsertIndexRecord(IndexName,id,Value)
l +^Index(IndexName,Value,id)
s ^Index(IndexName,Value,id)=""
l -^Index(IndexName,Value,id)
q
DeleteIndexRecord(IndexName,id,Value)
l +^Index(IndexName,Value,id)
k ^Index(IndexName,Value,id)
l -^Index(IndexName,Value,id)
q
В приведенном примере код UpdateRecord явно содержит перечень
имен всех свойств и способ получения их значений. В структуре алгоритма дифференциального перестроения индексов явно просматривается
возможность написания более обобщенного кода. В более общем случае
имеет смысл применять обобщенные функции вроде
GetAttrCount(rectype)
GetAttrName(rectype,attrnumber)
GetAttrValue(rec,attrnumber)
GetRecType(rec)
IsIndexed(rectype,attrnumber)
То есть тем или иным образом ввести а-ля объектный подход и информацию о метаданных. Это позволит реализовать код индексирования
для общего случая и применять его для множества различных структур.
В целом, введение дифференциального перестроения индекса может
приводить к уменьшению накладных расходов на индексирование в разы
по сравнению с неоптимизированным обновлением индексных записей "в
лоб". Поскольку данное усовершенствование относится к оптимизации,
его стоит отнести на последние этапы разработки.

272 ГЛАВА 3. ИНДЕКСАЦИЯ ДАННЫХ
3.14 Индексация длинных атрибутов
В реализации любых М систем в целях повышения эффективности реализаций вводятся ограничения на длину индекса. Ограничения такого
же характера присутствуют и в других, не-М реализациях СУБД. В распространенных реализациях М длина индексов включая имя переменной
ограничена 255 байт.
В случае применения сложных структур индексирования, составных
индексов или просто длинных строковых атрибутов возникает задача
выполнить индексацию в указанных ограничениях. Для решения этой
задачи может быть применено сегментирование значений атрибутов. В
индексе вместо отображения значения атрибута на набор идентификаторов прописывается отображение сегментов атрибута на набор идентификаторов.
Получить сегменты можно например так:
f i=0:1:$l(value)/n s @ind@(i+1)=$e(value,i*n+1,i+1*n)
Здесь n - число символов в одном сегменте.
При использовании сегментирования значения атрибута поиск выполняется указанием значений сегментов. При этом может возникнуть
ситуация поиска по значениям с длиной, кратной величине сегмента.
При этом возможно появление ситуации нахождения лишних записей,
имеющих первую часть значения атрибута совпадающего с искомым.
Чтобы исключить появление такой ситуации вводится дополнительная
структура - индекс на длину атрибута. И либо длина искомого фрагмента должна обязательно указываться при поиске, либо необходимо использовать полный набор всех сегментов, которые могут принадлежать
используемому значению.
После получения сегментов для каждого из них прописывается отображение сегмента на идентификатор. Например, используя модифицированный пример с простым индексом, для демонстрации используя сегменты длиной по 12 символов:
CreateRecords() ; k d CreateRecords^ind09() w
k ^Index
k ^Data
n i,Figures,Colors,Counts,Figure,Color,Count,id
s Figures="квадрат с такими разными и неровными »
краями~круг тоже не очень ровный~отрезок ну очень »
прямой, но только жаль что в плоскости~треугольник »
вообще какой-то очень правильный"
s Colors="красный~зелёный~синий~белый"

3.14. ИНДЕКСАЦИЯ ДЛИННЫХ АТРИБУТОВ 273
s Counts="2~5~12~8"
f i=1:1:12 d
. s Figure=$p(Figures,"~",$r(4)+1)
. s Color=$p(Colors,"~",$r(4)+1)
. s Count=$p(Counts,"~",$r(4)+1)
. s id=$$InsertRecord(Figure_"~"_Color_"~"_Count)
q
InsertRecord(RecordValues)
n id s id=$i(^Data)
l +^Data(id)
s ^Data(id)=RecordValues
d InsertIndexRecords(id,RecordValues)
l -^Data(id)
q id
DeleteRecord(id)
q:’$d(^Data(id))
l +^Data(id)
n RecordValues s RecordValues=$g(^Data(id))
d DeleteIndexRecords(id,RecordValues)
k ^Data(id)
l -^Data(id)
q
UpdateRecord(id,RecordValues)
q:’$d(^Data(id))
l +^Data(id)
n OldRecordValues s OldRecordValues=$g(^Data(id))
d DeleteIndexRecords(id,OldRecordValues)
s ^Data(id)=RecordValues
d InsertIndexRecords(id,RecordValues)
l -^Data(id)
q
DeleteIndex(IndexName)
k ^Index(IndexName)
q
InsertIndexRecords(id,RecordValues)
d InsertIndexRecord("Figure",id,$p((RecordValues),"~",1))
d InsertIndexRecord("Color",id,$p((RecordValues),"~",2))
d InsertIndexRecord("Count",id,$p((RecordValues),"~",3))
q
DeleteIndexRecords(id,RecordValues)
d DeleteIndexRecord("Figure",id,$p((RecordValues),"~",1))
d DeleteIndexRecord("Color",id,$p((RecordValues),"~",2))
d DeleteIndexRecord("Count",id,$p((RecordValues),"~",3))
q
InsertIndexRecord(IndexName,id,Value)
l +^Index(IndexName,id)
n i,segment,n
s n=12
f i=0:1:$l(Value)/n d

274 ГЛАВА 3. ИНДЕКСАЦИЯ ДАННЫХ
. s segment=$e(Value,i*n+1,i+1*n)
. s:segment’="" ^Index(IndexName,i+1,segment,id)=""
s ^Index(IndexName_" size",$l(Value)+1,id)=""
l -^Index(IndexName,id)
q
DeleteIndexRecord(IndexName,id,Value)
l +^Index(IndexName,id)
n i,segment,n
s n=12
f i=0:1:$l(Value)/n d
. s segment=$e(Value,i*n+1,i+1*n)
. k:segment’="" ^Index(IndexName,i+1,segment,id)
k ^Index(IndexName_" size",$l(Value)+1,id)
l -^Index(IndexName,id)
q
Здесь символом » обозначен перенос строки, которого в реальном
коде не должно быть.
В результате получаем индексные записи с сегментированными значениями атрибутов. Обратим внимание, что можно упростить схему блокировок, используя более короткое имя блокировки - означающее не
столько блокируемую переменную, сколько смысл операции - в данном
индексе идет операция с данным идентификатором.
Для выборки данных из индекса следует, конечно, учитывать, что
атрибуты сегментированы и искать следует те идентификаторы, для которых полностью совпадут все сегменты. Примерный код выборки из
одного сегментированного индекса, использующий многоиндексную выборку, может быть таким:
Select()
n Figure,ind,i,n,segment,res s n=12
s Figure="квадрат с такими разными и неровными краями"
f i=0:1:$l(Figure)/n d
. s segment=$e(Figure,i*n+1,i+1*n)
. s:segment’="" ind(i+1)=$na(^Index("Figure",i+1,segment))
s ind(i+1)=$na(^Index("Figure size",$l(Figure)+1))
s ind=$l(Figure)/n+1
; используем zig-zag ordered scan
d ANDv($na(res),.ind)
s res="" f s res=$o(res(res)) q:res="" d
. w res,!
q
ANDv(ret,names) g AND+1
AND(ret,names...)
n id,i,j,place
; идем по всем и запоминаем. контекст выборки - в переменных
; id - текущий идентификатор

3.15. МЕЖТАБЛИЧНЫЙ ИНДЕКС 275
; i - номер индекса в наборе индексов
; j - внутренная переменная прохода по набору индексов
; place - внутренняя переменная
s id="" f s i=1,id=$$ANDnext() q:id="" s @ret@(id)=""
q
ANDnext()
ANDrep
s id=$o(@names(i)@(id))
q:id="" "" ; кончилось хотя бы по одному - кончилось совсем
; и идем по всем кроме полученного и проверяем существование
; если хотя бы один не существует, то с него делаем следующий шаг
f j=i+1:1:i+names-1 s place=((j-1)#names)+1 »
q id ; по всем есть, значит подходит
i ’$d(@names(place)@(id)) s i=place g ANDrep
Применение сегментирования значений атрибутов может быть вызвано не только длинными значениями самих атрибутов, но и служебными
значениями в индексе, также занимающими место.
Такой метод может быть применен также для поиска записей по блобам, поскольку теоретически величина атрибута не ограничена. Поиск
выполним так же, как при использовании атомарного строкового атрибута - при точном совпадении значения атрибута.
3.15 Межтабличный индекс
Межтабличным индексом называется индексная структура, объединяющая в себе данные не для одного, а для двух или более логических
наборов данных. Записи в межтабличном индексе перестраиваются при
добавлении, изменении и удалении записей в наборах записей, данные
из которых входят в этот индекс. Основным назначением межтабличного
индекса является сокращение операций соединения таблиц.
Рассмотрим структуру данных вида Отдел - Подразделение - Сотрудник. Та сущность что правее в этой цепочке входит в ту что левее. Пусть
у каждой сущности есть два атрибута - идентификатор записи и название, или ФИО для сотрудника. Схема существенно упрощенная по отношению к реальной, но приведена исключительно в демонстрационных
целях. Сущности Подразделение и Сотрудник имеют соответственно дополнительные атрибуты - ссылки на Отдел и Подразделение. Структурно
схема данных может быть обозначена так:
Отдел
^Data("Otdel",id)=$lb(Name)
Подразделение

276 ГЛАВА 3. ИНДЕКСАЦИЯ ДАННЫХ
^Data("Podr",id)=$lb(Name,IDOtdel)
Сотрудник
^Data("Sotr",id)=$lb(Name,IDPodr)
Эта схема существенно нормализована, не содержит избыточной информации, и значения атрибутов зависят только от идентификаторов
строк. По приведенным данным можно выполнить любые операции добавления, удаления и изменения. Рассмотрим две тяжелых операции
- вывести фамилии сотрудников для заданного отдела и вывести наименование отдела для заданного сотрудника. Обе задачи решаются пошаговым прохождением через промежуточные структуры подразделения.
Межтабличные индексы могут решить задачу ускорения вывода. В нашем случае индексов будет два - по одному для каждой из задач.
CreateRecords()
k ^Data
n id
; сделаем отделы
f id=1:1:12 d InsertOtdel("Отдел "_id)
; сделаем подразделения
f id=1:1:30 d InsertPodr("Подразделение "_id,$r(12)+1)
; сделаем сотрудников
f id=1:1:300 d InsertSotr("Сотрудник "_id,$r(30)+1)
q
InsertOtdel(Name)
n id
s id=$i(^Data("Otdel"))
s ^Data("Otdel",id)=$lb(Name)
q:$Q id q
InsertPodr(Name,fk)
n id
s id=$i(^Data("Podr"))
s ^Data("Podr",id)=$lb(Name,fk)
q:$Q id q
InsertSotr(Name,fk)
n id
s id=$i(^Data("Sotr"))
s ^Data("Sotr",id)=$lb(Name,fk)
q:$Q id q
Здесь при создании записи сотрудника нам становится известным
полный путь от сотрудника к отделу. На самом деле это очень серьезное
соглашение - может ли быть запись которая ни на что не ссылается. В
нашем примере пусть будет нельзя. Также пусть будет нельзя удалять
те записи, на которые имеются ссылки. В нашем примере полный путь
между сотрудником и отделом определяется, таким образом, в момент создания записи о сотруднике, при удалении записи о сотруднике, а также

3.15. МЕЖТАБЛИЧНЫЙ ИНДЕКС 277
при изменениях внешних ссылок подразделения на отдел и сотрудника
на подразделение.
Структура поддерживаемого индекса будет фактически так или иначе отражать путь в условном графе принадлежности объектов одного
другому:
Для получения сотрудников по отделу
^Data("ind1",otdelid,podrid,sotrid)=""
Для получения отдела по сотруднику
^Data("ind2",sotrid,podrid,otdelid)=""
Пусть в нашей схеме данных поддерживается строгая дисциплина
ссылочности: не может быть подразделения без отдела и сотрудника
без подразделения. Исходя из этой упрощенной дисциплины значения в
индексах меняются при
1. при создании сотрудника
2. при удалении сотрудника
3. при изменение ссылочного значения сотрудника на подразделение
4. при изменении ссылочного значения подразделения на отдел
Соответственно для этих событий вводим функции поддержания индекса:
indAddSotr(idsotr)
n idotdel,idpodr
s idpodr=$li(^Data("Sotr",idsotr),2)
s idotdel=$li(^Data("Podr",idpodr),2)
s ^Data("ind1",idotdel,idpodr,idsotr)=""
s ^Data("ind2",idsotr,idpodr,idotdel)=""
q
indDelSotr(idsotr)
n idotdel,idpodr
s idpodr=$li(^Data("Sotr",idsotr),2)
s idotdel=$li(^Data("Podr",idpodr),2)
k ^Data("ind1",idotdel,idpodr,idsotr)
k ^Data("ind2",idsotr,idpodr,idotdel)
q
indChangeSotrFK(idsotr,newpodr)
n oldpodr,oldotdel,newotdel
s oldpodr=$li(^Data("Sotr",idsotr),2)
s oldotdel=$li(^Data("Podr",oldpodr),2)
s newotdel=$li(^Data("Podr",newpodr),2)
k ^Data("ind1",oldotdel,oldpodr,idsotr)

278 ГЛАВА 3. ИНДЕКСАЦИЯ ДАННЫХ
s ^Data("ind1",newotdel,newpodr,idsotr)=""
k ^Data("ind2",idsotr,oldpodr,oldotdel)
s ^Data("ind2",idsotr,newpodr,newotdel)=""
q
indChangePodrFK(idpodr,newotdel)
n oldotdel,idsotr
s oldotdel=$li(^Data("Podr",idpodr),2)
m ^Data("ind1",newotdel,idpodr)=^Data("ind1",oldotdel,idpodr)
k ^Data("ind1",oldotdel,idpodr)
s idsotr=""
f s idsotr=$o(^Data("ind1",newotdel, »
. k ^Data("ind2",idsotr,idpodr,oldotdel)
. s ^Data("ind2",idsotr,idpodr,newotdel)=""
q
idpodr,idsotr)) q:idsotr="" d
Приведенная схема отображения отдела на сотрудников (индекс ind1)
использует составной индекс. Он неудобен для многоиндексной выборки,
поскольку отображает соответственно отдел на неупорядоченный набор
сотрудников. Для индекса ind2 отображение единственно. Для того, чтобы можно было применять многоиндексную выборку сотрудников лучше
ввести дополнительный индекс, отображающий отдел на упорядоченный
набор сотрудников:
^Data("ind3",otdelid,sotrid)=""
indAddSotr(idsotr)
n idotdel,idpodr
s idpodr=$li(^Data("Sotr",idsotr),2)
s idotdel=$li(^Data("Podr",idpodr),2)
s ^Data("ind1",idotdel,idpodr,idsotr)=""
s ^Data("ind3",idotdel,idsotr)=""
s ^Data("ind2",idsotr,idpodr,idotdel)=""
q
indDelSotr(idsotr)
n idotdel,idpodr
s idpodr=$li(^Data("Sotr",idsotr),2)
s idotdel=$li(^Data("Podr",idpodr),2)
k ^Data("ind1",idotdel,idpodr,idsotr)
k ^Data("ind3",idotdel,idsotr)
k ^Data("ind2",idsotr,idpodr,idotdel)
q
indChangeSotrFK(idsotr,newpodr)
n oldpodr,oldotdel,newotdel
s oldpodr=$li(^Data("Sotr",idsotr),2)
s oldotdel=$li(^Data("Podr",oldpodr),2)
s newotdel=$li(^Data("Podr",newpodr),2)
k ^Data("ind1",oldotdel,oldpodr,idsotr)

3.16. ИНДЕКС С УСЛОВИЕМ НА ВСТАВКУ 279
k ^Data("ind3",oldotdel,idsotr)
s ^Data("ind1",newotdel,newpodr,idsotr)=""
s ^Data("ind3",newotdel,idsotr)=""
k ^Data("ind2",idsotr,oldpodr,oldotdel)
s ^Data("ind2",idsotr,newpodr,newotdel)=""
q
indChangePodrFK(idpodr,newotdel)
n oldotdel,idsotr
s oldotdel=$li(^Data("Podr",idpodr),2)
m ^Data("ind1",newotdel,idpodr)=^Data("ind1",oldotdel,idpodr)
k ^Data("ind1",oldotdel,idpodr)
s idsotr=""
f s idsotr=$o(^Data("ind1",newotdel, »
idpodr,idsotr)) q:idsotr="" d
. k ^Data("ind2",idsotr,idpodr,oldotdel)
. s ^Data("ind2",idsotr,idpodr,newotdel)=""
. k ^Data("ind3",oldotdel,idsotr)
. s ^Data("ind3",newotdel,idsotr)=""
q
В случае применения межтабличных индексов следует внимательно
пересмотреть политику отношения к ссылкам, а также степень востребованности запросов "через голову", требующих без межтабличных индексов долгих соединений. Возможно, что негативный фактор усложнения
операции добавления / удаления и увеличение объема хранения может
перевесить выгоды от эпизодического использования прямого индекса
между таблицами. В случае использования в индексе идентификаторов
записей операции обновления индексов по причине смены внешней ссылки в одной из промежуточных таблиц будут происходить нечасто, поэтому алгоритмическая нагрузка может быть совершенно незначительной.
Применение межтабличных индексов видится чрезвычайно оправданным для построения ROLAP хранилищ данных по схеме сильно разветвленной звезды с частично нормализованными данными. По сути,
применение именно таких индексов и упрощает структуру схемы для
операции выборки данных, оставляя сами данные нормализованными.
3.16 Индекс с условием на вставку
При поддержке индексных структур параллельно со структурами данных
может быть использовано хранение не всех индексных записей, а лишь
некоторых. Механизм поддержки индексов может использовать некоторое заданное условие для того, чтобы определить, следует ли вставлять
индексную запись или нет.

280 ГЛАВА 3. ИНДЕКСАЦИЯ ДАННЫХ
При применении этого механизна следует обращать внимание, зависит ли условие вставки индексной записи только от значений атрибутов
или также от иных факторов. Механизм удаления и обновления индексной записи может в первом случае использовать то же самое условие,
либо производить удаление индексной информации всегда и независимо
от результата вычисления условия.
Использование условной вставки индексной записи приводит к тому,
что в индексы попадает информация не о всех записях данных. Этот
факт может быть использован при выборке по условному индексу - при
выборке мы автоматически получаем только те записи, которые удовлетворяют заданному условию. Этот метод может оказаться во много раз
эффективнее применения сложной индексной структуры или сложных
алгоритмов выборки по нескольким индексам.
Кроме простой реализации достаточно сложного условия можно отметить другие плюсы условной вставки индексов, такие как уменьшение
объемов журналирования, ресурсов кеширования и дисковых операций.
Приведем пример условной вставки индексных записей. Здесь индексная запись по атрибуту Figure вставляется только если выполняется
условие. При этом в условии проверяется что значение атрибута Count
больше 1. При использовании индекса по атрибуту Figure мы автоматически получаем выборку с дополнительным условием Count > 1 - в
выборку попадут только записи попадающие под это условие.
CreateRecords()
k ^Index
k ^Data
n i,Figures,Colors,Counts,Figure,Color,Count,id
s Figures="квадрат~круг~отрезок~треугольник"
s Colors="красный~зелёный~синий~белый"
s Counts="2~5~12~8"
f i=1:1:12 d
. s Figure=$p(Figures,"~",$r(4)+1)
. s Color=$p(Colors,"~",$r(4)+1)
. s Count=$p(Counts,"~",$r(4)+1)
. s id=$$InsertRecord(Figure_"~"_Color_"~"_Count)
q
InsertRecord(RecordValues)
n id s id=$i(^Data)
l +^Data(id)
s ^Data(id)=RecordValues
d InsertIndexRecords(id,RecordValues)
l -^Data(id)
q id
DeleteRecord(id)
q:’$d(^Data(id))
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]
