001 /* 002 // $Id: //open/mondrian/src/main/mondrian/rolap/RolapAggregator.java#17 $ 003 // This software is subject to the terms of the Common Public License 004 // Agreement, available at the following URL: 005 // http://www.opensource.org/licenses/cpl.html. 006 // Copyright (C) 2003-2007 Julian Hyde 007 // All Rights Reserved. 008 // You must accept the terms of that agreement to use this software. 009 */ 010 package mondrian.rolap; 011 012 import mondrian.calc.Calc; 013 import mondrian.olap.*; 014 import mondrian.olap.fun.FunUtil; 015 016 import java.util.List; 017 018 /** 019 * Describes an aggregation operator, such as "sum" or "count". 020 * 021 * @author jhyde 022 * @since Jul 9, 2003 023 * @version $Id: //open/mondrian/src/main/mondrian/rolap/RolapAggregator.java#17 $ 024 */ 025 public abstract class RolapAggregator 026 extends EnumeratedValues.BasicValue 027 implements Aggregator { 028 029 private static int index = 0; 030 031 public static final RolapAggregator Sum = 032 new RolapAggregator("sum", index++, false) { 033 public Object aggregate(Evaluator evaluator, List members, Calc exp) { 034 return FunUtil.sum(evaluator, members, exp); 035 } 036 }; 037 038 public static final RolapAggregator Count = 039 new RolapAggregator("count", index++, false) { 040 public Aggregator getRollup() { 041 return Sum; 042 } 043 public Object aggregate(Evaluator evaluator, List members, Calc exp) { 044 return FunUtil.count(evaluator, members, false); 045 } 046 }; 047 048 public static final RolapAggregator Min = 049 new RolapAggregator("min", index++, false) { 050 public Object aggregate(Evaluator evaluator, List members, Calc exp) { 051 return FunUtil.min(evaluator, members, exp); 052 } 053 }; 054 055 public static final RolapAggregator Max = 056 new RolapAggregator("max", index++, false) { 057 public Object aggregate(Evaluator evaluator, List members, Calc exp) { 058 return FunUtil.max(evaluator, members, exp); 059 } 060 }; 061 062 public static final RolapAggregator Avg = 063 new RolapAggregator("avg", index++, false) { 064 public Aggregator getRollup() { 065 return null; 066 } 067 public Object aggregate(Evaluator evaluator, List members, Calc exp) { 068 return FunUtil.avg(evaluator, members, exp); 069 } 070 }; 071 072 public static final RolapAggregator DistinctCount = 073 new RolapAggregator("distinct-count", index++, true) { 074 public Aggregator getRollup() { 075 // Distinct counts cannot always be rolled up, when they can, 076 // it's using Sum. 077 return Sum; 078 } 079 public RolapAggregator getNonDistinctAggregator() { 080 return Count; 081 } 082 public Object aggregate(Evaluator evaluator, List members, Calc exp) { 083 throw new UnsupportedOperationException(); 084 } 085 public String getExpression(String operand) { 086 return "count(distinct " + operand + ")"; 087 } 088 }; 089 090 /** 091 * List of all valid aggregation operators. 092 */ 093 public static final EnumeratedValues<RolapAggregator> enumeration = 094 new EnumeratedValues<RolapAggregator> ( 095 new RolapAggregator[] {Sum, Count, Min, Max, Avg, DistinctCount}); 096 097 /** 098 * This is the base class for implementing aggregators over sum and 099 * average columns in an aggregate table. These differ from the above 100 * aggregators in that these require not oly the operand to create 101 * the aggregation String expression, but also, the aggregate table's 102 * fact count column expression. 103 * These aggregators are NOT singletons like the above aggregators; rather, 104 * each is different because of the fact count column expression. 105 */ 106 protected abstract static class BaseAggor extends RolapAggregator { 107 protected final String factCountExpr; 108 109 protected BaseAggor(final String name, final String factCountExpr) { 110 super(name, index++, false); 111 this.factCountExpr = factCountExpr; 112 } 113 public Object aggregate(Evaluator evaluator, List members, Calc exp) { 114 throw new UnsupportedOperationException(); 115 } 116 } 117 118 /** 119 * Aggregator used for aggregate tables implementing the 120 * average aggregator. 121 * 122 * <p>It uses the aggregate table fact_count column 123 * and a sum measure to create the query used to generate an average: 124 * <blockquote> 125 * <code> 126 * avg == sum(column_sum) / sum(factcount). 127 * </code> 128 * </blockquote> 129 * 130 * <p>If the fact table has both a sum and average over the same column and 131 * the aggregate table only has a sum and fact count column, then the 132 * average aggregator can be generated using this aggregator. 133 */ 134 public static class AvgFromSum extends BaseAggor { 135 public AvgFromSum(String factCountExpr) { 136 super("AvgFromSum", factCountExpr); 137 } 138 public String getExpression(String operand) { 139 StringBuilder buf = new StringBuilder(64); 140 buf.append("sum("); 141 buf.append(operand); 142 buf.append(") / sum("); 143 buf.append(factCountExpr); 144 buf.append(')'); 145 return buf.toString(); 146 } 147 } 148 149 /** 150 * Aggregator used for aggregate tables implementing the 151 * average aggregator. 152 * 153 * <p>It uses the aggregate table fact_count column 154 * and an average measure to create the query used to generate an average: 155 * <blockquote> 156 * <code> 157 * avg == sum(column_sum * factcount) / sum(factcount). 158 * </code> 159 * </blockquote> 160 * 161 * <p>If the fact table has both a sum and average over the same column and 162 * the aggregate table only has a average and fact count column, then the 163 * average aggregator can be generated using this aggregator. 164 */ 165 public static class AvgFromAvg extends BaseAggor { 166 public AvgFromAvg(String factCountExpr) { 167 super("AvgFromAvg", factCountExpr); 168 } 169 public String getExpression(String operand) { 170 StringBuilder buf = new StringBuilder(64); 171 buf.append("sum("); 172 buf.append(operand); 173 buf.append(" * "); 174 buf.append(factCountExpr); 175 buf.append(") / sum("); 176 buf.append(factCountExpr); 177 buf.append(')'); 178 return buf.toString(); 179 } 180 } 181 /** 182 * This is an aggregator used for aggregate tables implementing the 183 * sum aggregator. It uses the aggregate table fact_count column 184 * and an average measure to create the query used to generate a sum: 185 * <pre> 186 * sum == sum(column_avg * factcount) 187 * </pre> 188 * If the fact table has both a sum and average over the same column and 189 * the aggregate table only has an average and fact count column, then the 190 * sum aggregator can be generated using this aggregator. 191 */ 192 public static class SumFromAvg extends BaseAggor { 193 public SumFromAvg(String factCountExpr) { 194 super("SumFromAvg", factCountExpr); 195 } 196 public String getExpression(String operand) { 197 StringBuilder buf = new StringBuilder(64); 198 buf.append("sum("); 199 buf.append(operand); 200 buf.append(" * "); 201 buf.append(factCountExpr); 202 buf.append(')'); 203 return buf.toString(); 204 } 205 } 206 207 208 209 210 private final boolean distinct; 211 212 public RolapAggregator(String name, int ordinal, boolean distinct) { 213 super(name, ordinal, null); 214 this.distinct = distinct; 215 } 216 217 public boolean isDistinct() { 218 return distinct; 219 } 220 221 /** 222 * Returns the expression to apply this aggregator to an operand. 223 * For example, <code>getExpression("emp.sal")</code> returns 224 * <code>"sum(emp.sal)"</code>. 225 */ 226 public String getExpression(String operand) { 227 StringBuilder buf = new StringBuilder(64); 228 buf.append(name); 229 buf.append('('); 230 if (distinct) { 231 buf.append("distinct "); 232 } 233 buf.append(operand); 234 buf.append(')'); 235 return buf.toString(); 236 } 237 238 /** 239 * If this is a distinct aggregator, returns the corresponding non-distinct 240 * aggregator, otherwise throws an error. 241 */ 242 public RolapAggregator getNonDistinctAggregator() { 243 throw new UnsupportedOperationException(); 244 } 245 246 /** 247 * Returns the aggregator used to roll up. By default, aggregators roll up 248 * themselves. 249 */ 250 public Aggregator getRollup() { 251 return this; 252 } 253 } 254 255 // End RolapAggregator.java