001: /*
002: * Copyright 2004-2008 H2 Group. Licensed under the H2 License, Version 1.0
003: * (http://h2database.com/html/license.html).
004: * Initial Developer: H2 Group
005: */
006: package org.h2.expression;
007:
008: import java.sql.SQLException;
009:
010: import org.h2.engine.Constants;
011: import org.h2.engine.Database;
012: import org.h2.message.Message;
013: import org.h2.util.ObjectArray;
014: import org.h2.util.ValueHashMap;
015: import org.h2.value.Value;
016: import org.h2.value.ValueBoolean;
017: import org.h2.value.ValueDouble;
018: import org.h2.value.ValueInt;
019: import org.h2.value.ValueLong;
020: import org.h2.value.ValueNull;
021:
022: /**
023: * Data stored while calculating an aggregate.
024: */
025: public class AggregateData {
026: private final int aggregateType;
027: private long count;
028: private ValueHashMap distinctValues;
029: private Value value;
030: private double sum, vpn;
031: private ObjectArray list;
032:
033: AggregateData(int aggregateType) {
034: this .aggregateType = aggregateType;
035: }
036:
037: void add(Database database, boolean distinct, Value v)
038: throws SQLException {
039: if (aggregateType == Aggregate.SELECTIVITY) {
040: count++;
041: if (distinctValues == null) {
042: distinctValues = new ValueHashMap(database);
043: }
044: int size = distinctValues.size();
045: if (size > Constants.SELECTIVITY_DISTINCT_COUNT) {
046: distinctValues = new ValueHashMap(database);
047: sum += size;
048: }
049: distinctValues.put(v, this );
050: return;
051: }
052: if (aggregateType == Aggregate.COUNT_ALL) {
053: count++;
054: return;
055: }
056: if (v == ValueNull.INSTANCE) {
057: return;
058: }
059: count++;
060: if (distinct) {
061: if (distinctValues == null) {
062: distinctValues = new ValueHashMap(database);
063: }
064: distinctValues.put(v, this );
065: return;
066: }
067: switch (aggregateType) {
068: case Aggregate.COUNT:
069: return;
070: case Aggregate.SUM:
071: case Aggregate.AVG:
072: if (value == null) {
073: value = v;
074: } else {
075: v = v.convertTo(value.getType());
076: value = value.add(v);
077: }
078: break;
079: case Aggregate.MIN:
080: if (value == null || database.compare(v, value) < 0) {
081: value = v;
082: }
083: break;
084: case Aggregate.MAX:
085: if (value == null || database.compare(v, value) > 0) {
086: value = v;
087: }
088: break;
089: case Aggregate.GROUP_CONCAT: {
090: if (list == null) {
091: list = new ObjectArray();
092: }
093: list.add(v);
094: break;
095: }
096: case Aggregate.STDDEV_POP:
097: case Aggregate.STDDEV_SAMP:
098: case Aggregate.VAR_POP:
099: case Aggregate.VAR_SAMP: {
100: double x = v.getDouble();
101: if (count == 1) {
102: sum = x;
103: vpn = 0;
104: } else {
105: double xs = sum - (x * (count - 1));
106: vpn += (xs * xs) / count / (count - 1);
107: sum += x;
108: }
109: break;
110: }
111: case Aggregate.EVERY:
112: v = v.convertTo(Value.BOOLEAN);
113: if (value == null) {
114: value = v;
115: } else {
116: value = ValueBoolean.get(value.getBoolean()
117: .booleanValue()
118: && v.getBoolean().booleanValue());
119: }
120: break;
121: case Aggregate.SOME:
122: v = v.convertTo(Value.BOOLEAN);
123: if (value == null) {
124: value = v;
125: } else {
126: value = ValueBoolean.get(value.getBoolean()
127: .booleanValue()
128: || v.getBoolean().booleanValue());
129: }
130: break;
131: default:
132: throw Message.getInternalError("type=" + aggregateType);
133: }
134: }
135:
136: ObjectArray getList() {
137: return list;
138: }
139:
140: Value getValue(Database database, boolean distinct)
141: throws SQLException {
142: if (distinct) {
143: count = 0;
144: groupDistinct(database);
145: }
146: Value v = null;
147: switch (aggregateType) {
148: case Aggregate.SELECTIVITY: {
149: int s = 0;
150: if (count == 0) {
151: s = 0;
152: } else {
153: sum += distinctValues.size();
154: sum = (100 * sum / count);
155: s = (int) sum;
156: s = s <= 0 ? 1 : s > 100 ? 100 : s;
157: }
158: v = ValueInt.get(s);
159: break;
160: }
161: case Aggregate.COUNT:
162: case Aggregate.COUNT_ALL:
163: v = ValueLong.get(count);
164: break;
165: case Aggregate.SUM:
166: case Aggregate.MIN:
167: case Aggregate.MAX:
168: case Aggregate.SOME:
169: case Aggregate.EVERY:
170: v = value;
171: break;
172: case Aggregate.AVG:
173: if (value != null) {
174: v = divide(value, count);
175: }
176: break;
177: case Aggregate.GROUP_CONCAT:
178: return null;
179: case Aggregate.STDDEV_POP: {
180: if (count < 1) {
181: return ValueNull.INSTANCE;
182: }
183: v = ValueDouble.get(Math.sqrt(vpn / count));
184: break;
185: }
186: case Aggregate.STDDEV_SAMP: {
187: if (count < 2) {
188: return ValueNull.INSTANCE;
189: }
190: v = ValueDouble.get(Math.sqrt(vpn / (count - 1)));
191: break;
192: }
193: case Aggregate.VAR_POP: {
194: if (count < 1) {
195: return ValueNull.INSTANCE;
196: }
197: v = ValueDouble.get(vpn / count);
198: break;
199: }
200: case Aggregate.VAR_SAMP: {
201: if (count < 2) {
202: return ValueNull.INSTANCE;
203: }
204: v = ValueDouble.get(vpn / (count - 1));
205: break;
206: }
207: default:
208: throw Message.getInternalError("type=" + aggregateType);
209: }
210: return v == null ? ValueNull.INSTANCE : v;
211: }
212:
213: private Value divide(Value a, long count) throws SQLException {
214: if (count == 0) {
215: return ValueNull.INSTANCE;
216: }
217: int type = Value.getHigherOrder(a.getType(), Value.LONG);
218: Value b = ValueLong.get(count).convertTo(type);
219: a = a.convertTo(type).divide(b);
220: return a;
221: }
222:
223: private void groupDistinct(Database database) throws SQLException {
224: if (distinctValues == null) {
225: return;
226: }
227: if (aggregateType == Aggregate.COUNT) {
228: count = distinctValues.size();
229: } else {
230: count = 0;
231: ObjectArray l2 = distinctValues.keys();
232: for (int i = 0; i < l2.size(); i++) {
233: add(database, false, (Value) l2.get(i));
234: }
235: }
236: }
237:
238: }
|