File D:\maven-qcr-plugin-testsunitaires\qcr4java\h2-1.3.170-sources\target\site\qc\lecode\expression\AggregateData.java.html

1 /*
2 * Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
3 * Version 1.0, and under the Eclipse Public License, Version 1.0
4 * (http://h2database.com/html/license.html).
5 * Initial Developer: H2 Group
6 */
7 package org.h2.expression;
8
9 import java.util.ArrayList;
10 import java.util.Arrays;
11 import java.util.Comparator;
12 import org.h2.engine.Constants;
13 import org.h2.engine.Database;
14 import org.h2.message.DbException;
15 import org.h2.util.IntIntHashMap;
16 import org.h2.util.New;
17 import org.h2.util.ValueHashMap;
18 import org.h2.value.CompareMode;
19 import org.h2.value.DataType;
20 import org.h2.value.Value;
21 import org.h2.value.ValueArray;
22 import org.h2.value.ValueBoolean;
23 import org.h2.value.ValueDouble;
24 import org.h2.value.ValueInt;
25 import org.h2.value.ValueLong;
26 import org.h2.value.ValueNull;
27
28 /**
29 * Data stored while calculating an aggregate.
30 */
31 class AggregateData {
32 private final int aggregateType;
33 private final int dataType;
34 private long count;
35 private IntIntHashMap distinctHashes;
36 private ValueHashMap<AggregateData> distinctValues;
37 private Value value;
38 private double m2, mean;
39 private ArrayList<Value> list;
40
41 AggregateData(int aggregateType, int dataType) {
42 this.aggregateType = aggregateType;
43 this.dataType = dataType;
44 }
45
46 /**
47 * Add a value to this aggregate.
48 *
49 * @param database the database
50 * @param distinct if the calculation should be distinct
51 * @param v the value
52 */
53 void add(Database database, boolean distinct, Value v) {
54 if (aggregateType == Aggregate.SELECTIVITY) {
55 count++;
56 if (distinctHashes == null) {
57 distinctHashes = new IntIntHashMap();
58 }
59 int size = distinctHashes.size();
60 if (size > Constants.SELECTIVITY_DISTINCT_COUNT) {
61 distinctHashes = new IntIntHashMap();
62 m2 += size;
63 }
64 int hash = v.hashCode();
65 // the value -1 is not supported
66 distinctHashes.put(hash, 1);
67 return;
68 } else if (aggregateType == Aggregate.COUNT_ALL) {
69 count++;
70 return;
71 } else if (aggregateType == Aggregate.HISTOGRAM) {
72 if (distinctValues == null) {
73 distinctValues = ValueHashMap.newInstance();
74 }
75 AggregateData a = distinctValues.get(v);
76 if (a == null) {
77 if (distinctValues.size() < Constants.SELECTIVITY_DISTINCT_COUNT) {
78 a = new AggregateData(Aggregate.HISTOGRAM, dataType);
79 distinctValues.put(v, a);
80 }
81 }
82 if (a != null) {
83 a.count++;
84 }
85 return;
86 }
87 if (v == ValueNull.INSTANCE) {
88 return;
89 }
90 count++;
91 if (distinct) {
92 if (distinctValues == null) {
93 distinctValues = ValueHashMap.newInstance();
94 }
95 distinctValues.put(v, this);
96 return;
97 }
98 switch (aggregateType) {
99 case Aggregate.COUNT:
100 case Aggregate.HISTOGRAM:
101 return;
102 case Aggregate.SUM:
103 if (value == null) {
104 value = v.convertTo(dataType);
105 } else {
106 v = v.convertTo(value.getType());
107 value = value.add(v);
108 }
109 break;
110 case Aggregate.AVG:
111 if (value == null) {
112 value = v.convertTo(DataType.getAddProofType(dataType));
113 } else {
114 v = v.convertTo(value.getType());
115 value = value.add(v);
116 }
117 break;
118 case Aggregate.MIN:
119 if (value == null || database.compare(v, value) < 0) {
120 value = v;
121 }
122 break;
123 case Aggregate.MAX:
124 if (value == null || database.compare(v, value) > 0) {
125 value = v;
126 }
127 break;
128 case Aggregate.GROUP_CONCAT: {
129 if (list == null) {
130 list = New.arrayList();
131 }
132 list.add(v);
133 break;
134 }
135 case Aggregate.STDDEV_POP:
136 case Aggregate.STDDEV_SAMP:
137 case Aggregate.VAR_POP:
138 case Aggregate.VAR_SAMP: {
139 // Using Welford's method, see also
140 // http://en.wikipedia.org/wiki/Algorithms_for_calculating_variance
141 // http://www.johndcook.com/standard_deviation.html
142 double x = v.getDouble();
143 if (count == 1) {
144 mean = x;
145 m2 = 0;
146 } else {
147 double delta = x - mean;
148 mean += delta / count;
149 m2 += delta * (x - mean);
150 }
151 break;
152 }
153 case Aggregate.BOOL_AND:
154 v = v.convertTo(Value.BOOLEAN);
155 if (value == null) {
156 value = v;
157 } else {
158 value = ValueBoolean.get(value.getBoolean().booleanValue() && v.getBoolean().booleanValue());
159 }
160 break;
161 case Aggregate.BOOL_OR:
162 v = v.convertTo(Value.BOOLEAN);
163 if (value == null) {
164 value = v;
165 } else {
166 value = ValueBoolean.get(value.getBoolean().booleanValue() || v.getBoolean().booleanValue());
167 }
168 break;
169 default:
170 DbException.throwInternalError("type=" + aggregateType);
171 }
172 }
173
174 ArrayList<Value> getList() {
175 return list;
176 }
177
178 /**
179 * Get the aggregate result.
180 *
181 * @param database the database
182 * @param distinct if distinct is used
183 * @return the value
184 */
185 Value getValue(Database database, boolean distinct) {
186 if (distinct) {
187 count = 0;
188 groupDistinct(database);
189 }
190 Value v = null;
191 switch (aggregateType) {
192 case Aggregate.SELECTIVITY: {
193 int s = 0;
194 if (count == 0) {
195 s = 0;
196 } else {
197 m2 += distinctHashes.size();
198 m2 = 100 * m2 / count;
199 s = (int) m2;
200 s = s <= 0 ? 1 : s > 100 ? 100 : s;
201 }
202 v = ValueInt.get(s);
203 break;
204 }
205 case Aggregate.COUNT:
206 case Aggregate.COUNT_ALL:
207 v = ValueLong.get(count);
208 break;
209 case Aggregate.SUM:
210 case Aggregate.MIN:
211 case Aggregate.MAX:
212 case Aggregate.BOOL_OR:
213 case Aggregate.BOOL_AND:
214 v = value;
215 break;
216 case Aggregate.AVG:
217 if (value != null) {
218 v = divide(value, count);
219 }
220 break;
221 case Aggregate.GROUP_CONCAT:
222 return null;
223 case Aggregate.STDDEV_POP: {
224 if (count < 1) {
225 return ValueNull.INSTANCE;
226 }
227 v = ValueDouble.get(Math.sqrt(m2 / count));
228 break;
229 }
230 case Aggregate.STDDEV_SAMP: {
231 if (count < 2) {
232 return ValueNull.INSTANCE;
233 }
234 v = ValueDouble.get(Math.sqrt(m2 / (count - 1)));
235 break;
236 }
237 case Aggregate.VAR_POP: {
238 if (count < 1) {
239 return ValueNull.INSTANCE;
240 }
241 v = ValueDouble.get(m2 / count);
242 break;
243 }
244 case Aggregate.VAR_SAMP: {
245 if (count < 2) {
246 return ValueNull.INSTANCE;
247 }
248 v = ValueDouble.get(m2 / (count - 1));
249 break;
250 }
251 case Aggregate.HISTOGRAM:
252 ValueArray[] values = new ValueArray[distinctValues.size()];
253 int i = 0;
254 for (Value dv : distinctValues.keys()) {
255 AggregateData d = distinctValues.get(dv);
256 values[i] = ValueArray.get(new Value[] {dv, ValueLong.get(d.count)});
257 i++;
258 }
259 final CompareMode compareMode = database.getCompareMode();
260 Arrays.sort(values, new Comparator<ValueArray>() {
261 public int compare(ValueArray v1, ValueArray v2) {
262 Value a1 = v1.getList()[0];
263 Value a2 = v2.getList()[0];
264 return a1.compareTo(a2, compareMode);
265 }
266 });
267 v = ValueArray.get(values);
268 break;
269 default:
270 DbException.throwInternalError("type=" + aggregateType);
271 }
272 return v == null ? ValueNull.INSTANCE : v.convertTo(dataType);
273 }
274
275 private static Value divide(Value a, long by) {
276 if (by == 0) {
277 return ValueNull.INSTANCE;
278 }
279 int type = Value.getHigherOrder(a.getType(), Value.LONG);
280 Value b = ValueLong.get(by).convertTo(type);
281 a = a.convertTo(type).divide(b);
282 return a;
283 }
284
285 private void groupDistinct(Database database) {
286 if (distinctValues == null) {
287 return;
288 }
289 if (aggregateType == Aggregate.COUNT) {
290 count = distinctValues.size();
291 } else {
292 count = 0;
293 for (Value v : distinctValues.keys()) {
294 add(database, false, v);
295 }
296 }
297 }
298
299 }
300