Добавил:
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.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
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]
