Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
MUMPS СУБД. Практика применения и опыт программирования.pdf
Скачиваний:
0
Добавлен:
07.09.2026
Размер:
2 Мб
Скачать
3.22. ОПЕРАЦИИ С ДРЕВОВИДНЫМИ ИНДЕКСАМИ 301
BT(ret,name,begin,end)
n i s i=begin f s i=$o(@name@(i)) q:(i="")!(i]]end)!(i=end) d . m @ret@(i)=@name@(i) q
USER>d BT^TREEOP($na(r),$na(^Index("Figure")), » "квадрат","треугольник")
Получить число различных значений атрибутов.
COUNT(name)
n ret,id s ret=0 s id="" f s id=$o(@name@(id)) q:id="" s ret=ret+1 q ret
USER>w $$COUNT^TREEOP($na(^Index("Figure","треугольник")))
Рассмотрим операции с поддеревьями идентификаторов. К ним мож­но отнести трансформирование дерева - списка значений + списки иден­тификаторов в дерево списка идентификаторов, операцию объединения множеств (OR), операцию пересечения множеств (AND) и операцию до­полнения множеств (SUB).
Сократить дерево на один уровень, или трансформировать дерево ­список значений + список идентификаторов в дерево - список идентифи­каторов:
SIMPLE(ret,name)
n i s i="" f s i=$o(@name@(i)) q:i="" m @ret=@name@(i) q
Получить идентификаторы, содержащиеся в индексе по полю Figure:
USER>d SIMPLE^TREEOP($na(r),$na(^Index("Figure")))
Получить идентификаторы, у которых значение Figure лежит между заданными значениями:
USER>d BT^TREEOP($na(r2), »
$na(^Index("Figure")),"квадрат","треугольник")
USER>d SIMPLE^TREEOP($na(r),$na(r2))
Получить объединение множеств:
302 ГЛАВА 3. ИНДЕКСАЦИЯ ДАННЫХ
OR(ret,names...)
n i f i=1:1:names m:$d(names(i)) @ret=@(names(i)) q
Используя эту операцию, получить идентификаторы записей, у кото-
рых значение Figure равно "круг" или "отрезок":
d OR^TREEOP($na(r),$na(^Index("Figure","круг")), »
$na(^Index("Figure","отрезок")))
Найти пересечение множеств:
AND(ret,names...)
n id,i,j,k,place s id="" f s i=1 s 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
Используя операцию пересечения найти красные круги:
d AND^TREEOP($na(r),$na(^Index("Figure","круг")), » $na(^Index("Color","красный")))
Найти дополнение множеств:
SUB(ret,from,what...)
n i,id
m @ret=@from
f i=1:1:what d:$d(what(i))
. s id="" f s id=$o(@what(i)@(id)) q:id="" k @ret@(id)
q
Используя операцию дополнения, найти круги и не красные:
d SUB^TREEOP($na(r),$na(^Index("Figure","круг")), »
$na(^Index("Color","красный")))
3.23. ОПЕРАЦИИ С БИТОВЫМИ ИНДЕКСАМИ 303
Кроме приведенных операций в практике также встречаются другие, такие как взять первое или последнее значение, или взять начиная с n­го m значений. Для них также можно составить обобщенные операции, использующие косвенность аргумента.
Несложно видеть, что собственно сами операции на индексных струк­турах просты, если их описывать обобщенно, в косвенной форме, и сложность использования индексов проявляется тогда, когда их жестко прописывают в коде программ, используя предопределенные магические константы.

3.23 Операции с битовыми индексами

Здесь поведем речь о реализации теоретико-множественных операций над битовыми индексами.
Битовый индекс структурно представляет собой отображение значе­ний атрибутов на набор идентификаторов в виде сопоставления с каж­дым значением битовой последовательности. При этом в силу того, что технически неограниченная последовательность не может быть реали­зована, она разбивается на сегменты. Единичному биту в такой после­довательности сопоставляется признак, что номер этого бита в общей последовательности (с учетом всех предшествующих сегментов) пред­ставляет собой числовой идентификатор индексированной записи.
Для выполнения операций поиска в таких индексах строится дере­во логического выражения и каждой операции сопоставляется логиче­ская операция над битовыми последовательностями. Каждая из опера­ций имеет результатом битовую последовательность такой же структуры
- совокупность битовых сегментов.
При этом, что важно, семантика битовой последовательности явля­ется открытой - если есть единичный бит, то ему соответствует объект. Если его нет - то объекта нет. При этом под состояние "нет единич­ного бита" попадает как нулевой бит, так и отсутствие сегмента как такового. Над непосредственно битовыми последовательностями, таким образом, отсутствует операция унарной инверсии (отрицания), посколь­ку нет признака окончания последовательности. Таких же принципов открытости логических множеств придерживаются большинство других программных систем, например, в языке Prolog нет операции получить множество неизвестных высказываний.
При этом, хотя нет операции унарной инверсии, в модели открыто­го мира присутствует операция вычитания, которая в логике закрытого мира эквивалентна операции AND (И) первого операнда с результатом
304 ГЛАВА 3. ИНДЕКСАЦИЯ ДАННЫХ
инверсии второго операнда. В модели открытого мира такая операция не раскладывается на отдельные и применяется как есть, без промежуточ­ной инверсии вычитаемого множества.
С точки зрения применения в базах данных логическая операция "исключающее или" над индексами весьма спорна, поскольку пока не было разумных примеров ее использования. Видимо, именно поэтому в битовых функциях Cach´e и MiniM этой операции нет. Впрочем, если она потребуется (поскольку мир разнообразен), ее можно выразить через имеющиеся базовые логические операции.
Рассмотрим остальные операции:
1. Логическое И или пересечение множеств.
2. Логическое ИЛИ или объединение множеств.
3. Логический бинарный НЕТ или вычитание множеств.
Структурно, поскольку битовые последовательности имеют сегмент­ное строение, каждая из этих операций должна принять два или больше аргументов и выполнить соответствующую логическую операцию над каждым из сегментов и вернуть набор сегментов с результатом.
Для испытаний воспользуемся тестовыми данными, сделанными с помощью:
#define BITSIZE (260000)
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
3.23. ОПЕРАЦИИ С БИТОВЫМИ ИНДЕКСАМИ 305
InsertIndexRecords(id,RecordValues)
d SET($na(^Index("Figure",$p(RecordValues,"~",1))),id) d SET($na(^Index("Color",$p(RecordValues,"~",2))),id) d SET($na(^Index("Count",$p(RecordValues,"~",3))),id) q
SET(name,id,bit=1)
n seg,pos s seg=id\$$$BITSIZE s pos=id#$$$BITSIZE+1 s $bit(@name@(seg),pos)=’’bit q
Для реализации операции И или пересечения множеств:
AND(out,in1,in2)
n i k @out m @out=@in1 s i="" f s i=$o(@out@(i)) q:i="" d . s @out@(i)=$bitlogic(@out@(i)&@in2@(i)) q
Для реализации операции ИЛИ или объединения множеств:
OR(out,in1,in2)
n i k @out m @out=@in1 s i="" f s i=$o(@in2@(i)) q:i="" d . s @out@(i)=$bitlogic(@out@(i)|@in2@(i)) q
Для реализации операции SUB или дополнения множеств:
SUB(out,in1,in2)
n i k @out m @out=@in1 s i="" f s i=$o(@in2@(i)) q:i="" d . s @out@(i)=$bitlogic(@out@(i)&~@in2@(i)) q
При этом этим операциям не требуется использовать размер сегмента
- они должны провести операции с теми сегментами, которые имеются. Для операций с сегментами битовых карт есть нюанс, неуловимый на
первый взгляд - работа с пропущенными сегментами. При простановке битов может оказаться, что по каким-либо сегментам совсем не было операций, и такой сегмент будет отсутствовать. Поэтому операции OR и SUB опираются на первый операнд, но проход выполняют по второ­му, а операция AND выполняет проход по первому операнду, игнорируя наличие сегментов второго.
306 ГЛАВА 3. ИНДЕКСАЦИЯ ДАННЫХ
Отличительной особенностью реализации операций на битовыми ин­дексами также является отсутствие проверки существования самих би­товых сегментов, поскольку реализация функций $bit отсутствующие сегменты (неопределенные значения) считает как не содержащие еди­ничных битов.
Интересной задачей по оптимизации работы приведенных функций AND, OR, SUB выглядит переход от операции merge к проходам по имеющимся сегментам.
Кроме описанных, для типовых операций с данными часто требуется еще две операции - операция получения количества единичных битов:
COUNT(in)
n i,ret s ret=0 s i="" f s i=$o(@in@(i)) q:i="" d . s ret=ret+$bitcount(@in@(i),1) q ret
и получение следующего единичного бита после заданного:
NEXT(name,from)
n ret,seg,pos s ret=0 s from=+from s seg=from\$$$BITSIZE s pos=from#$$$BITSIZE+2 f s:(seg’="") ret=$bitfind(@name@(seg),1,pos) »
q:(ret)!(seg="") d . s seg=$o(@name@(seg)) q:seg="" . s pos=0 q:ret seg*$$$BITSIZE+ret-1 q ""
С использованием приведенных функций уже очень легко составить довольно сложные запросы, например запрос "выдать идентификаторы с заданными Figure и Color":
Select(Figure,Color)
n id,result d AND($na(result),$na(^Index("Figure",Figure)), »
$na(^Index("Color",Color))) s id="" f s id=$$NEXT($na(result),id) q:id="" d . w id,! q
Если провести сравнение простоты и удобства пользования битовыми и древовидными индексами, то у различных систем баз данных, несмот­ря на общие принципы, реализация битовых индексов различна. И, ве­роятно, это также является весомым фактором в сравнении. Например,
3.23. ОПЕРАЦИИ С БИТОВЫМИ ИНДЕКСАМИ 307
Cach´e и MiniM имеют перед другими СУБД чисто технические преиму­щества - 1) битовые операции атомарны, несмотря на то что операции различных процессов могут прийтись на один и тот же сегмент, 2) откат транзакций работает вполне корректно, 3) блокирование сегментов не требуется в силу пункта 1, 4) применение встроенной компрессии при хранении сегмента и 5) действительно малый объем журнала по сравне­нию с другими реализациями.
В целом же, если сравнить битовые индексы с древовидными, то дре­вовидные несомненно богаче по функциональности, но битовые намного проще в реализации и, видимо, несколько быстрее в работе. Особенно если используются встроенные и характерные для битовых сегментов операции, например $bitcount. Видится перспективным, что если для древовидных структур в MUMPS появятся аналогичные специализиро­ванные функции, то быстродействие древовидных индексов также можно ускорить.
Как возможные направления в развитии Cach´e и MiniM с целью ускорения древовидных индексов можно было бы назвать встроенные системные функции или команды
Получить число потомков у узла
Системная функция, похожая на $data, но возвращающая число раз­личных значений непосредственных потомков узла, например если есть
s ^d("s",1)="" s ^d("s",4)="" s ^d("s",78)="" s ^d("s",54,789)="" s ^d("s",100)="" s ^d("k",3)="" s ^d("k",8)="" s ^d("k",12,456)=""
то такая новая функция вернет:
w $data2(^d("s")) 5 w $data2(^d("k")) 3
Это позволит упростить часто используемую операцию оценки коли­чества элементов.
Выполнить логический И между потомками двух или больше заданных узлов
Команда, похожая на merge, но оставляющая в результате пересече­ния подиндексов, например если есть
308 ГЛАВА 3. ИНДЕКСАЦИЯ ДАННЫХ
s ^d("s",1)="" s ^d("s",4)="" s ^d("s",12)="" s ^d("k",1)="" s ^d("k",12)="" s ^d("k",34)=""
то такая новая команда сделает
merge2 ^d("R")=^d("s")&^d("k") zw ^d("R") ^d("R",1)="" ^d("R",12)=""
В остальном же сравнение битовых и древовидных индексов скорее всего в той или иной форме приходит к тому, что битовые индексы имеют намного более бедные покрывающие свойства (меньшее число различных операций, в которых они могут быть применены). Если к используемой системе предъявляются довольно сложные требования по запросам, то применение исключительно битовых индексов вместо древовидных мо­жет в некоторых случаях даже ухудшить результаты. С другой стороны, если можно обойтись только структурно простыми запросами, легко ре­ализующимися на битовых операциях, это скорее всего приведет к уско­рению работы системы, особенно если операция применяется к очень большому объему данных.
В оценке какие именно индексы лучше использовать наилучшим кри­терием выглядит практика. В задачах OLTP обычно проще применять древовидные, а в задачах OLAP битовые индексы. Преимущество бито­вых индексов существенно возрастает при увеличении объемов данных, и некоторые системы OLAP построенные не на MUMPS, имели в каче­стве ключевого преимущества по скорости именно реализацию битовых индексов.
3.24 Совмещение древовидных и битовых ин-
дексов
Рассмотрим технические детали совмещения в одной выборке битовых и древовидных индексов. Для такого совмещения следует построить мо­дель абстрагирования от реализации, то есть рассматривать индексные операции как таковые безотносительно их реальной внутренней реали­зации. И, используя абстрактные операции, реализовать механизм сов­мещения двух разнородных структур.
3.24. СОВМЕЩЕНИЕ ДРЕВОВИДНЫХ И БИТОВЫХ ИНДЕКСОВ309
В случае древовидных и битовых индексов такой абстрацией может быть абстрагирование до уровня отображения вообще, следования вооб­ще, проверки существования вообще и другие. Составив алгоритм опе­рирования индексами вообще, его просто нужно адаптировать до уровня, способного использовать конкретную реализацию, не зная ее деталей.
Основной операцией выборки по индексам является операция И. Рас­смотрим на ее примере данную методику. Для этого мы располагаем двумя абстрактными операциями - теоретико-множественной операцией битового И и многоиндексной шаговой выборки. Приведем операции с обоими видами индексов к одинаковым абстракциям.
Многоиндексная операция выборки, как было рассмотрено, исполь­зует такие ключевые элементы:
1. Упорядоченный набор имен индексов. Упорядочение требуется что­бы выбирать их из используемого набора.
2. Одинаковое упорядочение (сортировка) выбираемых из индекса ис­комых значений.
3. Наличие у абстрактного индекса операции "взять следующий иден­тификатор".
4. Наличие у абстрактного индекса операции "проверить существова­ние идентификатора".
Под все четыре приведенных условия мы уже можем подвести опе­рации и с битовым и с древовидным индексом. Сам алгоритм, выполняя собственно выборку, не должен знать реальную реализацию каждого ин­декса, а должен оперировать каждым из индексов в обобщенной форме. Для реализации такого полиморфизма используем косвенность - будем передавать в структуру, описывающий индекс, кроме имени самого ин­декса, также еще два имени - один как имя функции, выполняющей получение следующего идентификатора, и второй как имя функции вы­полняющей проверку существования заданного идентификатора в индек­се.
CreateRecords()
k ^Index k ^Data n i,Figures,Colors,Counts,Figure,Color,Count,id s Figures="квадрат~круг~отрезок~треугольник" s Colors="красный~зелёный~синий~белый" s Counts="2~5~12~8"
310 ГЛАВА 3. ИНДЕКСАЦИЯ ДАННЫХ
f i=1:1:24 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
InsertIndexRecords(id,RecordValues)
d InsertIndexRecord("Figure",id,$p((RecordValues),"~",1)) d SET($na(^Index("Color",$p((RecordValues),"~",2))),id) q
DeleteIndexRecords(id,RecordValues)
d DeleteIndexRecord("Figure",id,$p((RecordValues),"~",1)) d DEL($na(^Index("Color",$p((RecordValues),"~",2))),id) q
InsertIndexRecord(IndexName,id,Value)
s ^Index(IndexName,Value,id)="" q
DeleteIndexRecord(IndexName,id,Value)
k ^Index(IndexName,Value,id)
q #define BITSIZE (260000) DEL(name,id) s bit=0 g SET+1 SET(name,id,bit=1)
n seg,pos
s seg=id\$$$BITSIZE s pos=id#$$$BITSIZE+1