How-to: GROUP SUM
Пример 1
Условие
Есть набор книг, привязанных к определенной категории.
CLASS Book 'Книга';
CLASS Category 'Категория';
category 'Категория' = DATA Category (Book);
Необходимо посчитать количество книг в категории.
Решение
countBooks 'Количество кн иг' (Category c) = GROUP SUM 1 BY category(Book book);
Пример 2
Условие
Есть набор книг, привязанных к определенным тегам. Каждая книга может относиться к нескольким тегам одновременно.
CLASS Tag 'Тег';
in 'Вкл' = DATA BOOLEAN (Tag, Book);
Необходимо посчитать количество книг в теге.
Решение
countBooks 'Количество книг' (Tag t) = GROUP SUM 1 IF in(t, Book b);
Пример 3
Условие
Существует информация о движении книг, где для каждой записи есть ссылка на книгу и склад, по которому было движение, а также количество и тип операции (приход/расход).
CLASS Stock 'Склад';
CLASS Ledger 'Движение';
book 'Книга' = DATA Book (Ledger);
stock 'Склад' = DATA Stock (Ledger);
quantity 'Кол-во' = DATA INTEGER (Ledger);
out 'Расход' = DATA BOOLEAN (Ledger);
Необходимо посчитать текущей остаток по складу для книги.
Решение
TABLE bookStock (Book, Stock);
currentBalance 'Текущий остаток' (Book b, Stock s) = GROUP SUM IF out(Ledger l) THEN -quantity(l) ELSE quantity(l) BY book(l), stock(l) MATERIALIZED;
Свойство currentBalance
лучше всего помечать как MATERIALIZED
для того, чтобы при чтении текущего остатка система не считала его на основе движения за все время, а обращалась бы к таблице bookStock
с уже готовым значением. Это замедляет запись (так как при записи каждого движения будет требоваться обновление текущего остатка), однако значительно ускоряет чтение.
Для свойства currentBalance
нигде явно не задается, в какой таблице оно должно храниться, так как система может сама определить по сигнатуре, в какую таблицу его лучше всего положить (в дан ном случае это будет таблица bookStock
).
Пример 4
Условие
Аналогично Примеру 3, только для движения указана дата движения.
// лучше добавлять индекс, чтобы быстро шла фильтрация по дате
date 'Дата' = DATA DATE (Ledger) INDEXED;
Необходимо посчитать остаток по складу для книги на дату (на утро, без учета движений за выбранную дату).
Решение
// Вариант 1
balance1 'Остаток на дату' (Book b, Stock s, DATE d) = GROUP SUM (IF out(Ledger l) THEN -quantity(l) ELSE quantity(l)) IF date(l) < d BY book(l), stock(l);
// Вариант 2
balance2 'Остаток на дату' (Book b, Stock s, DATE d) = currentBalance(b, s) (-) [ GROUP SUM (IF out(Ledger l) THEN -quantity(l) ELSE quantity(l)) IF date(l) >= d BY book(l), stock(l)](b, s);
Второй вариант предпочтительнее. Так как чаще всего запросы будут идти к датам ближе к текущей, то сервер будет считать от текущего остатка "назад", что требует использования меньшего количества записей.
Пример 5
Условие
Аналогично Примеру 3, только нужно посчитать остаток книги по всем складам.
Решение
currentBalance 'Текущий остаток' (Book b) = GROUP SUM currentBalance(b, Stock s);
Это свойство в отличии от текущего остатка по складам нет смысла делать MATERIALIZED
в случае, если количество складов невелико, так как это может приводить к UPDATE CONFLICT при одновременной записи несколькими пользователями движения по одной книге по разным складам.