Распределение (UNGROUP)
Оператор распределения создает свойство, значение группировки по сумме которого будет совпадать со значением заданного свойства (распределяемого). Соответственно, как и для оператора группировки, для оператора распределения необходимо задать множество свойств (групп), по которым будет осуществляться группировка.
Существует много различных способов построить такое распределение, на данный момент в платформе поддерживаются два наиболее часто используемых:
- Ограничивающий - результат распределения не должен превышать значение заданного свойства.
- Пропорциональный - результат распределения должен быть прямо пропорциален значению некоторого свойства (другими словами отношение результатов распределения на двух наборах объектов в рамках одной группы должно совпадать с отношением значений этого свойства на тех же наборах объектов).
Оператор может работать в не строгом режиме (используется по умолчанию). В этом случае платформа пытается рассчитать значение максимально близкое к значению распределяемого свойства, но не гарантирует, что они совпадут.
Как и для других операций со множествами, для оператора распределения можно (и в большинстве случаев нужно) задавать порядок.
Класс результата распределения определяется свойством, задающим форму распределения на элемент — ограничением для ограничивающей формы или пропорцией для пропорциональной формы, — а не классом самого распределяемого свойства. Поэтому если у ограничения / пропорции класс шире или иной, чем у распределяемого значения, результат берёт класс ограничения / пропорции.
Общий алгоритм работы оператора распределения, в зависимости от типа распределения, следующий:
- Ограничивающий - распределение идет в заданном порядке, не превышая ограничение, до тех пор, пока общий результат не совпадет со значением распределяемого свойства. Если оператор работает в строгом режиме и общий результат не достиг значения распределяемого свойство, вся разница добавляется к результирующему значению первого набора объектов.
- Пропорциональный - вычисляется общая сумма пропорций по каждой группе, затем для каждого набора объектов определяется коэффициент распределения, равный отношению значения пропорции для этого набора объектов к общей сумме группы, в которую он входит, и, наконец, результат распределения рассчитывается как произведение этого коэффициента и значения распределяемого свойства. Так как конечный (и промежуточный) результаты округляются (а значит теряется точность), сумма результата такого распределения может отличаться от значения распределяемого свойства. Поэтому, если оператор работает в строгом режиме, разница между этими значения добавляется к результирующему значению первого набора объектов .
Расширенная форма
Описанный выше механизм позволяет делать распределение только в режиме "один-ко-многим". Однако в некоторых случаях этого бывает недостаточно, и необходимо делать распределение "много-ко-многим". Для этого в платформе существует так называемая расширенная форма оператора распределения (соответственно базовую будем называть простой).
В расширенной форме этого оператора условия на результат распределения изменяются следующим образом:
- Ограничивающий - не сам результат распределения должен не превышать значение заданного свойства, а группировка результата распределения по некоторым дополнительным группам не должна превышать это значение.
- Пропорциональный - аналогично, то есть не сам результат распределения должен быть прямо пропорционален значению некоторого свойства, а его группировка по дополнительным группам.
Соответствующим образом изменяются и сам алгоритм работы оператора.
Язык
Так как простая форма оператора семантически очень похожа на оператор разбиения / упорядочивания, для объявления свойства, реализующего простое распределение также используется оператор PARTITION.
Для расширенной формы используется оператор UNGROUP.
Примеры
CLASS Invoice;
totalCost 'Общая стоимость' = DATA NUMERIC[10,2] (Invoice);
CLASS InvoiceLine;
invoice = DATA Invoice (InvoiceLine) NONULL DELETE;
weight = DATA NUMERIC[14,3] (InvoiceLine);
// пропорциональное распределение: разносим totalCost по строкам накладной пропорционально weight,
// с округлением до двух знаков; остаток от округления отдаётся первой строке в заданном порядке
lineCost 'Стоимость по строке' (InvoiceLine l) = PARTITION UNGROUP totalCost
PROPORTION STRICT ROUND(2) weight(l)
ORDER l
BY invoice(l);
// ограничивающее распределение: разносим totalCost по строкам накладной в порядке приоритета,
// не превышая на каждой строке её квоту, пока исходное значение не будет исчерпано
quota = DATA NUMERIC[14,2] (InvoiceLine);
priority = DATA INTEGER (InvoiceLine);
allocated 'Распределённая стоимость' (InvoiceLine l) = PARTITION UNGROUP totalCost
LIMIT quota(l)
ORDER priority(l), l
BY invoice(l);