вторник, 24 мая 2016 г.

MUMPS: Индексация длинных атрибутов

В реализации любых М систем в целях повышения эффективности реализаций вводятся ограничения на длину индекса. Ограничения такого же характера присутствуют и в других, не-М реализациях СУБД. В распространенных реализациях М длина индексов включая имя переменной ограничена 255 байт.

В случае применения сложных структур индексирования, составных индексов или просто длинных строковых атрибутов возникает задача выполнить индексацию в указанных ограничениях. Для решения этой задачи может быть применено сегментирование значений атрибутов. В индексе вместо отображения значения атрибута на набор идентификаторов прописывается отображение сегментов атрибута на набор идентификаторов.

Получить сегменты можно например так:
f i=0:1:$l(value)/n s @ind@(i+1)=$e(value,i*n+1,i+1*n)
Здесь n - число символов в одном сегменте.

При использовании сегментирования значения атрибута поиск выполняется указанием значений сегментов. При этом может возникнуть ситуация поиска по значениям с длиной, кратной величине сегмента. При этом возможно появление ситуации нахождения лишних записей, имеющих первую часть значения атрибута совпадающего с искомым. Чтобы исключить появление такой ситуации вводится дополнительная структура - индекс на длину атрибута. И либо длина искомого фрагмента должна обязательно указываться при поиске, либо необходимо использовать полный набор всех сегментов, которые могут принадлежать используемому значению.

После получения сегментов для каждого из них прописывается отображение сегмента на идентификатор. Например, используя модифицированный пример с простым индексом, для демонстрации используя сегменты длиной по 12 символов:
 CreateRecords() ; k  d CreateRecords^ind09() w
 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
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,id)
 n i,segment,n
 s n=12
 f i=0:1:$l(Value)/n d
 . s segment=$e(Value,i*n+1,i+1*n)
 . s:segment'="" ^Index(IndexName,i+1,segment,id)=""
 s ^Index(IndexName_" size",$l(Value)+1,id)=""
 l -^Index(IndexName,id)
 q
DeleteIndexRecord(IndexName,id,Value)
 l +^Index(IndexName,id)
 n i,segment,n
 s n=12
 f i=0:1:$l(Value)/n d
 . s segment=$e(Value,i*n+1,i+1*n)
 . k:segment'="" ^Index(IndexName,i+1,segment,id)
 k ^Index(IndexName_" size",$l(Value)+1,id)
 l -^Index(IndexName,id)
 q
Здесь символом » обозначен перенос строки, которого в реальном коде не должно быть.

В результате получаем индексные записи с сегментированными значениями атрибутов. Обратим внимание, что можно упростить схему блокировок, используя более короткое имя блокировки - означающее не столько блокируемую переменную, сколько смысл операции - в данном индексе идет операция с данным идентификатором.

Для выборки данных из индекса следует, конечно, учитывать, что атрибуты сегментированы и искать следует те идентификаторы, для которых полностью совпадут все сегменты. Примерный код выборки из одного сегментированного индекса, использующий многоиндексную выборку, может быть таким:
 Select()
 n Figure,ind,i,n,segment,res s n=12
 s Figure="квадрат с такими разными и неровными краями"
 f i=0:1:$l(Figure)/n d
 . s segment=$e(Figure,i*n+1,i+1*n)
 . s:segment'="" ind(i+1)=$na(^Index("Figure",i+1,segment))
 s ind(i+1)=$na(^Index("Figure size",$l(Figure)+1))
 s ind=$l(Figure)/n+1
 ; используем zig-zag ordered scan
 d ANDv($na(res),.ind)
 s res="" f  s res=$o(res(res)) q:res=""  d
 . w res,!
 q
ANDv(ret,names) g AND+1
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 »
        i '$d(@names(place)@(id)) s i=place g ANDrep
 q id ; по всем есть, значит подходит
Применение сегментирования значений атрибутов может быть вызвано не только длинными значениями самих атрибутов, но и служебными значениями в индексе, также занимающими место.

Такой метод может быть применен также для поиска записей по блобам, поскольку теоретически величина атрибута не ограничена. Поиск выполним так же, как при использовании атомарного строкового атрибута - при точном совпадении значения атрибута.

Подробнее о книге "MUMPS СУБД"

Комментариев нет:

Отправить комментарий