Перейти к основному содержимому
Версия: 7.0

Операции с множествами

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

Агрегирующие функции​

Агрегируюшая функция вычисляет на множестве наборов объектов некоторый результат в виде единичного объекта. Эта функция определяется начальным значением (как правило NULL), свойствами, которые она использует (операндами), операцией добавления к промежуточному результату текущих значений операндов, а также функцией преобразования промежуточного результата в конечный (как правило промежуточный результат и является конечным результатом).

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

В таблице снизу представлены поддерживаемые на данный момент виды агрегирующих функций:

Вид / опция в инструкцииНачальное значениеНазв. операндовОперация добавленияФункция преобразованияКоммутативностьТип данных
SUMNULLoperandresult = result (+) operandresult+число
MAXNULLoperandresult = max(result, operand)result+любой сравнимый
MINNULLoperandresult = min(result, operand)result+любой сравнимый
CONCATNULLseparator, operandresult = CONCAT separator, result, operandresult-строковый
LAST / PREVNULLwhere, operandresult = IF where THEN operand ELSE resultresult-любой

С точки зрения определения множества наборов объектов, а также способа представления результата, можно выделить четыре основных оператора работы с множествами:

  • Группировка (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;