Материализации
Практически любое агрегированное свойств в платформе можно материализовать. В этом случае свойство будет храниться в базе данных постоянно и автоматически обновляться при изменении данных, от которых это свойство зависит. При этом при чтении значений такого материализованного свойства в сессии изменений, не изменившей данные, от которых оно зависит, эти значения будут читаться непосредственно из базы, как если бы свойство было первичным (а не вычисляться каждый раз). Соответственно, все первичные свойства являются материализованными по определению.
Свойство можно материализовать, тогда и только тогда, когда для него существует конечное число наборов объектов, для которых значение этого свойства не NULL (то есть операция итерации по всем его не NULL значениям будет корректной)
Не материализованное свойство вычисляется внутри каждого запроса, который его читает: агрегированное свойство становится подзапросом такого запроса. Если такое свойство читается внутри другого агрегирования, его подзапрос включается в текст внешнего запроса - по разу на каждое место и каждый вид, в котором оно там читается (например, отдельно его новое и предыдущее значения при вычислении изменений), - поэтому текст одного запроса растёт с числом таких мест и размером вложенных определений. Длина текста одного запроса ограничена рабочим параметром queryLengthLimit (по умолчанию 2000000 символов): более длинный запрос сервер приложений отклоняет ещё до выполнения (ошибка TOO LONG QUERY), независимо от того, сколько строк он вернул бы, и не повторяет его. Материализация внутреннего свойства заменяет его подзапрос чтением хранимых значений.
Хранимые значения материализованного свойства можно пересчитать — заново вычислить из определения свойства. Это нужно, когда эти значения могли разойтись с определением, например после изменения определения свойства или после прямого исправления данных.
Пересчет
Пересчет материализованного свойства сверяет сохраненные значения с его определением и записывает обратно те, что разошлись. То, как именно это делается, задают несколько рабочих параметров.
useRecalculateClassesInsteadOfInconsisentExpr (по умолчанию true) сначала пересчитывает классы таблицы, в которой хранится свойство, и только потом сверяет значения - так сверка может пользоваться обычным выражением свойства. При выключенном параметре классы не трогаются, а сверка идет по выражению, которое терпит несогласованные данные о классах, что добавляет соединений и требует больше памяти. Из двух вариантов пересчет классов грубее: он переписывает данные о классах всей таблицы. Если пересчет запрошен без классов, параметр не действует.
recalculateMaterializationsMixedSerializable (по умолчанию false) меняет то, как пересчет, идущий в собственной транзакции, удерживает уровень изоляции: расходящиеся строки сначала собираются вне транзакции, и на строгом уровне выполняется только их обратная запись. Это стоит лишней временной таблицы и второго запроса и ничего не меняет для пересчета, который администратор попросил выполнить в одной общей транзакции.
maxRecalculateTime (300000 миллисекунд) - не таймаут, по нему ничего не прерывается. Операция, занявшая больше этого времени, добавляется в отчет, который возвращает сервисное действие, - так видно медленные таблицы и свойства; 0 выводит в отчет практически каждую операцию, так как сравнение строгое.
groupByTables (по умолчанию true) группирует пересчитываемые свойства по таблице, в которой они хранятся: при многопоточном пересчете свойства одной таблицы выполняются одно за другим, а не одновременно, и поэтому не ждут блокировок друг друга. Он же обновляет - при старте сервера - статистику по колонкам, которые синхронизация схемы базы только что создала или перенесла. При выключенном параметре это обновление статистики пропускается совсем, и запросы по таким колонкам сохраняют плохие планы, пока статистика не будет пересчитана иначе, поэтому его лучше оставить включенным.
Язык
Для материализации свойства используется опция MATERIALIZED в опциях свойств. Для пересчета хранимых значений материализованного свойства используется оператор RECALCULATE.
Примеры
sum = GROUP SUM sum(OrderDetail od) BY order(od) MATERIALIZED;
date(OrderDetail od) = date(order(od)) MATERIALIZED;
// такое свойство нельзя материализовать, так как оно не NULL для бесконечного числа дат
lastDate(Customer customer, DATE date) = GROUP LAST date(Order order) IF customer(order) = customer AND date(order) < date ORDER order;