Операции с множествами
Одной из ключевых возможностей платформы является возможность выполнения некоторых операций для всех наборов объектов, для которых значения одного или нескольких свойств не равны NULL. В логике свойств такой операцией является вычисление различных агрегирующих функций.
Агрегирующие функции
Агрегируюшая функция вычисляет на множестве наборов объектов некоторый результат в виде единичного объекта. Эта функция определяется начальным значением (как правило NULL), свойствами, которые она использует (операндами), операцией добавления к промежуточному результату текущих значений операндов, а также функцией преобразования промежуточного результата в конечный (как правило промежуточный результат и является конечным результатом).
Агрегирующая функция является коммутативной, если при вычислении результата не важно, в каком порядке перебираются наборы объектов исходного множества.
В таблице снизу представлены поддерживаемые на данный момент виды агрегирующих функций:
| Вид / опция в инструкции | Начальное значение | Назв. операндов | Операция добавления | Функция преобразования | Коммутативность | Тип данных |
|---|---|---|---|---|---|---|
SUM | NULL | operand | result = result (+) operand | result | + | число |
MAX | NULL | operand | result = max(result, operand) | result | + | любой сравнимый |
MIN | NULL | operand | result = min(result, operand) | result | + | любой сравнимый |
CONCAT | NULL | separator, operand | result = CONCAT separator, result, operand | result | - | строковый |
LAST / PREV | NULL | where, operand | result = IF where THEN operand ELSE result | result | - | любой |
С точки зрения определения множества наборов объектов, а также способа представления результата, можно выделить четыре основных оператора работы с множествами:
- Группировка (
GROUP) — разбивает наборы объектов на группы и вычисляет один результат на каждую группу. - Разбиение / Упорядочивание (
PARTITION ... ORDER) — тоже группирует наборы объектов, но вычисляет результат для каждого набора объектов — по части его группы вплоть до его собственной позиции в порядке. - Рекурсия (
RECURSION) — строит множество рекурсивно из начального значения и повторяемого шага, после чего агрегирует по полученному результату. - Распределение (
UNGROUP) — операция, обратная группировке: распределяет значение по наборам объектов группы так, чтобы группировка результатов по сумме давала это значение или, в нестрогом режиме, приближалась к нему.
Корректность операции
Необходимо учитывать, что при выполнении каждой операции над множеством наборов объектов это множество должно быть конечным. В этом случае операцию будем называть корректной.
Конечным платформа считает множество, у каждого параметра которого есть источник перебора: пользовательский класс (условие a IS A, выражение a AS A или объявление A a вводимого оператором параметра) либо свойство с конечным множеством не-NULL значений, в котором этот параметр участвует (например, первичное свойство). Известный встроенный класс сам по себе таким источником не является, как и сравнение преобразования типа параметра с литералом или вычисленным значением: в условиях LONG(a) = 5 или STRING(a) = '5' (поиск объекта по его внутреннему идентификатору) параметр a встречается только под преобразованием типа, поэтому такое множество считается бесконечным, а операция — некорректной. Источник может следовать и из других частей операции — например, из свойства, которому присваивается значение, или из другого условия на тот же параметр; ошибка возникает, когда его нет нигде. Источник учитывается только в той операции, которая перебирает параметр. Группировка перебирает только вводимые ею параметры; верхние параметры становятся её группировками, и источник для них даёт операция, использующая результат группировки. Разбиение / упорядочивание, рекурсия и распределение перебирают все свои параметры, хотя и не вводят их сами, поэтому для них не учитываются ни объявление A a (в самом операторе или среди параметров свойства), ни свойство, которому присваивается значение, ни условие операции, использующей их результат. Обнаруживается ошибка только при выполнении (загрузка модуля проходит успешно) и выдаётся с текстом Операция над множеством некорректна либо Параметр нарушает ограничение типов или операция над множеством некорректна, без указания оператора, поэтому в скрипте из нескольких операторов искать нужно параметр, у которого нет ни пользовательского класса, ни свойства-источника. Исправление — задать пользовательский класс явно либо переписать условие через такое свойство.
Примеры
CLASS A;
d = DATA INTEGER (A);
f (b) = GROUP SUM 1 IF d(a) < b;
messageF { MESSAGE f(5); } // успешно выполнится
// свойство iterate — источник перебора для b;
// без этого условия f(b) не NULL для бесконечного числа b, и операция была бы некорректной
g = GROUP SUM f(b) IF iterate(b, 1, 10);
messageG { MESSAGE g(); } // успешно выполнится
FORM f
OBJECTS d=DATE
;
// значение d передаётся в форму;
// без OBJECTS d=... фильтра для дат нет, d IS DATE не NULL для бесконечного числа d, и операция была бы некорректной
printFWithD { PRINT f OBJECTS d=currentDate(); } // успешно выполнится
Класс параметра не следует из сравнения его преобразования типа с литералом:
// объявление класса параметра делает множество конечным;
// без него у a нет ни класса, ни свойства-источника, и операция была бы некорректной
byId (LONG id) = GROUP MAX A a IF LONG(a) = id;
Для разбиения / упорядочивания объявление A a среди параметров свойства источником не является:
locked = DATA BOOLEAN (A);
// условие a IS A внутри оператора делает множество конечным;
// без него NOT locked(a) выполняется для бесконечного числа a, несмотря на объявление A a, и операция была бы некорректной
index (A a) = PARTITION SUM 1 IF NOT locked(a) AND a IS A ORDER a;