## 4 General Form for a Data Warehouse Query in Prolog  
In order to represent an aggregation of data in Prolog, we used the aggregate operator of Prolog [1]. It was used to calculate the aggregates from the logical predicates. In order to use it to compute aggregation queries in a data warehouse, it was necessary to specify the joins between the relations. To do this, we exploited the link shown in Section 2 between the relational algebra and the conjunctive queries. We proposed the following query pattern expressed in Prolog:  
_aggregate_(_aggregation_function_1_,... _aggregation_function_N_),  
_Attribute_1_... _Attribute_M_(_relation_1_,... _relation_O_,... _condition_1_,... _condition_P_),  
_aggregation_result_1_,... _aggregation_result_N_)).  
_aggregation_function_i_ and _aggregation_result_i_ are, respectively, an aggregation function (e.g., count and sum) and the variable that stores the result of the aggregation obtained using this function. It was possible to use several aggregation functions in the same query. _relation_1_,... _relation_O_ were the relations needed for the aggregation. The joins between the relations were represented in the same manner as traditional conjunctive queries (_relation_1_,... _relation_O_ corresponded with _relation_1_\(\land\)... _\(\wedge\)_relation_O_). _Attribute_1_,... _Attribute_M_ were the attributes included in _relation_1_,... _relation_O_ that were not used for the aggregation. In SQL queries, the grouping attributes are specified in the GROUP BY clause; in Prolog, the attributes that are not used for the grouping are specified. Thus, by default, the Prolog aggregate operator grouped together all the attributes present in _relation_1_,... _relation_O_. The _Attribute_i_... notation allowed the exclusion of certain attributes from the grouping. _condition_i_ was used to specify the conditions (such as the relational algebra selection).  
We provide here a few examples of basic data warehouse queries. The following Prolog expressions produced the sum of the sales goals by the salespersons. aggregate(sum(SalesGoal), ProductID(fact(SalespersonID,ProductID,SalesGoal)), SalesGoalSumByPerson). The results were: SalespersonID = idsp01, SalesGoalSumByPerson = 1150000 ; SalespersonID = idsp02, SalesGoalSumByPerson = 300000 ; SalespersonID = idsp03, SalesGoalSumByPerson = 500000 ; SalespersonID = idsp04, SalesGoalSumByPerson = 450000. Here is a query to compute the sum by salesperson excluding the product idprdtP5:aggregate(sum(SalesGoal), ProductID^(fact(SalespersonID,ProductID,SalesGoal), ProductID\='idprdtP5'), SalesGoalSumByPerson). The query below calculated the average of the sales goals by salespersons (combining the count and sum functions). aggregate((count,sum(SalesGoal)), SalesGoal^ProductID^(fact(SalespersonID,Product,SalesGoal)), (SalesGoalCountByPerson,SalesGoalSumByPerson)), SalesGoalAvgByPerson is SalesGoalSumByPerson/SalesGoalCountByPerson. The sum of the sales goals by salesperson and product type (with relation joins) was calculated by: aggregate(sum(SalesGoal), ProductID^SalespersonManagerID^ProductName^( fact(SalespersonID,ProductID, SalesGoal), salesperson(SalespersonID,SalespersonName,SalespersonManagerID), product(ProductID,ProductName,ProductTypeID)), SalesGoalSumByPersonAndProductType). The results were: SalespersonID = idsp01, SalespersonName = '[PERSON]', ProductTypeID = 1, SalesGoalSumByPersonAndProductType = 100000 ; SalespersonID = idsp01, SalespersonName = '[PERSON]', ProductTypeID = 2, SalesGoalSumByPersonAndProductType = 550000 ; SalespersonID = idsp01, SalespersonName = '[PERSON]', ProductTypeID = 3, SalesGoalSumByPersonAndProductType = 500000 ; SalespersonID = idsp02, SalespersonName = '[PERSON]', ProductTypeID = 1, SalesGoalSumByPersonAndProductType = 300000 ; SalespersonID = idsp03, SalespersonName = '[PERSON]', ProductTypeID = 1, SalesGoalSumByPersonAndProductType = 500000 ; SalespersonID = idsp04, SalespersonName = '[PERSON]', ProductTypeID = 3, SalesGoalSumByPersonAndProductType = 450000.