Добавил:
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.16. ИНДЕКС С УСЛОВИЕМ НА ВСТАВКУ 281
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
Condition(RecordValues)
n Count
s Count=$p(RecordValues,"~",3)
q Count>1
InsertIndexRecords(id,RecordValues)
i $$Condition(RecordValues) d
. 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)
s ^Index(IndexName,Value,id)=""
q
DeleteIndexRecord(IndexName,id,Value)
k ^Index(IndexName,Value,id)
q
Здесь мы при удалении индексной записи не проверяем условие, поскольку для выполнения команды kill не требуется существования соответствующего узла.
При использовании условной вставки может поддерживаться несколько индексов по одному атрибуту с различными условиями вставки. Таким образом, может быть реализован механизм очень простой выборки с несколькими сложными условиями. Технически оказывается проще
сформулировать условие отбора в виде отдельной функции и использо-

282 ГЛАВА 3. ИНДЕКСАЦИЯ ДАННЫХ
вать ее при вставке индексных записей, чем то же самое условие описывать в виде алгоритма выборки. В частности, в условие вставки хорошо
вынести тяжелые для оптимизации элементы, например, операцию логического ИЛИ.
Как видно, наличие условия на вставку индексной записи никак не
связано со структурой индекса - условие может быть комбинировано с
любым типом индекса.
Применение индексов с условием на вставку, в действительности,
исходя из характеристик таких индексов - это довольно тонкая задача. Если при проектировании системы вообще сначала неизвестно, какие именно задачи будет выполнять структура данных, то применение
условного индекса может быть не оправдано из-за возрастания сложности разработки (увеличивается количество используемых сущностей).
При тонкой настройке и оптимизации системы наоборот - один универсальный полный индекс может быть заменен на несколько условных.
При такой замене можно прогнозировать небольшое замедление при обновлении записей данных и существенное ускорение при поиске по ним
с использованием «специализированных» индексов.
3.17 Индекс на вычисляемый атрибут
Индекс на вычисляемый атрибут - это индексная поисковая структура,
использующая не хранимые значения атрибутов, а вычисляемые на основе значения одного или совокупности значений нескольких атрибутов.
Индексная структура соответствует обычному индексу, но отображает
на набор идентификаторов условный виртуальный атрибут, значение которого вычисляется.
При индексировании вычисляемых атрибутов используется соглашение, что одним и тем же правилом вычисления должны пользоваться
функции вставки и удаления индексной записи. Это необходимо для того, чтобы поддержка поисковых структур была корректной. При этом
функция вычисления значения такого вычисляемого атрибута должна
зависеть лишь от значений другого или совокупности других атрибутов. Это требуется для корректного удаления индексной записи и при
обновлении строки данных.
Индексация вычисляемых атрибутов имеет характеристики:
1. Значение индексируемого атрибута не хранится.
2. Функция вычисления может быть довольно сложной.

3.17. ИНДЕКС НА ВЫЧИСЛЯЕМЫЙ АТРИБУТ 283
3. Функция вычисления может приводить вычисляемое значение к
индексной сортировке.
4. Функция вычисления может использовать не только логические
атрибуты самой строки данных, но и атрибуты других объектов,
жестко с ней связанных.
Исходя из характеристик индексов на вычислямые атрибуты, они при-
меняются в соответствующих специфических задачах:
Поиск по специфически заданному условию на хранимый атри-
бут
Например если у объекта есть атрибут дата, но поиск накладывает
ограничения на день недели. В этом случае мы можем поддерживать
индекс по вычисляемому атрибуту "день недели", значение которого вычисляется на основе поля даты.
Сортировка объектов в сложном порядке
Например, вывод объектов имеющих иерархическую организацию взаимного отношения. При поддержке индекса, ориентированного на специальную сортировку, мы можем многократно использовать хранимый
индекс вместо того, чтобы строить его каждый раз.
Поиск по значениям атрибутов подчиненных объектов
В этом случае значения подчиненных объектов рассматриваются как
основа для вычисления вычисляемого атрибута. Поиск может выполняться наложением условия как на значение атрибута индексируемого
объекта, так и на значения подчиненных объектов одновременно, что
позволяет путем поддержки пусть и сложной функции вычисления вычисляемого атрибута добиться очень простого и быстрого поиска.
Реализация быстрого сложного поиска
Возможность вычислять значение вычисляемого атрибута позволяет
объединить в функции вычисления довольно сложное условие, в том
числе «тяжелые» для поиска операции, например логическое ИЛИ и
использовать небольшое утяжеление действий по поддержке такого индекса, но существенно выиграть при поиске данных.
Индексация вычисляемого атрибута может быть выполнена также во
многих не-MUMPS системах баз данных путем добавления еще одного
хранимого атрибута и поддержкой индекса для него. Этот добавляемый
атрибут просто должен автоматически поддерживаться в соответствующем состоянии триггерами. При изменении значений атрибутов, на основе которых вычисляется этот дополнительный атрибут, триггеры просто
должны обновить его значение. В этой ситуации хранение дополнительного атрибута просто технически избыточно и логически необязательно,

284 ГЛАВА 3. ИНДЕКСАЦИЯ ДАННЫХ
хотя во многих системах баз данных оно и не может быть полностью
устранено.
Один из примеров применения вычисляемых атрибутов - вхождение
объекта в иерархическое отношение с другими объектами. Например, если в системе вводится условное деление значений атрибутов на группы.
В обычном варианте применяется введение дополнительной связывающей таблицы, отображающей номер группы на допустимые значения,
входящие в нее. При этом возникает проблема поддержки полного перечня такого отображения, и, кроме того, при выборке возникает относительно тяжелая операция соединения двух таблиц. В то время как
введение вычисляемого атрибута и индексация по нему решает вопрос
поиска наиболее элегантным образом.
Кроме технических проблем в данном случае упрощается сопровождение и развитие системы - со временем сложность классификации
может быть существенно изменена, и в классификацию может попасть
очень сложное условие, поддерживать же в этом случае структуру данных для отображения будет еще сложнее, если вообще это будет целесообразно.
3.18 Индекс поиска по фрагменту
Для поиска по фрагменту значения атрибута такой индекс запоминает
набор фрагментов и по каждому фрагменту поддерживается отображение
на набор идентификаторов записей, в которых он встретился. Структурно такой индекс может быть как инвертированным списком, так и
битовым.
При построении индекса должна быть задана схема выделения фрагментов из значения атрибута. Простой индекс при таком подходе тоже
может быть назван индексом поиска по фрагменту, просто на одно значение атрибута в данном случае приходится один фрагмент. Сложность
выполняемого поиска по такому индексу определяется сложностью алгоритма, задающего разбиение значения на фрагменты.
Одной из самых тяжелых операций поиска в базах данных является
поиск по фрагменту значения. При использовании специального индекса для такого поиска, разумеется, поиск можно существенно облегчить.
Но также внимательно следует отнестись к составлению алгоритма выделения фрагментов, поскольку именно совпадение этого алгоритма и
поискового шаблона определяет применимость индекса.
Приведем простой пример построения индекса по фрагменту, с поиском по любой заданной части значения атрибута. Алгоритм выделения

3.18. ИНДЕКС ПОИСКА ПО ФРАГМЕНТУ 285
фрагментов просто берет подстроки значения атрибута по схеме:
USER>s Value="green blue"
USER>f i=1:1:len w $e(Value,i,len),!
green blue
reen blue
een blue
en blue
n blue
blue
blue
lue
ue
e
Задание фрагментов в таком виде, используя соглашения индексного упорядочения, позволяет быстро найти любой объект, содержащий
искомый фрагмент. Приведем небольшую учебную реализацию с такой
индексацией:
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))
l +^Data(id)
n RecordValues s RecordValues=$g(^Data(id))
d DeleteIndexRecords(id,RecordValues)
k ^Data(id)
l -^Data(id)

286 ГЛАВА 3. ИНДЕКСАЦИЯ ДАННЫХ
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
InsertIndexRecords(id,RecordValues)
d InsertIndexRecord("Figure",id,$p((RecordValues),"~",1))
d InsertIndexRecord("Color",id,$p((RecordValues),"~",2))
q
DeleteIndexRecords(id,RecordValues)
d DeleteIndexRecord("Figure",id,$p((RecordValues),"~",1))
d DeleteIndexRecord("Color",id,$p((RecordValues),"~",2))
q
InsertIndexRecord(IndexName,id,Value)
n i,len s len=$l(Value)
f i=1:1:len s ^Index(IndexName,$e(Value,i,len),id)=""
q
DeleteIndexRecord(IndexName,id,Value)
n i,len s len=$l(Value)
f i=1:1:len k ^Index(IndexName,$e(Value,i,len),id)
q
FindFigure(part,ids)
n ref,id
s ref=$na(^Index("Figure",part))
f s ref=$q(@ref) q:(ref="")!($qs(ref,2)’[part) d
. s id=$qs(ref,3)
. s ids(id)=""
q
Здесь функция FindFigure выдает в подиндексы переменной ids най-
денные идентификаторы, например
d FindFigure^indpart("тре",.ids)
находит объекты, у которых атрибут Figure имеет значение "отрезок" и
"треугольник".
В действительности иногда приходитcя искать по гораздо более сложному условию, в котором используется не один фрагмент, а несколько,
и накладывается условие на взаимное расположение найденных фрагментов, на расстояние между фрагментами в значении. В этом случае
требуется внимательное составление правила получения фрагментов по
значению атрибутов, чтобы учесть все предъявляемые требования. В

3.19. ИНДЕКСАЦИЯ ДЛЯ ШАБЛОНА (LIKE) 287
SQL - ориентированных базах данных есть выражение для условия поиска LIKE. Приведенная техника индекса поиска по фрагменту примерно
соотносится именно с таким условием поиска. Но условие LIKE, конечно,
имеет гораздо большую сложность.
3.19 Индексация для шаблона (like)
Индексация для поиска по шаблону представляет собой одну из наиболее
интересных и занятных задач-головоломок. Общая формулировка задачи
такова: построить индекс со структурой и алгоритм поиска так, чтобы
поиск по шаблону значения атрибута выполнялся наиболее быстро.
Прямым применением традиционных индексных структур такая задча в общем случае не решается. Обычные индексы могут быть использованы только если шаблон значения атрибута начинается на какие-то
символы. Если начало шаблона определено, то мы можем спозиционироваться в индексе на это начало и дальше пройти перебором значений до
тех пор пока начальная часть значений в индексе не перестанет соответствовать шаблону.
Конечно, во-первых, в общем случае это условие не выполняется и,
во-вторых, перебор индексированных значений по начальной части поиска может пройтись по десяткам мегабайт диска. Для малых баз данных
перебор обычно не критичен, но для больших реальных инсталляций
такой подход существенно увеличивает число операций с глобалами и
может прокачать через кеш множество бесполезных блоков базы данных.
Более того, можно сказать, что для малых баз данных и сама индексация скорее может быть вредной. Если данных в базе данных мало,
то намного эффетивнее выполнять просто переборы по кешу, если сами
данные помещаются в памяти, чем регулярно обращаться к индексным
структурам, которые вымывают кеш и усложняют логику работы. Индексация для поиска по шаблону по самой постановке вопроса и по степени
его привередливости традиционно относится к реально большим базам
данных, где построение индексов соответствующих выполняемой задаче
действительно дает преимущество.
В качестве одного из вариантов индексации для поиска по шаблону может быть использован принцип максимального отсечения неподходящих вариантов. Для этого индексируемое значение разбивается на
последовательности в 1, 2, 3 и т.д. символов. Например, строка
abcd
разбивается на подстроки

288 ГЛАВА 3. ИНДЕКСАЦИЯ ДАННЫХ
a
b
c
d
ab
bc
cd
abc
bcd
При задании шаблона поиска шаблон трансформируется также в по-
следовательность подстрок, которые он содержит, например шаблон
ab*d
разбивается на подстроки
a
b
d
ab
После этого производится поиск аналогичный многоиндексной выборке, как если бы у искомой записи было несколько атрибутов, с применением алгоритма zig-zag. Основной задачей такого подхода является пропуск заведомо неиспользуемых подстрок и отсечение как можно
большего числа неподходящих записей.
Для построения индекса по шаблону используются служебные функции разбиения слова и шаблона на части:
DEPTHS() q 3 ; use 1,...,3 symbols
MAKEPARTS(str,parts) ; use as parts all symbols
n depth=$$DEPTHS(),d,pos,i,substr
f i=1:1:depth d
. f pos=1:1:$l(str)-i+1 d
. . s substr=$e(str,pos,pos+i-1) s:substr’="" parts(substr)=""
q
MAKEPATPARTS(str,parts)
; use as parts only symbols between * and ?
q:str=""
n i f i=1:1:$l(str,"*") d MAKEPATPARTS2($p(str,"*",i),.parts)
q
MAKEPATPARTS2(str,parts)
q:str=""
n i f i=1:1:$l(str,"?") d MAKEPATPARTS3($p(str,"?",i),.parts)
q
MAKEPATPARTS3(str,parts)

3.19. ИНДЕКСАЦИЯ ДЛЯ ШАБЛОНА (LIKE) 289
q:str=""
n depth=$$DEPTHS(),d,pos,i,substr,start=$l(str)
i $l(str)>=depth s start=depth
f i=1:1:depth d
. f pos=1:1:$l(str)-i+1 d
. . s substr=$e(str,pos,pos+i-1) s:substr’="" parts(substr)=""
q
PAT(mask) ; make MUMPS pattern based on the LIKE’s template
n quote,i,ret,char
s quote=0,ret=""
f i=1:1:$l(mask) d
. s char=$e(mask,i) s:char="""" char=char_char
. i "*?"’[char s ret=ret_$s(quote:char,1:"1"""_char),quote=1 q
. i quote s ret=ret_""""
. s ret=ret_$s(char="*":".E",1:"1E")
. s quote=0
i quote s ret=ret_""""
q:$q ret q
Структурно пример использует набор слов и индексов в виде:
^LIKEDATA(id)=word
^LIKEIND(part,id)=""
Здесь id - идентификатор слова, word - значение слова, part - часть
слова.
Для построения индекса используется набор функций добавления и
удаления индексных записей:
ADDWORD(word)
n id=$i(^LIKEDATA)
s ^LIKEDATA(id)=word
n parts
d MAKEPARTS(word,.parts)
n part="" f s part=$o(parts(part)) q:part="" d
. s ^LIKEIND(part,id)=""
q
DELWORD(id)
n word=$g(^LIKEDATA(id)),parts
d MAKEPARTS(word,.parts)
n part="" f s part=$o(parts(part)) q:part="" d
. k ^LIKEIND(part,id)
k ^LIKEDATA(id)
q
REINDEX
k ^LIKEIND
n i="" f s i=$o(^LIKEDATA(i)) q:i="" d
. n parts d MAKEPARTS(^LIKEDATA(i),.parts)

290 ГЛАВА 3. ИНДЕКСАЦИЯ ДАННЫХ
. n part="" f s part=$o(parts(part)) q:part="" d
. . s ^LIKEIND(part,i)=""
q
Пример использует контрольный набор данных:
INIT ; clear and recreate all data
k ^LIKEDATA,^LIKEIND
n word
f word="abc def","def ghj","rty iop","789 hjk","abdefghj" d
. d ADDWORD(word)
q
Для выборки по шаблону используется алгоритм многоиндексной выборки адаптированный с использованием нескольких индексов по разным атрибутам к использованию одного индекса по нескольким фрагментам:
LIKESELECT(mask,ids)
n pat=$$PAT(mask),parts,inames,part,id
; make template parts
d MAKEPATPARTS(mask,.parts)
; make index names
s part="" f s part=$o(parts(part)) q:part="" d
. s inames($i(inames))=$na(^LIKEIND(part))
; call index search
k ids
d AND($na(ids),.inames)
; use filter to remove unneed
s id="" f s id=$o(ids(id)) q:id="" d
. i ^LIKEDATA(id)’?@pat k ids(id)
q
AND(ret,names) ; make zig-zag ordered selection
n id,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 >>
i ’$d(@names(place)@(id)) s i=place g ANDrep
q id
Поскольку при поиске по индексу находятся записи, подходящие по
индексу, но не подходящие по шаблону, применяется дополнительная
фильтрация по шаблону
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]
