воскресенье, 29 мая 2016 г.

MUMPS: Сортировка по индексу

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

В индексной сортировке используется соглашение следования - если оба значения являются строковыми представлениями числа, то они сравниваются как числа в арифметическом упорядочении. Если не являются представлениями числа, то сортируются как строки побайтно. Если одно значение является представлением числа, а другое нет, то числовое значение следует перед нечисловым. Кроме того, в большинстве реализаций М-систем существует возможность тонкой настройки правила сортировки путем определения сортирующих таблиц. Для Cache это выполняет утилита nls. Для MiniM сортировка символов задается файлом определения символов (nat файл).

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

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

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

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

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

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

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

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

Приведем простой пример выравнивания чисел:
ViewSorted()
 n a
 s a($$ToSortVal(-100))=""
 s a($$ToSortVal(-1.345))=""
 s a($$ToSortVal(-1.0))=""
 s a($$ToSortVal(-5.0))=""
 s a($$ToSortVal(0))=""
 s a($$ToSortVal(1.0))=""
 s a($$ToSortVal(1.345))=""
 s a($$ToSortVal(3.0))=""
 s a($$ToSortVal(100.5))=""
 w "Как значения легли в индексе",!
 zw a
 w "Как значения выбираются из индекса",!
 s a="" f  s a=$o(a(a)) q:a=""  d
 . ; удаляем лидирующие нули
 . n z s z=a f  q:$e(z)'="0"  s $e(z)=""
 . w +z,!
 q
ToSortVal(val)
 n p1,p2
 s p1=$p(val,".",1) 
 s p1=$j(p1,10," ") s p1=$tr(p1," ","0")
 s p2=$p(val,".",2) 
 s p2=$re($j($re(p2),10," ")) s p2=$tr(p2," ","0")
 q p1_"."_p2
\noindent здесь сортируемые значения приводятся к строкам, но при этом, сортируясь как строки они сортируются также как числа, сохраняя взаимное арифметическое упорядочение.

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

Результат работы этого теста такой:
USER>d ViewSorted^indsort()
Как значения легли в индексе
a("000000-100.0000000000")=""
a("00000000-1.0000000000")=""
a("00000000-1.3450000000")=""
a("00000000-5.0000000000")=""
a("0000000000.0000000000")=""
a("0000000001.0000000000")=""
a("0000000001.3450000000")=""
a("0000000003.0000000000")=""
a("0000000100.5000000000")=""
Как значения выбираются из индекса
-100
-1
-1.345
-5
0
1
1.345
3
100.5
Здесь отрицательные числа сортируются неправильно, и для компенсации такого эффекта необходимо приведение всех чисел к положительным путем прибавления константы к сортируемому значению для получения сортирующего:
ViewSorted()
 n a
 s a($$ToSortVal(-100))=""
 s a($$ToSortVal(-1.345))=""
 s a($$ToSortVal(-1.0))=""
 s a($$ToSortVal(-5.0))=""
 s a($$ToSortVal(0))=""
 s a($$ToSortVal(1.0))=""
 s a($$ToSortVal(1.345))=""
 s a($$ToSortVal(3.0))=""
 s a($$ToSortVal(100.5))=""
 w "Как значения легли в индексе",!
 zw a
 w "Как значения выбираются из индекса",!
 s a="" f  s a=$o(a(a)) q:a=""  d
 . ; удаляем лидирующие нули
 . n z s z=a-1000000000 f  q:$e(z)'="0"  s $e(z)=""
 . w +z,!
 q
ToSortVal(val)
 n p1,p2 s val=val+1000000000
 s p1=$p(val,".",1) 
 s p1=$j(p1,10," ") s p1=$tr(p1," ","0")
 s p2=$p(val,".",2) 
 s p2=$re($j($re(p2),10," ")) s p2=$tr(p2," ","0")
 q p1_"."_p2
Здесь значение 1000000000 было использовано в качестве сортирующего дополнения, и эта величина существенно зависит от характера данных. В реальных случаях выбору величины дополнения необходимо уделять особое внимание.

Теперь, после применения дополнения, результат работы правильный, и числовое значение приведено к строковой сортировке, включая как положительные, так и отрицательные величины.
USER>d ViewSorted^indsort()
Как значения легли в индексе
a("0999999900.0000000000")=""
a("0999999995.0000000000")=""
a("0999999998.6550000000")=""
a("0999999999.0000000000")=""
a("1000000000.0000000000")=""
a("1000000001.0000000000")=""
a("1000000001.3450000000")=""
a("1000000003.0000000000")=""
a("1000000100.5000000000")=""
Как значения выбираются из индекса
-100
-5
-1.345
-1
0
1
1.345
3
100.5
Отметим, что аналогичным способом отображения чисел из одного множества в другое но уже с требуемыми сортирующими свойствами используют также для дат. Стандартно в М дату используют в формате $H, но это число, и для приведения к строковой сортировке используют замену, например в виде функции $zdt($h,8) в Cache. Это дает приведение даты и времени к замечательно сортирующемуся виду в фиксированном формате
"YYYYMMDD HH:MM:SS"
В таком виде использовать дату в качестве части сортирующей строки даже вполне читабельно при отладке.

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

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

Например, пусть есть два объекта
^Data(1)="Белый~Волк"
^Data(2)="Белый~Гусь"
здесь при сортировке следует выбрать либо вариант групп, либо вариант подиндекса.

Первый вариант, с применением групп:
структура:
 ^sort(цвет_$C(1)_идентиифкатор)=""
пример данных:
 ^sort("Белый"_$С(1)_"1")=""
 ^sort("Белый"_$С(1)_"2")=""
Второй вариант, с применением подиндекса:
структура:
 ^sort(Цвет,идентификатор)=""
пример данных:
 ^sort("Белый",1)=""
 ^sort("Белый",2)=""
При этом для выборки следует использовать в первом случае проход по $O() и получать значение искомого идентификатора функцией $P(), а во втором - проход по $Q() и получение искомого идентификатора функцией $QS().

Нужно помнить, то способ сортирования объектов полностью определяется функцией получения сортирующего значения. При ее формировании следует быть особенно внимательным и обеспечить правильность ее работы.

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

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

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