Добавил:
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.10. НОРМИРОВАНИЕ ЗНАЧЕНИЙ 261
никаких дополнительных мер, то такой индекс будет чувствителен к
регистру. Или, иными словами, сравнение будет выполняться "как есть".
При необходимости искать по индексу нечувствительно к регистру
мы должны оба значения - и искомое, и записанное в индексе, привести
к единым соглашениям по регистру символов.
Традиционно, большинство систем баз данных использует приведение строк к верхнему регистру, но это необязательное правило.
Одна из проблем, вытекающих из построения регистронезависимого
поиска - это то, что после приведения к единому регистру несколько
различных значений могут стать равны, например:
Исходное Нормированное
Шар ШАР
шар ШАР
ШАР ШАР
и, в этом случае, при необходимости использовать такой индекс для
поиска чувствительно к регистру, необходимо применять дополнительную операцию фильтрации. В ней нужно проверить равенство искомого
атрибута атрибуту найденной записи.
Чувствительность к регистру является, вообще говоря, частным примером общей нормировки значений. При внесении данных в базу данных
могут нормироваться как сами значения атрибутов, так и их индексные
значения. Вот несколько распространенных примеров нормирования значений атрибутов:
1. Приведение к единому регистру
2. Отбрасывание лидирующих и завершающих пробельных символов
3. Замена последовательности пробельных символов на один пробел
4. Удаление непечатных символов
5. Капитализация названий
Разработчик должен самостоятельно решить, на каком из этапов
должна производиться нормировка значений - при изменении значения
атрибута или при вычислении индексного значения. Очевидно, что применение нормировки, если она использовалась хотя бы раз, далее всегда
необходимо при обращении к индексу.
В случае применения нормированных значений индексов для уникального индекса разработчик должен принять соответствующие меры

262 ГЛАВА 3. ИНДЕКСАЦИЯ ДАННЫХ
для разрешения конфликта - что является для базы данных уникальным
- оригинальное значение атрибута или его нормированное представление. В такой ситуации традиционным выбором разработчиков является
опора на оригинальное значение, и ведение в индексных записях в действительности неуникальных записей, и обеспечение уникальности не
структурно, а алгоритмически. В любом случае, у разработчиков обычно есть выбор, что является более важным критерием - ведение уникальности собственно индекса или уникальности значений записей при
сохранении нормированности.
3.11 Выборки по индексу
Выборкой по индексу (возможно, термин не совсем соответствует русскому языку, но так он употребляется очень часто в среде разработчиков)
называется использование дополнительно поддерживаемых индексных
структур для получения данных либо непосредственно, либо косвенно
в сочетании с самими записями данных. Собственно говоря, именно эта
операция и является целью применения параллельных структур данных.
По своему строению индексные структуры дают возможность тем или
иным способом указав искомое значение атрибута получить ноль, одно
или несколько идентификаторов записей или значения других атрибутов
записей. Индексные записи отображают индексируемое значение на один
или несколько идентификаторов. Поэтому операции выборки могут быть
нескольких видов:
1. Проверка существования в индексе заданного значения атрибута.
2. Выборка значений атрибутов для заданного диапазона значений
атрибута.
3. Выборка значений идентификаторов для заданного значения атрибута.
4. Выборка значений идентификаторов для заданного диапазона значений атрибута.
Структурно строение индексных записей схематично выглядит как:
^Ind("IndName",IndValue,id)=""
При этом первой задаче соответствует применение операции $d() для
проверки существования узла, второй соответствует применение $o()

3.11. ВЫБОРКИ ПО ИНДЕКСУ 263
для выборки значений IndValue, третьей - операция $o() для выборки
набора id при фиксировании значения IndValue и для четвертой - либо операция $q() либо два вложенных друг в друга цикла - один по
IndValue, второй - по id.
Для демонстрации выборок используем структуру хранения простого
индекса из первого примера.
Выборка диапазона значений атрибута
SelectFigures(from="",to="")
i from’="" s from=$o(^Index("Figure",from),-1)
i to’="" s to=$o(^Index("Figure",to))
f s from=$o(^Index("Figure",from)) q:(from=to)!(from="") d
. w from,!
q
Здесь функция SelectFigures выбирает различные значения атрибута
Figure от from до to включительно. Если аргумент опущен, то считается
пустой строкой и производится выборка с открытым концом: если from
не указан, то выдаются все с начала, если to не указан, то выдаются все
до конца. В реализации функция корректирует значения from и to для
того чтобы цикл for был применим к всем комбинациям.
Выборка идентификаторов по значению атрибута
SelectByFigure(Figure)
n id s id=""
f s id=$o(^Index("Figure",Figure,id)) q:id="" d
. w id,!
q
Выборка значений идентификаторов для заданного диапазона значений атрибута с использованием вложенного цикла:
SelectByFigures1(from="",to="")
n id
i from’="" s from=$o(^Index("Figure",from),-1)
i to’="" s to=$o(^Index("Figure",to))
f s from=$o(^Index("Figure",from)) q:from=to d
. s id="" f s id=$o(^Index("Figure",from,id)) q:id="" d
. . w id,!
q
Выборка значений идентификаторов для заданного диапазона значений атрибута с использованием итераций по $q:

264 ГЛАВА 3. ИНДЕКСАЦИЯ ДАННЫХ
SelectByFigures2(from="",to="")
n ref
s ref=$na(^Index("Figure",from))
f s ref=$q(@ref) q:$s(to="":ref="",1:$qs(ref,2)]]to) d
. w $qs(ref,3),!
q
Здесь в качестве условия прекращения цикла стоит сложное условие:
в зависимости от непустоты хвоста to либо продолжать пока ссылка не
станет пустой, либо учитывать порядок индексной сортировки.
Во всех приведенных случаях есть возможность указывать направление выдачи результата - по возрастанию или убыванию значений атрибутов и / или идентификаторов. Для этого можно либо явно указать
в необязательном аргументе функций $o и $q направление траверса
либо передавать его из необязательного параметра функций SelectBy...
Приведенные функции выдают в направлении возрастания значений в
отношении индексной сортировки. Для полноты и строгости изложения
также следует отметить, что возможность задать направление выборки
для функции $query может поддерживаться не всеми MUMPS системами, для которых разрабатывается программа, и такую возможность
необходимо проверить по документации. В частности, система GT.M такой параметр не поддерживает.
В третьем случае, при выборке идентификаторов при наложении ограничений на диапазон значений атрибутов, идентификаторы выдаются не
в порядке сортировки чисел, а в порядке сортировки атрибутов. Для
выдачи идентификаторов также в сортированном виде следует применить дополнительную сортировку, хотя обычно в упорядочении значений идентификаторов нет особого смысла кроме соответствия порядку
создания записи.
Приведенные случаи рассчитаны на выборку включительно указанные значения. Во многих случаях требуется выдавать значения со строгими неравенствами. Для этого нужно соответственно модифицировать
приведенные функции.
Примечание - в англоязычной литературе и справочниках описанные
выборки могут называться так: 2, 4 - range scan, 1, 3 - index scan.
Механизм выборки данных структурно обычно выполняют одним из
двух видов:
1. Итератор непосредственно применяет операцию к полученному идентификатору
2. Итератор записывает найденный идентификатор в набор для возврата

3.12. МНОГОИНДЕКСНАЯ ВЫБОРКА (ZIG-ZAG) 265
Использование первого способа продемонстрировано на приведенных
выше примерах выборки данных. Команда write выводит найденные данные непосредственно на экран.
Второй способ используется в библиотечных или обобщенных функциях. Итерации по данным выполняются по тем же самым алгоритмам,
но вместо применения какой-либо операции к найденным данным эти
данные записываются в указаную переменную.
В простых случаях переменная для получения набора найденных данных может передаваться по ссылке и использоваться локальная переменная. В общем же случае используются временные глобальные переменные. Для формирования имени используют, например, номер текущего
процесса, внутренний идентификатор выборки или что-то еще.
Для демонстрации примера выборки в указанную переменную:
indselect ;
n Search
s Search=$na(^TempResult($j))
d SelectByFigure("квадрат",Search)
w "Результат поиска:",!
zw @Search
k @Search
q
SelectByFigure(Figure,Result)
n id s id=""
k @Result
f s id=$o(^Index("Figure",Figure,id)) q:id="" d
. s @Result@(id)=""
q
В некоторых из примеров используется первый способ, для простой
демонстрации, а в более сложных случаях, или для выполнения операций над результатами поиска, используется второй вариант.
Использование временных глобальных переменных обычно предпочтительнее из-за возможно большого объема данных, попадающих в
выборку. В тех случаях, когда по самой формулировке задачи объем
выборки небольшой, то также применяются и локальные переменные.
3.12 Многоиндексная выборка (zig-zag)
Многоиндексной выборкой, иначе называемой шаговой или зиг-загом,
называется выборки идентификаторов записей по нескольким индексным структурам одновременно. Также часто встречается название zigzag ordered scan. Принципиальным моментом является слово ordered,

266 ГЛАВА 3. ИНДЕКСАЦИЯ ДАННЫХ
или упорядочение искомых идентификаторов. Применяется для выборки
из двух или более индексов.
Индексные структуры отображают значение атрибута на набор идентификаторов. При этом набор идентификаторов может быть упорядочен
в каком-либо упорядочении. Если порядок сортировки в нескольких индексах одинаков, то к ним может быть применена многоиндексная выборка.
При такой выборке алгоритм выбирает по какому-то критерию один
из индексов, например первый попавшийся и получает идентификатор
записи. После чего идет по списку индексов и сверяет есть ли в них соответствующая запись с соответствующими этим индексам значениями
атрибутов. Если во всех заданных индексах такая запись есть, то она
считается найденной. Если по какому-то из индексов запись не найдена, то по этому индексу выполняется сдвиг в наборе идентификаторов
и этот индекс считается начальным, все повторяется. Если на каком-то
шаге идентификатор стал пуст, то выборка закончена.
Приведенный алгоритм является одним из вариантов алгоритма соединения сортированных списков, называемый merge join или sort merge
join, и может объединять, вообще говоря, произвольное число индексных
структур.
Условно представим схему выборки. Положим, что нужно найти серую кошку. При этом располагаем двумя индексами - по цвету и по
виду.
^Index("color","белый",1)=""
^Index("color","белый",2)=""
^Index("color","серый",3)=""
^Index("color","серый",4)=""
^Index("type","кошка",1)=""
^Index("type","кошка",4)=""
^Index("type","кошка",5)=""
^Index("type","собака",2)=""
В качестве начального условия выбираем индекс по цвету и позиционируемся на первое значение идентификаторов 3. Получаем в переменную id = 3. Переходим к индексу по виду и проверяем есть ли такой
идентификатор в отображении значения "кошка". Такого нет. Делаем текущим индекс по виду и выполняем выборку следующего идентификатора по виду относительно текущего значения id, id принимает значение
4.
Поскольку идентификатор не пуст, не прекращаем поиск и проверяем
существование отображения следующего индекса (это индекс по цвету).

3.12. МНОГОИНДЕКСНАЯ ВЫБОРКА (ZIG-ZAG) 267
Такая запись есть, и других индексов для проверки нет, поэтому идентификатор считается удовлетворяющим заданным условиям и отмечается
каким-либо способом как найденный.
С этого же индекса (или с любого другого) выполняем сдвиг на следующий идентификатор. Сдвинувшись по индексу по цвету на следующий идентификатор после 4, получаем пустой идентификатор, следовательно поиск закончен.
В найденный набор идентификаторов таким образом должен попасть
только один идентификатор id = 4.
Обобщенная функция многоиндексной выборки может выглядеть например таким образом:
AND(ret,names...)
n id,i,j,place
; идем по всем и запоминаем. контекст выборки - в переменных
; 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 »
q id ; по всем есть, значит подходит
i ’$d(@names(place)@(id)) s i=place g ANDrep
Здесь символом » обозначен перенос строки, которого в реальном коде не должно быть. В функции AND используется передача переменного
числа параметров. То же самое можно организовать и на стандартном
М:
ANDv(ret,names) g AND+1
Но при этом следует самостоятельно сформировать набор индексов.
Приведенные функции используют соглашения что в передаваемых переменных передаются имена и функции должны использовать косвенность.
Это соглашение позволяет составить функцию выборки по индексам в
достаточно общей форме.
Приведем примеры получения данных в соглашениях из первого примера:

268 ГЛАВА 3. ИНДЕКСАЦИЯ ДАННЫХ
SelectFigureColor(Figure,Color)
n res
d AND($na(res),$na(^Index("Figure",Figure)), »
$na(^Index("Color",Color)))
s res="" f s res=$o(res(res)) q:res="" d
. w res,!
q
Здесь формируется набор имен, куда следует вренуть результат и
имена индексных записей. Тот же вариант без использования переменного числа аргументов:
SelectFigureColor(Figure,Color)
n res,ind
s ind(1)=$na(^Index("Figure",Figure))
s ind(2)=$na(^Index("Color",Color))
s ind=2 ; два индекса
d ANDv($na(res),.ind)
s res="" f s res=$o(res(res)) q:res="" d
. w res,!
q
Многоиндексная выборка по своему характеру не является наиболее
оптимальной по числу выполняемых с глобалами действий, поскольку
часть шагов выполняется впустую. Количество лишних операций, или
операций не приведших к получению искомого идентификатора, сильно
зависит от состояния используемых индексов. Вполне возможны ситуации, когда при большом количестве данных по обеим индексам в искомое
множество попадает очень малое число идентификаторов, но в этом случае многоиндексная выборка тем не менее предпримет попытки прохода
по неинтересующим идентификаторам в том числе.
Одновременно с тем в большинстве случаев многоиндексная выборка намного предпочтительнее использования только одного индекса и
фильтрации значений по остальным атрибутам. В определенной степени
это утверждение опирается на наиболее распространенные статистики
применяемых данных. Соотношение эффективности различных методов
поиска по нескольким индексам, безусловно, зависит от состояния данных.
Многоиндексную выборку можно применять также при смешанных
индексах - одновременно использовать древовидные, составные и битовые индексы. Условием их совместности является одинаковость упорядочения искомых идентификаторов. В случае применения индексов разного
вида соответственно следует изменить алгоритм выборки, чтобы он обращался к соответствующим операциям получения следующего идентификатора и для проверки существования идентификатора в индексных

3.13. ДИФФЕРЕНЦИАЛЬНОЕ ИНДЕКСИРОВАНИЕ 269
структурах. Вышеприведенные коды использовали простые индексы и
соответственно использовали функции $o() и $d().
Замечание относительно одинаковости упорядочения важно также в
отношении применения баз данных с различными соглашениями о сравнении символов (character collation), а также для тех MUMPS систем,
в которых сортировка локальных переменных может отличаться от сортировки глобалов в случае если производится выборка из индексов или
промежуточных индексных структур, расположенных в смешанном виде
хранения - и в локальных и в глобальных переменных одновременно.
3.13 Дифференциальное индексирование
Дифференциальным индексированием называется такое перестроение индексных элементов, при котором изменению подвергаются индексные
элементы только для тех атрибутов записи, которые изменились. Введение дифференциального индексирования является оптимизирующей операцией, поскольку в большинстве случаев позволяет уменьшить количество операций с диском, не изменяя саму логику работы и структуру
данных. В большинстве случаев записи данных действительно редко
меняются целиком, зачастую меняется лишь часть атрибутов.
Введение дифференциального индексирования принципиально сказывается только в одном месте кода поддержки индексов - при обновлении
записи. При вставке новой и при удалении записи данных индексные записи должны быть строго либо добавлены либо удалены соответственно.
Только при обновлении записи есть возможность сверить значения атрибутов в текущем значении записи и в новом значении. Сравнение делается бесхитростно, повальным сканированием всех атрибутов попадающих
под индексирование, либо используется информация от функции, изменяющей значение атрибута.
На примере ведения простого индекса покажем изменения кода, которые нужно сделать для введения дифференциального перестроения
индекса.
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)

270 ГЛАВА 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))
n Figure,Color,Count,FigureOld,ColorOld,CountOld
; check Figure
s FigureOld=$p((OldRecordValues),"~",1)
s Figure=$p((RecordValues),"~",1)
i Figure’=FigureOld d
. d DeleteIndexRecord("Figure",id,FigureOld)
. d InsertIndexRecord("Figure",id,Figure)
; check Color
s ColorOld=$p((OldRecordValues),"~",2)
s Color=$p((RecordValues),"~",2)
i Color’=ColorOld d
. d DeleteIndexRecord("Color",id,ColorOld)
. d InsertIndexRecord("Color",id,Color)
; check Count
s CountOld=$p((OldRecordValues),"~",3)
s Count=$p((RecordValues),"~",3)
i Count’=CountOld d
. d DeleteIndexRecord("Count",id,CountOld)
. d InsertIndexRecord("Count",id,Count)
s ^Data(id)=RecordValues
l -^Data(id)
q
DeleteIndex(IndexName)
k ^Index(IndexName)
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]
