Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
MUMPS СУБД. Практика применения и опыт программирования.pdf
Скачиваний:
0
Добавлен:
07.09.2026
Размер:
2 Мб
Скачать
3.3. ПРОСТОЙ ИНДЕКС 231
системой) на запись как совокупность значений атрибутов, то простой индекс отображает значение индексируемых атрибутов на идентифика­тор или набор идентификаторов записей.
Наличие такого быстрого отображения и реализует способ быстрого поиска записи по значению атрибута. Положим, что в нашем случае записи имеют структуру
^Data(id)=Figure~Color~Count
Тогда при наличии индекса по фигуре мы должны поддерживать ак­туальность дополнительной структуры данных
^Index(Figure,id)=""
Эта структура позволяет, задав значение фигуры, получить набор идентификаторов записей, в которых встречается это значение фигуры. В случае поддержки нескольких индексов, конечно, структура должна быть усложнена:
^Index("Figure",Figure,id)="" ^Index("Color",Color,id)="" ^Index("Count",Count,id)=""
с целью повторного использования имени индексной глобали.
Можно также выбрать вариант хранить индексы по разным атри­бутам в разных глобалях, но обычно придерживаются использования одной. Это сокращает список используемых глобалов и упрощает сопро­вождение и модификацию программ.
Простой индекс используется как в задачах выборки данных по усло­вию, так и определения существования записей с заданными значениями атрибутов. Что используется при реализации ограничений различного вида.
Небольшой пример реализации простых индексов:
ind01 ; простые индексы, автоматический идентификатор
q
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)
232 ГЛАВА 3. ИНДЕКСАЦИЯ ДАННЫХ
. 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,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)
3.4. СОСТАВНОЙ ИНДЕКС 233
l -^Index(IndexName,Value,id) q

3.4 Составной индекс

Если простой индекс отображает значение одного атрибута на идентифи­катор или набор идентификаторов строк, то составной индекс отобража­ет совокупность значений двух или более атрибутов на идентификатор или набор идентификаторов строк.
Структурно индексные записи содержат значения не одного, а двух или более атрибутов в определенном порядке. При этом значения атри­бутов могут быть как указаны по отдельности в подъиндексах узлов, так и может быть применена функция, получающая по двум или более значениям одно значение, которое не можут быть получено при других значениях атрибутов. Приведем простой пример с одним составным ин­дексом по атрибутам Figure и Color. В первом случае, при раздельном указании значений атрибутов в индексе:
^Index("FigureColor",Figure,Color,id)=""
Во втором случае при использовании, например, функции $listbuild:
^Index("FigureColor",$lb(Figure,Color),id)=""
Или при использовании одного значения в виде строки с разделите­лем:
^Index("FigureColor",Figure_"~"_Color,id)=""
Вопрос выбора функции получения составного значения по несколь­ким оставим программисту. Думаю, что это лучше решать в каждом конкретном случае. В расчет следует принимать не только уникальность её результата для значений аргументов, но и возможность использовать индексную сортировку при выборке данных, а также время вычисления индексирующего значения.
Составные индексы применяются в выборках данных, где задано усло­вие на два атрибута одновременно. Фактически, имея составной индекс по этим атрибутам, мы получаем максимальную скорость выборки.
Кроме указания идентификатора записи в подъиндексе узла индекс­ной записи можно также применить хранение набора идентификаторов в одном значении. Например:
234 ГЛАВА 3. ИНДЕКСАЦИЯ ДАННЫХ
^Index("FigureColor",Figure,Color)=idlist
Здесь idlist - список идентификаторов строк. Такая организация ин­декса позволяет организовать эффективное покрытие. Покрывающим ин­дексом называется индекс, который может быть применен в другом типе выборки. В случае составного индекса например это могут быть узлы двух видов:
^Index("FigureColor",Figure)=FigureIdList ^Index("FigureColor",Figure,Color)=FigureColorIdList
Например, пусть есть записи
1 шарик красный 20 2 шарик зеленый 12 3 квадрат синий 5
Им будут соответствовать индексные записи:
^Index("FigureColor","шарик")=$lb(1,2) ^Index("FigureColor","шарик","красный")=$lb(1) ^Index("FigureColor","шарик","зеленый")=$lb(2) ^Index("FigureColor","квадрат")=$lb(3) ^Index("FigureColor","квадрат","синий")=$lb(3)
Второй вариант организации покрывающего индекса есть применение операции не $o() для выборки данных, а операции $q(). При ее исполь­зовании в качестве покрывающего индекса может быть использована начальная простая структура вида
^Index("FigureColor",Figure,Color,id)=""
В данном случае индекс покрывает две выборки - при указании как обеих атрибутов Figure и Color, так и при указании только атрибута Figure. Приведем пример применения такого покрытия:
n Data,id,ref s Data("FigureColor","шарик","красный",1)="" s Data("FigureColor","шарик","зеленый",2)="" s Data("FigureColor","квадрат","красный",3)="" s Data("FigureColor","квадрат","синий",4)="" s Data("FigureColor","шарик","красный",5)="" w "Select шарик + красный",! s id="" f s id=$o(Data("FigureColor","шарик","красный",id)) q:id="" d . w id,!
3.4. СОСТАВНОЙ ИНДЕКС 235
w "Select шарик",! s ref=$na(Data("FigureColor","шарик")) »
. w $qs(ref,4),! q
f s ref=$q(@ref) q:ref="" d
Первая выборка выдает по условию по двум атрибутам, вторая - по
одному.
Использование и поддержка покрывающих индексов может быть как очень эффективна по быстродействию и задействованию одного индекса в нескольких задачах, так и может оказаться неэффективна в случае катастрофического роста объема данных, занимаемого самим индексом и фактическим обесцениванием кеширования базы данных. Фактически, составной индекс является прямым конкурентом методу многоиндексной выборки.
В случае если движок базы данных не умеет использовать многоин­дексную выборку, то составному индексу альтернатив, конечно, нет. В случае же возможности выбора следует оценить затраты на поддержку составного индекса. При высокой кардинальности атрибутов составной индекс, видимо, может оказаться менее эффективным, чем многоиндекс­ная выборка по совокупным затратам.
Оценку затратности и предпочтительности разных вариантов лучше производить в реальном проекте на реальных объемах данных, сопоста­вив с используемым аппаратным обеспечением. На общую оценку может повлиять и общая использованная алгоритмика, и объем кеша, и объем данных подлежащих индексированию, и объем получающихся в итоге выборок.
Небольшой пример реализации составного индекса:
ind03 ; составной индекс по Figure м Color
q
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)
236 ГЛАВА 3. ИНДЕКСАЦИЯ ДАННЫХ
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
InsertIndexRecords(id,RecordValues)
n Figure,Color s Figure=$p((RecordValues),"~",1) s Color=$p((RecordValues),"~",2) l +^Index("FigureColor",Figure,Color,id) s ^Index("FigureColor",Figure,Color,id)="" l -^Index("FigureColor",Figure,Color,id) q
DeleteIndexRecords(id,RecordValues)
n Figure,Color s Figure=$p((RecordValues),"~",1) s Color=$p((RecordValues),"~",2) l +^Index("FigureColor",Figure,Color,id) k ^Index("FigureColor",Figure,Color,id) l -^Index("FigureColor",Figure,Color,id) q
Здесь поддерживается только один составной индекс.
То, что индекс является составным, одновременно с тем не мешает ему быть простым или кластерным. Эти возможности можно совмещать. В приведенном примере показан простой составной индекс.
Можно также обратить внимание на то, что в индексных записях для большого объема данных оказывается много записей, имеющих дублиру­ющиеся фрагменты. В частности, могут совпадать следующие фрагменты
3.5. ПОКРЫВАЮЩИЙ ИНДЕКС 237
^Index("FigureColor",Figure,Color) ^Index("FigureColor",Figure) ^Index("FigureColor")
В практически всех MUMPS системах, в отличие от многих про­стых NoSQL систем, применяется компрессия ключей, и в большинстве случаев такие дублирования фрагментов не приводят к какому-либо су­щественному росту объема индексов.

3.5 Покрывающий индекс

Покрывающим индексом (cover index) называется индекс, который может быть использован для нескольких задач выборки данных, в том числе для тех, для которых он изначально не планировался. Название отражает не вид структуры, а различные возможности ее применения.
Индекс в силу своей структуры есть отображение значений атрибу­тов на идентификаторы записей. Поэтому содержит как набор значений атрибутов, так и набор отображений. Практически любой индекс в силу своего строения является покрывающим для по крайней мере нескольких задач:
1. Поскольку содержит набор значений атрибутов, может быть ис­пользован для выборки списка различных имеющихся значений атрибутов.
2. Поскольку каждое значение атрибута отображается на набор иден­тификаторов записей, можно найти записи по заданному значению.
3. Если значения атрибута находятся в некотором упорядочении, то можно получить сортировку по значениям атрибутов в этом упоря­дочении.
4. Возможно определить, существует ли запись с заданным значе­нием, и тогда можно реализовать ограничения различного рода, накладываемые на данные. В частности, запрет иметь значение "синий" более чем 5 записям одновременно.
Каждая возможность применения индекса вызвана некоторым отно­шением - либо внутри однородного набора, либо между значением и набором. И, чем больше отношений содержится в индексе, тем больше покрытий он обеспечивает. Например, пусть есть таблица с атрибута­ми Color и Figure. Составной индекс по Color и Figure покрывает уже следующие задачи:
238 ГЛАВА 3. ИНДЕКСАЦИЯ ДАННЫХ
1. Выбрать набор различных значений Color
2. Выбрать по заданному Color идентификаторы записей (применять вместо функции $O функцию $Q)
3. Выбрать по заданному Color и Figure идентификаторы записей (применять функцию $O)
4. Отсортировать идентификаторы по Color и Figure ($O)
5. Выбрать идентификаторы для диапазона Color ($Q)
6. Выбрать идентификаторы для диапазона Figure при задании Color ($O)
7. Выбрать идентификаторы при задании диапазонов Color и Figure ($O по Color + вложенный $O по Figure)
8. Для заданного Color отсортировать по Figure.
При этом нужно отметить, что составной индекс по Color и Figure уже не может быть использован, если условие наложено только на атри­бут Figure. Поскольку в таком индексе значения Figure находятся в под­чиненной, а не независимой иерархии по отношению к Color. Если необ­ходимо использовать значения Figure независимо от значений Color, то необходимо поддерживать второй индекс соответствующей структуры.
С точки зрения эффективности применение покрывающих индексов вполне оправдано, поскольку одна индексная структура может быть ис­пользована многократно в различных задачах. При этом чем больше атрибутов записи объединяет индекс, тем больше покрытий он обеспе­чивает. В случае применения межтабличных индексов эффективность покрывающего свойства многократно увеличивается, поскольку позво­ляет сократить число операций соединений.
Приведем демонстрационный пример поддержания структуры данных для записей с атрибутами Color и Figure, после чего приведем примеры использования одного и того же индекса в различных задачах:
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:120 d
3.5. ПОКРЫВАЮЩИЙ ИНДЕКС 239
. 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("ColorFigure",id,
$p((RecordValues),"~",2),$p((RecordValues),"~",1))
q
DeleteIndexRecords(id,RecordValues)
d DeleteIndexRecord("ColorFigure",id,
$p((RecordValues),"~",2),$p((RecordValues),"~",1))
q
InsertIndexRecord(IndexName,id,ColorValue,FigureValue)
l +^Index(IndexName,ColorValue,FigureValue,id) s ^Index(IndexName,ColorValue,FigureValue,id)="" l -^Index(IndexName,ColorValue,FigureValue,id) q
DeleteIndexRecord(IndexName,id,ColorValue,FigureValue)
l +^Index(IndexName,ColorValue,FigureValue,id) k ^Index(IndexName,ColorValue,FigureValue,id) l -^Index(IndexName,ColorValue,FigureValue,id)
240 ГЛАВА 3. ИНДЕКСАЦИЯ ДАННЫХ
q
Приведем несколько примеров таких выборок различного назначения по одному и тому же индексу.
Выбрать набор различных значений Color
Select1()
n Color s Color="" f s Color=$o(^Index("ColorFigure",Color)) q:Color="" d . w Color,! q
Выбрать по заданному Color идентификаторы записей (применять вместо функции $O функцию $Q)
Select2(Color="красный")
n ref s ref=$na(^Index("ColorFigure",Color)) f s ref=$q(@ref) q:$s(ref="":1,$qs(ref,2)’=Color:1,1:0) d . w $qs(ref,4),! q
Выбрать по заданному Color и Figure идентификаторы записей (при­менять функцию $O)
Select3(Color="синий",Figure="квадрат")
n id s id="" f s id=$o(^Index("ColorFigure",Color,Figure,id)) q:id="" d . w "Found id: "_id,! q
Отсортировать идентификаторы по Color и Figure ($O)
Select4(COrder=1,FOrder=-1)
n c,f,id s c="" f s c=$o(^Index("ColorFigure",c),COrder) q:c="" d . s f="" f s f=$o(^Index("ColorFigure",c,f),FOrder) q:f="" d . . s id="" f s id=$o(^Index("ColorFigure",c,f,id)) q:id="" d . . . w c_" "_f_" "_id,! q