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

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 при одновременной записи несколькими пользователями движения по одной книге по разным складам.