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

Многоиндексной выборкой, иначе называемой шаговой или зиг-загом, называется выборки идентификаторов записей по нескольким индекс­ным структурам одновременно. Также часто встречается название zig­zag 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)