Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
MUMPS СУБД. Практика применения и опыт программирования.pdf
Скачиваний:
0
Добавлен:
07.09.2026
Размер:
2 Мб
Скачать
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
Поскольку при поиске по индексу находятся записи, подходящие по индексу, но не подходящие по шаблону, применяется дополнительная фильтрация по шаблону