📄 aggregation.java
字号:
/*
// $Id: //open/mondrian/src/main/mondrian/rolap/agg/Aggregation.java#24 $
// This software is subject to the terms of the Common Public License
// Agreement, available at the following URL:
// http://www.opensource.org/licenses/cpl.html.
// Copyright (C) 2001-2005 Kana Software, Inc. and others.
// All Rights Reserved.
// You must accept the terms of that agreement to use this software.
//
// jhyde, 28 August, 2001
*/
package mondrian.rolap.agg;
import mondrian.olap.Level;
import mondrian.olap.Member;
import mondrian.olap.SchemaReader;
import mondrian.olap.Util;
import mondrian.rolap.RolapStar;
import mondrian.rolap.BitKey;
import mondrian.rolap.sql.SqlQuery;
import java.lang.ref.SoftReference;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.util.*;
/**
* A <code>Aggregation</code> is a pre-computed aggregation over a set of
* columns.
*
* <p>Rollup operations:<ul>
* <li>drop an unrestricted column (e.g. state=*)</li>
* <li>tighten any restriction (e.g. year={1997,1998} becomes
* year={1997})</li>
* <li>restrict an unrestricted column (e.g. year=* becomes
* year={1997})</li>
* </ul>
*
* <p>Representation of aggregations. Sparse and dense representations are
* necessary for different data sets. Should adapt automatically. Use an
* interface to hold the data set, so the segment doesn't care.</p>
*
* Suppose we have a segment {year=1997, quarter={1,2,3},
* state={CA,WA}}. We want to roll up to a segment for {year=1997,
* state={CA,WA}}. We need to know that we have all quarters. We don't.
* Because year and quarter are independent, we know that we have all of
* the ...</p>
*
* <p>Suppose we have a segment specified by {region=West, state=*,
* year=*}, which materializes to ({West}, {CA,WA,OR}, {1997,1998}).
* Because state=*, we can rollup to {region=West, year=*} or {region=West,
* year=1997}.</p>
*
* <p>The space required for a segment depends upon the dimensionality (d),
* cell count (c) and the value count (v). We don't count the space
* required for the actual values, which is the same in any scheme.</p>
*
* <b>Note to developers</b>: {@link Segment} implements
* {@link CachePool.Cacheable}, and must adhere to the contract that imposes.
* For this class, that means that references to segments must be made using
* soft references (see {@link CachePool.SoftCacheableReference}) so that they
* can be garbage-collected.
*
* @author jhyde
* @since 28 August, 2001
* @version $Id: //open/mondrian/src/main/mondrian/rolap/agg/Aggregation.java#24 $
**/
public class Aggregation {
private static final int MAXLEN_ORACLE = 1000;
private final RolapStar star;
private final BitKey bitKey;
/**
* List of soft references to segments.
* Access must be inside of synchronized methods.
**/
private final List segmentRefs;
private final boolean oracle;
/**
* This is set in the load method and is used during
* the processing of a particular aggregate load.
* The AggregateManager synchonizes access to each instance's methods
* so that an instance is processing a single set of columns, measures,
* contraints at any one time.
*/
private RolapStar.Column[] columns;
public Aggregation(RolapStar star,
BitKey bitKey) {
this.star = star;
this.bitKey = bitKey;
this.segmentRefs = new ArrayList();
this.oracle = star.getSqlQueryDialect().isOracle();
}
/**
* Loads a set of segments into this aggregation, one per measure,
* each constrained by the same set of column values, and each pinned
* once.
* <p>
* A Column and its constraints are accessed at the same level in their
* respective arrays.
*
* For example,
* measures = {unit_sales, store_sales},
* state = {CA, OR},
* gender = unconstrained
*/
public synchronized void load(RolapStar.Column[] columns,
RolapStar.Measure[] measures,
ColumnConstraint[][] constraintses,
Collection pinnedSegments) {
this.columns = columns;
BitKey bitKey = this.bitKey.copy();
int axisCount = columns.length;
Util.assertTrue(constraintses.length == axisCount);
// This array of Aggregation.Axis is shared by all Segments for
// this set of measures and constraintses
Aggregation.Axis[] axes = new Aggregation.Axis[axisCount];
for (int i = 0; i < axisCount; i++) {
axes[i] = new Aggregation.Axis(columns[i], constraintses[i]);
}
boolean isDistinct = false;
Segment[] segments = new Segment[measures.length];
for (int i = 0; i < measures.length; i++) {
RolapStar.Measure measure = measures[i];
if (measure.getAggregator().isDistinct()) {
isDistinct = true;
}
bitKey.setByPos(measure.getBitPosition());
Segment segment = new Segment(this, measure, constraintses, axes);
segments[i] = segment;
SoftReference ref = new SoftReference(segment);
segmentRefs.add(ref);
pinnedSegments.add(segment);
}
Segment.load(segments, bitKey, isDistinct, pinnedSegments, axes);
}
/**
* Drops constraints, where the list of values is close to the values which
* would be returned anyway.
**/
public synchronized ColumnConstraint[][] optimizeConstraints(
RolapStar.Column[] columns,
ColumnConstraint[][] constraintses) {
Util.assertTrue(constraintses.length == columns.length);
ColumnConstraint[][] newConstraintses =
(ColumnConstraint[][]) constraintses.clone();
double[] bloats = new double[columns.length];
// We want to handle the special case "drilldown" which occurs pretty often.
// Here, the parent is here as a constraint with a single member
// and the list of children as well.
List potentialParents = new ArrayList();
for (int i = 0; i < constraintses.length; i++) {
if (constraintses[i] != null && constraintses[i].length == 1
&& constraintses[i][0].isMember())
potentialParents.add(constraintses[i][0].getMember());
}
for (int i = 0; i < newConstraintses.length; i++) {
double constraintLength = (double) newConstraintses[i].length;
// a set of constraints with only one entry will not be optimized away
if (newConstraintses[i] == null || newConstraintses[i].length < 2) {
bloats[i] = 0.0;
} else {
// Oracle can only handle up to 1000 elements inside an IN(..) clause
if (oracle && newConstraintses[i].length > MAXLEN_ORACLE) {
bloats[i] = 1.0; // will be optimized away
continue;
}
// more than one - check for children of same parent
Member parent = null;
Level level = null;
for (int j = 0; j < newConstraintses[i].length; j++) {
if (!(newConstraintses[i][j].isMember())) {
// should not occur - but
// we compute bloat by #constraints / column cardinality
parent = null;
level = null;
bloats[i] = constraintLength / columns[i].getCardinality();
break;
} else {
Member m = newConstraintses[i][j].getMember();
if (j == 0) {
parent = m.getParentMember();
level = m.getLevel();
} else {
if (parent != null
&& !parent.equals(m.getParentMember())) {
parent = null; // no common parent
}
if (level != null
&& !level.equals(m.getLevel())) {
// should never occur, constraintses are of same level
level = null;
}
}
}
}
boolean done = false;
if (parent != null) {
// common parent exists
if (parent.isAll() || potentialParents.contains(parent) ) {
// common parent is there as constraint
// if the children are complete, this constraint set is
// unneccessary try to get the children directly from
// cache for the drilldown case, the children will be
// in the cache
// - if not, forget this optimization.
int nChildren = -1;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -