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