public class BasicQueryTest extends FoodMartTestCase
BasicQueryTest
is a test case which tests simple queries
against the FoodMart database.Modifier and Type | Class and Description |
---|---|
static class |
BasicQueryTest.CountConcurrentUdf |
static class |
BasicQueryTest.MyJdbcStatisticsProvider
Dummy statistics provider for
testStatistics() . |
static class |
BasicQueryTest.SleepUdf
A simple user-defined function which adds one to its argument, but
sleeps 1 ms before doing so.
|
FoodMartTestCase.QueryAndResult
Modifier and Type | Field and Description |
---|---|
(package private) static String |
EmptyResult |
propSaver
Constructor and Description |
---|
BasicQueryTest() |
BasicQueryTest(String name) |
Modifier and Type | Method and Description |
---|---|
void |
_badArgsToTupleFails() |
void |
_testBug793616()
Bug 793616: Deeply nested UNION function takes forever to validate.
|
void |
_testCubeWhichUsesSameSharedDimTwice()
This test modifies the Sales cube to contain both the regular usage
of the [Store] shared dimension, and another usage called [Other Store]
which is connected to the [Unit Sales] column
|
void |
_testCumlativeSums()
How Can I Show Cumulative Sums as Measures?
|
void |
_testDairy() |
void |
_testDateRange()
How Can I Use Date Ranges in MDX?
|
void |
_testDc4dtp2()
Other techniques, such as the addition of member properties to the
Time or Product dimensions to support such calculations, are not as
flexible but are much more efficient.
|
void |
_testDifferentCalculationsForDifferentDimensions()
How Can I Use Different Calculations for Different Dimensions?
|
void |
_testDifferentCalculationsForDifferentDimensions2()
The second case is easier to resolve, because MDX provides the
OpeningPeriod and ClosingPeriod MDX functions specifically to support
opening and closing period values.
|
void |
_testDrillingDownMoreThanOneLevel()
How Can I Drill Down More Than One Level Deep, or Skip Levels When
Drilling Down?
|
void |
_testEver() |
void |
_testHalfYearsTrickyCase() |
void |
_testLookupCube()
Basically, the LookupCube function can evaluate a single MDX statement
against a cube other than the cube currently indicated by query context
to retrieve a single string or numeric result.
|
void |
_testMemberPropertyAsCalcMember()
The drawback to using the DIMENSION PROPERTIES statement is that,
for most client applications, the member property is not readily
apparent.
|
void |
_testMembersFunction() |
void |
_testProduct2() |
void |
_testRolling()
How Can I Use Rolling Date Ranges in MDX?
|
void |
_testSet()
The WHERE clause in the previous MDX query effectively provides a
logical AND operator, in which all unit sales for 1997 are returned only
for drink products and only for those sold in stores in the USA.
|
void |
_testSetArgToTupleFails() |
void |
_testTopmost()
How Do I Get the Topmost Members of a Level Broken Out by an Ancestor
Level?
|
void |
_testWarehouseProfit() |
void |
_testYtdGrowth()
How Can I Compare Time Periods in MDX?
|
void |
dont_testParallelFlushCache() |
void |
dont_testParallelMutliple()
Disabled; takes a quite long time.
|
void |
dont_testParallelNot() |
void |
dont_testParallelSomewhat() |
void |
dont_testParallelVery() |
void |
testAllLevelName() |
void |
testAllMemberCaption() |
void |
testArrayIndexOutOfBoundsWithEmptySegment()
This is a test for
MONDRIAN-1605
|
void |
testAsSample7ButUsingVirtualCube() |
void |
testAvgCastProblem()
This tests a fix for bug #1603653
|
void |
testBadComments() |
void |
testBadMeasure1() |
void |
testBadMeasure2() |
void |
testBasketAnalysis()
Basket analysis is a topic better suited to data mining discussions,
but some basic forms of basket analysis can be handled through the use of
MDX queries.
|
void |
testBasketAnalysisAfterFlush()
Flushes the cache then runs
testBasketAnalysis() , because this
test has been known to fail when run standalone. |
void |
testBigQuery()
Requires the use of a sparse segment, because the product dimension
has 6 atttributes, the product of whose cardinalities is ~8M.
|
void |
testBothAxesEmpty()
Tests that a query whose axes are empty works; bug
MONDRIAN-52.
|
void |
testBug1630754()
Tests for bug #1630754.
|
void |
testBug636687()
The bug happened when a cell which was in cache was compared with a cell
which was not in cache.
|
void |
testBug769114()
Bug 769114: Internal error ("not found") when executing
Order(TopCount).
|
void |
testBugMondrian14()
If a measure (in this case,
[Measures].[Sales Count] )
occurs only within a format expression, bug
MONDRIAN-14. |
void |
testBugMondrian34()
This bug causes all of the format strings to be the same, because the
required expression [Measures].[Unit Sales] is not in the cache; bug
MONDRIAN-34.
|
void |
testBugMondrian36()
Tuple as slicer causes
ClassCastException ; bug
MONDRIAN-36. |
void |
testBugMondrian46()
Query with distinct-count measure and no other measures gives
ArrayIndexOutOfBoundsException ;
MONDRIAN-46. |
void |
testBugMondrian8()
|
void |
testCalculatedMemberWhichIsNotAMeasure() |
void |
testCancel() |
void |
testCaseInsensitiveResolution()
Test for MONDRIAN-1560
Verifies that various references to a member resolve
correctly when case.sensitive=false
|
void |
testCatalogHierarchyBasedOnView() |
void |
testCatalogHierarchyBasedOnView2()
Run a query against a large hierarchy, to make sure that we can generate
joins correctly.
|
void |
testCellValue() |
void |
testCompoundSlicer()
Used to test that a slicer with multiple values gives an error; bug
MONDRIAN-96.
|
void |
testCompoundSlicerNonEmpty()
Test case for
MONDRIAN-814,
"MDX with specific where clause doesn't work" .
|
void |
testConcurrentStatementRun_2()
This is a test for
MONDRIAN-1161.
|
void |
testConcurrentStatementRun()
This is a test for MONDRIAN-1014.
|
void |
testConstantNumber() |
void |
testConstantString() |
void |
testCountDistinct() |
void |
testCountDistinctAgg()
Turn off aggregate caching and run query with both use of aggregate
tables on and off - should result in the same answer.
|
void |
testCrossjoinWithDescendantsAndUnknownMember()
|
void |
testCustomMemberProperties()
How Can I Use Custom Member Properties in MDX?
|
void |
testCycle()
Disabled test.
|
void |
testCyclicalCalculatedMembers() |
void |
testDefaultMeasureInCube() |
void |
testDefaultMeasureInCubeForCaseSensitivity() |
void |
testDefaultMeasureInCubeForIncorrectMeasureName() |
void |
testDependsOn()
Makes sure that the expression
[Measures].[Unit Sales] / ([Measures].[Unit Sales], [Product].[All
Products])
depends on the current member of the Product dimension, although
[Product].[All Products] is referenced from the expression. |
void |
testDifferentCalcsForDifferentTimePeriods()
How Can I Use Different Calculations for Different Time Periods?
|
void |
testDifferentCalculations2()
This calculated measure is more powerful than it seems; if, for
example, you then want to see the average number of units ordered for
beer products in all of the stores in the California area, the following
MDX query can be executed with the same calculated measure.
|
void |
testDifferentCalculationsForDifferentLevels()
How Can I Use Different Calculations for Different Levels in a
Dimension?
|
void |
testDimWithoutAll()
Bug 1250080 caused a dimension with no 'all' member to be constrained
twice.
|
void |
testDirectMemberReferenceOnDimensionWithCalculationsDefined() |
void |
testDuplicateAxisFails() |
void |
testDynamicFormat() |
void |
testEmptyAggregationListDueToFilterDoesNotThrowException() |
void |
testEmptyProperty()
the following query raised a classcast exception because
an empty property evaluated as "NullMember"
note: Store "HQ" does not have a "Store Manager"
|
void |
testEmptySqlBug()
Testcase for Pentaho bug
BISERVER-1323,
empty SQL query generated when crossjoining more than two sets each
containing just the 'all' member.
|
void |
testEmptyTupleSlicerFails() |
void |
testExplain() |
void |
testExplainComplex() |
void |
testExplainInvalid() |
void |
testFilteredCrossJoin()
This resulted in
OutOfMemoryError when the
BatchingCellReader did not know the values for the tuples that
were used in filters. |
void |
testFilterWithCrossJoin()
Testcase for bug 1755778, "CrossJoin / Filter query returns null row in
result set"
|
void |
testFormatInheritance() |
void |
testFormatInheritanceUseFirstValid()
Tests format inheritance with complex expression to assert that the
format of the first member that has a valid format is used.
|
void |
testFormatInheritanceUseSecondIfFirstHasNoFormat()
Test format inheritance to pickup format from second measure when the
first does not have one.
|
void |
testFormatInheritanceWithIIF() |
void |
testFormatInheritanceWorksWithFirstFormatItFinds()
For a calulated member picks up the format of first member that has a
format.
|
void |
testFormatOfNil()
Test case for bug
MONDRIAN-434,
"Small negative numbers cause exceptions w 2-section format".
|
void |
testFormatOfNulls() |
void |
testFormatStringAppliedToStringValue()
Test format string values.
|
void |
testGetCaptionUsingMemberDotCaption() |
void |
testGetCaptionUsingMemberDotPropertiesCaption() |
void |
testGetContext()
Unit test for the
Cell.getContextMember(mondrian.olap.Hierarchy)
method. |
void |
testGoodComments() |
void |
testHalfYears() |
void |
testHeterogeneousAxis()
|
void |
testHierarchiesOfSameDimensionOnDifferentAxes()
Tests hierarchies of the same dimension on different axes.
|
void |
testHierDifferentKeyClass()
description of this testcase:
A calculated member is created on the time.month level.
|
void |
testHighCardSqlTupleReaderLeakingConnections()
This unit test would cause connection leaks without a fix for bug
MONDRIAN-571,
"HighCardSqlTupleReader does not close SQL Connections".
|
void |
testIifWithMemberFirstAndTupleNextWithMeasure() |
void |
testIifWithMemberFirstAndTupleNextWithoutMeasure() |
void |
testIifWithTupleFirstAndMemberNextWithMeasure() |
void |
testIifWithTupleFirstAndMemberNextWithoutMeasure() |
void |
testIifWithTuplesOfUnequalSizes() |
void |
testIifWithTuplesOfUnequalSizesAndOrder() |
void |
testInvalidAxisFails() |
void |
testInvalidMembersInQuery() |
void |
testLogicalAnd()
A logical AND, by contrast, can be supported by using two different
techniques.
|
void |
testLogicalOps()
How Can I Implement a Logical AND or OR Condition in a WHERE
Clause?
|
void |
testMemberOnAxis()
If an axis expression is a member, implicitly convert it to a set.
|
void |
testMemberOrdinalCaching() |
void |
testMemberSameNameAsLevel() |
void |
testMembersOfLargeDimensionTheHardWay() |
void |
testMemberVisibility() |
void |
testMemberWithNullKey()
There are cross database order issues in this test.
|
void |
testMid()
|
void |
testMondrian1432_ZeroAxisSegment() |
void |
testMondrian1432()
|
void |
testMondrian1506()
Test for
MONDRIAN-1506
|
void |
testMultipleCalculatedMembersWhichAreNotMeasures() |
void |
testMultipleCalculatedMembersWhichAreNotMeasures2()
Testcase for bug
MONDRIAN-77, "Calculated member name conflict".
|
void |
testMultipleCalculatedMembersWhichAreNotMeasures3()
This one had the same problem.
|
void |
testMultipleConstraintsOnSameColumn()
Tests whether the agg mgr behaves correctly if a cell request causes
a column to be constrained multiple times.
|
void |
testNonEmpty1() |
void |
testNonEmpty2() |
void |
testNonEmptyCrossJoin()
Tests a query with a CrossJoin so large that we run out of memory unless
we can push down evaluation to SQL.
|
void |
testNonEmptyCrossjoinFilter()
Tests a query which uses filter and crossjoin.
|
void |
testNonEmptyNonEmptyCrossJoin1()
NonEmptyCrossJoin() is not the same as NON EMPTY CrossJoin()
because it's evaluated independently of the other axes.
|
void |
testNonEmptyNonEmptyCrossJoin2() |
void |
testNonEmptyNonEmptyCrossJoin3() |
void |
testNonEmptyNonEmptyCrossJoin4() |
void |
testNullMember() |
void |
testNullMemberWithOneNonNull() |
void |
testNumericToLogicalConversion()
This tests for bug #1706434,
the ability to convert numeric types to logical (boolean) types.
|
void |
testOneDimensionalQueryWithTupleAsSlicer() |
void |
testOverlappingCalculatedMembers()
Bug #1005995 - many totals of various dimensions
|
void |
testOverrideDimension() |
void |
testPercentagesAsMeasures()
How Can I Show Percentages as Measures?
|
void |
testQueryIterationLimit() |
void |
testQueryTimeout() |
void |
testRank()
How Can I Rank or Reorder Members?
|
void |
testResultLimit() |
void |
testRollup() |
void |
testRollupQuery() |
void |
testSameDimOnTwoAxesFails()
It is illegal for a query to have the same dimension on more than
one axis.
|
void |
testSample0() |
void |
testSample1() |
void |
testSample2() |
void |
testSample3() |
void |
testSample4() |
void |
testSample5() |
void |
testSample5Snowflake() |
void |
testSample6() |
void |
testSample7() |
void |
testSample8() |
void |
testScalarOnAxisFails() |
void |
testSchemaLevelOrdinalInOtherTable() |
void |
testSchemaLevelTableInAnotherHierarchy() |
void |
testSchemaLevelTableIsBad() |
void |
testSchemaLevelWithViewSpecifiesTable() |
void |
testSchemaTopLevelNotUnique() |
void |
testSlicerIsEvaluatedBeforeAxes() |
void |
testSlicerOverride()
Slicer contains
[Promotion Media].[Daily Paper] , but
filter expression is in terms of [Promotion Media].[Radio] . |
void |
testSlicerWithCalculatedMembers() |
void |
testSolveOrder() |
void |
testSolveOrderAmbiguous1()
Test what happens when the solve orders are the same.
|
void |
testSolveOrderAmbiguous2()
In the second test, the answer should be 2 because Product comes before
Promotions in the FoodMart.xml schema.
|
void |
testSolveOrderNonMeasure() |
void |
testSolveOrderNonMeasure2() |
void |
testStatistics()
Unit test for
StatisticsProvider and implementations
JdbcStatisticsProvider and
SqlStatisticsProvider . |
void |
testStoreCube()
Tests that the "Store" cube is working.
|
void |
testStringComparisons()
How Can I Perform Complex String Comparisons?
|
void |
testSummingProperties()
Tests various ways to sum the properties of the descendants of a member,
inspired by forum post
summing
properties.
|
void |
testTaglib0() |
void |
testTaglib1() |
void |
testTaglib2() |
void |
testTaglib3() |
void |
testTaglib4() |
void |
testTaglib5() |
void |
testTopmost2()
The MDX Descendants function is used to construct a set consisting of
only those members at the Store Name level in the Store dimension.
|
void |
testUnparse() |
void |
testUnparse2() |
void |
testUseDimensionAsShorthandForMember() |
void |
testVirtualCube() |
void |
testZeroValuesAreNotTreatedAsNull() |
allMember, assertAxisReturns, assertAxisThrows, assertBooleanExprReturns, assertExprReturns, assertExprThrows, assertQueriesReturnSimilarResults, assertQueryReturns, assertQueryThrows, assertSize, cubeByName, execute, executeExpr, executeQuery, executeSingletonAxis, genderMembersIncludingAll, getConnection, getDimensionWithName, getTestContext, isDefaultNullMemberRepresentation, isGroupingSetsSupported, member, productMembersPotScrubbersPotsAndPans, storeMembersCAAndOR, storeMembersUsaAndCanada, tearDown, warehouseMembersCanadaMexicoUsa
countTestCases, createResult, getName, run, run, runBare, runTest, setName, setUp, toString
assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertFalse, assertFalse, assertNotNull, assertNotNull, assertNotSame, assertNotSame, assertNull, assertNull, assertSame, assertSame, assertTrue, assertTrue, fail, fail
static final String EmptyResult
public BasicQueryTest()
public BasicQueryTest(String name)
public void testSample0()
public void testSample1()
public void testSample2()
public void testSample3()
public void testSample4()
public void testSample5()
public void testSample5Snowflake()
public void testSample6()
public void testSample7()
public void testSample8()
public void testGoodComments()
public void testBadComments()
public void testBothAxesEmpty()
public void testCompoundSlicer()
public void testCompoundSlicerNonEmpty()
public void testEmptyTupleSlicerFails()
public void testBigQuery()
public void testGetContext()
Cell.getContextMember(mondrian.olap.Hierarchy)
method.public void testNonEmpty1()
public void testNonEmpty2()
public void testOneDimensionalQueryWithTupleAsSlicer()
public void testSlicerIsEvaluatedBeforeAxes()
public void testSlicerWithCalculatedMembers()
public void _testEver()
public void _testDairy()
public void testSolveOrder()
public void testSolveOrderNonMeasure()
public void testSolveOrderNonMeasure2()
public void testSolveOrderAmbiguous1()
In the first test, the answer should be 1 because Promotions comes before Customers in the FoodMart.xml schema.
public void testSolveOrderAmbiguous2()
public void testCalculatedMemberWhichIsNotAMeasure()
public void testMultipleCalculatedMembersWhichAreNotMeasures()
public void testMultipleCalculatedMembersWhichAreNotMeasures2()
There used to be something wrong with non-measure calculated members where the ordering of the WITH MEMBER would determine whether or not the member would be found in the cube. This test would fail but the previous one would work ok.
public void testMultipleCalculatedMembersWhichAreNotMeasures3()
public void testConstantString()
public void testConstantNumber()
public void testCyclicalCalculatedMembers()
public void testCycle()
public void testHalfYears()
public void _testHalfYearsTrickyCase()
public void testAsSample7ButUsingVirtualCube()
public void testVirtualCube()
public void testUseDimensionAsShorthandForMember()
public void _testMembersFunction()
public void _testProduct2()
public void testTaglib0()
public void testTaglib1()
public void testTaglib2()
public void testTaglib3()
public void testTaglib4()
public void testTaglib5()
public void testCellValue()
public void testDynamicFormat()
public void testFormatOfNulls()
public void testFormatOfNil()
public void testBugMondrian14()
[Measures].[Sales Count]
)
occurs only within a format expression, bug
MONDRIAN-14.
causes an internal
error ("value not found") when the cell's formatted value is retrieved.public void testBugMondrian34()
public void testBugMondrian36()
ClassCastException
; bug
MONDRIAN-36.public void testBugMondrian46()
ArrayIndexOutOfBoundsException
;
MONDRIAN-46.public void testStoreCube()
The [Fact Count] measure, which is implicitly created because the cube definition does not include an explicit count measure, is flagged 'not visible' but is still correctly returned from [Measures].Members.
public void testSchemaLevelTableIsBad()
public void testSchemaLevelTableInAnotherHierarchy()
public void testSchemaLevelWithViewSpecifiesTable()
public void testSchemaLevelOrdinalInOtherTable()
public void testSchemaTopLevelNotUnique()
public void testBugMondrian8()
public void testBug636687()
RuntimeException
which indicates that the cell is not in cache.public void testBug769114()
public void _testBug793616()
O(2 ^ depth)
.)public void testCatalogHierarchyBasedOnView()
public void testMemberSameNameAsLevel() throws SQLException
SQLException
public void testCatalogHierarchyBasedOnView2()
public void testCountDistinct()
public void testCountDistinctAgg()
public void testMemberWithNullKey()
public void testCrossjoinWithDescendantsAndUnknownMember()
public void testSlicerOverride()
[Promotion Media].[Daily Paper]
, but
filter expression is in terms of [Promotion Media].[Radio]
.public void testMembersOfLargeDimensionTheHardWay()
public void testUnparse()
public void testUnparse2()
public void _testLookupCube()
For example, the Budget cube in the FoodMart 2000 database contains budget information that can be displayed by store. The Sales cube in the FoodMart 2000 database contains sales information that can be displayed by store. Since no virtual cube exists in the FoodMart 2000 database that joins the Sales and Budget cubes together, comparing the two sets of figures would be difficult at best.
Note In many situations a virtual cube can be used to integrate data from multiple cubes, which will often provide a simpler and more efficient solution than the LookupCube function. This example uses the LookupCube function for purposes of illustration.
The following MDX query, however, uses the LookupCube function to retrieve unit sales information for each store from the Sales cube, presenting it side by side with the budget information from the Budget cube.
public void testBasketAnalysis()
Basket analysis is a topic better suited to data mining discussions, but some basic forms of basket analysis can be handled through the use of MDX queries.
For example, one method of basket analysis groups customers based on qualification. In the following example, a qualified customer is one who has more than $10,000 in store sales or more than 10 unit sales. The following table illustrates such a report, run against the Sales cube in FoodMart 2000 with qualified customers grouped by the Country and State Province levels of the Customers dimension. The count and store sales total of qualified customers is represented by the Qualified Count and Qualified Sales columns, respectively.
To accomplish this basic form of basket analysis, the following MDX query constructs two calculated members. The first calculated member uses the MDX Count, Filter, and Descendants functions to create the Qualified Count column, while the second calculated member uses the MDX Sum, Filter, and Descendants functions to create the Qualified Sales column.
The key to this MDX query is the use of Filter and Descendants together to screen out non-qualified customers. Once screened out, the Sum and Count MDX functions can then be used to provide aggregation data only on qualified customers.
public void testBasketAnalysisAfterFlush()
testBasketAnalysis()
, because this
test has been known to fail when run standalone.public void testStringComparisons()
MDX can handle basic string comparisons, but does not include complex string comparison and manipulation functions, for example, for finding substrings in strings or for supporting case-insensitive string comparisons. However, since MDX can take advantage of external function libraries, this question is easily resolved using string manipulation and comparison functions from the Microsoft Visual Basic for Applications (VBA) external function library.
For example, you want to report the unit sales of all fruit-based products -- not only the sales of fruit, but canned fruit, fruit snacks, fruit juices, and so on. By using the LCase and InStr VBA functions, the following results are easily accomplished in a single MDX query, without complex set construction or explicit member names within the query.
The following MDX query demonstrates how to achieve the results displayed in the previous table. For each member in the Product dimension, the name of the member is converted to lowercase using the LCase VBA function. Then, the InStr VBA function is used to discover whether or not the name contains the word "fruit". This information is used to then construct a set, using the Filter MDX function, from only those members from the Product dimension that contain the substring "fruit" in their names.
public void testMid()
public void testPercentagesAsMeasures()
Another common business question easily answered through MDX is the display of percent values created as available measures.
For example, the Sales cube in the FoodMart 2000 database contains unit sales for each store in a given city, state, and country, organized along the Sales dimension. A report is requested to show, for California, the percentage of total unit sales attained by each city with a store. The results are illustrated in the following table.
Because the parent of a member is typically another, aggregated member in a regular dimension, this is easily achieved by the construction of a calculated member, as demonstrated in the following MDX query, using the CurrentMember and Parent MDX functions.
public void _testCumlativeSums()
Another common business request, cumulative sums, is useful for business reporting purposes. However, since aggregations are handled in a hierarchical fashion, cumulative sums present some unique challenges in Analysis Services.
The best way to create a cumulative sum is as a calculated measure in MDX, using the Rank, Head, Order, and Sum MDX functions together.
For example, the following table illustrates a report that shows two views of employee count in all stores and cities in California, sorted by employee count. The first column shows the aggregated counts for each store and city, while the second column shows aggregated counts for each store, but cumulative counts for each city.
The cumulative number of employees for San Diego represents the value of both Los Angeles and San Diego, the value for Beverly Hills represents the cumulative total of Los Angeles, San Diego, and Beverly Hills, and so on.
Since the members within the state of California have been ordered from highest to lowest number of employees, this form of cumulative sum measure provides a form of pareto analysis within each state.
To support this, the Order function is first used to reorder members accordingly for both the Rank and Head functions. Once reordered, the Rank function is used to supply the ranking of each tuple within the reordered set of members, progressing as each member in the Store dimension is examined. The value is then used to determine the number of tuples to retrieve from the set of reordered members using the Head function. Finally, the retrieved members are then added together using the Sum function to obtain a cumulative sum. The following MDX query demonstrates how all of this works in concert to provide cumulative sums.
As an aside, a named set cannot be used in this situation to replace the duplicate Order function calls. Named sets are evaluated once, when a query is parsed -- since the set can change based on the fact that the set can be different for each store member because the set is evaluated for the children of multiple parents, the set does not change with respect to its use in the Sum function. Since the named set is only evaluated once, it would not satisfy the needs of this query.
public void testLogicalOps()
For SQL users, the use of AND and OR logical operators in the WHERE clause of a SQL statement is an essential tool for constructing business queries. However, the WHERE clause of an MDX statement serves a slightly different purpose, and understanding how the WHERE clause is used in MDX can assist in constructing such business queries.
The WHERE clause in MDX is used to further restrict the results of an MDX query, in effect providing another dimension on which the results of the query are further sliced. As such, only expressions that resolve to a single tuple are allowed. The WHERE clause implicitly supports a logical AND operation involving members across different dimensions, by including the members as part of a tuple. To support logical AND operations involving members within a single dimensions, as well as logical OR operations, a calculated member needs to be defined in addition to the use of the WHERE clause.
For example, the following MDX query illustrates the use of a calculated member to support a logical OR. The query returns unit sales by quarter and year for all food and drink related products sold in 1997, run against the Sales cube in the FoodMart 2000 database.
The calculated member simply adds the values of the Unit Sales measure for the Food and the Drink levels of the Product dimension together. The WHERE clause is then used to restrict return of information only to the calculated member, effectively implementing a logical OR to return information for all time periods that contain unit sales values for either food, drink, or both types of products.
You can use the Aggregate function in similar situations where all measures are not aggregated by summing. To return the same results in the above example using the Aggregate function, replace the definition for the calculated member with this definition:
'Aggregate({[Product].[Food], [Product].[Drink]})'
public void testLogicalAnd()
A logical AND, by contrast, can be supported by using two different techniques. If the members used to construct the logical AND reside on different dimensions, all that is required is a WHERE clause that uses a tuple representing all involved members. The following MDX query uses a WHERE clause that effectively restricts the query to retrieve unit sales for drink products in the USA, shown by quarter and year for 1997.
public void _testSet()
The WHERE clause in the previous MDX query effectively provides a logical AND operator, in which all unit sales for 1997 are returned only for drink products and only for those sold in stores in the USA.
If the members used to construct the logical AND condition reside on the same dimension, you can use a calculated member or a named set to filter out the unwanted members, as demonstrated in the following MDX query.
The named set, [Good AND Pearl Stores], restricts the displayed unit sales totals only to those stores that have sold both Good products and Pearl products.
public void testCustomMemberProperties()
Member properties are a good way of adding secondary business information to members in a dimension. However, getting that information out can be confusing -- member properties are not readily apparent in a typical MDX query.
Member properties can be retrieved in one of two ways. The easiest and most used method of retrieving member properties is to use the DIMENSION PROPERTIES MDX statement when constructing an axis in an MDX query.
For example, a member property in the Store dimension in the FoodMart 2000 database details the total square feet for each store. The following MDX query can retrieve this member property as part of the returned cellset.
public void _testMemberPropertyAsCalcMember()
The drawback to using the DIMENSION PROPERTIES statement is that, for most client applications, the member property is not readily apparent. If the previous MDX query is executed in the MDX sample application shipped with SQL Server 2000 Analysis Services, for example, you must double-click the name of the member in the grid to open the Member Properties dialog box, which displays all of the member properties shipped as part of the cellset, including the [Store].[Store Name].[Store Sqft] member property.
The other method of retrieving member properties involves the creation of a calculated member based on the member property. The following MDX query brings back the total square feet for each store as a measure, included in the COLUMNS axis.
The [Store SqFt] measure is constructed with the Properties MDX function to retrieve the [Store SQFT] member property for each member in the Store dimension. The benefit to this technique is that the calculated member is readily apparent and easily accessible in client applications that do not support member properties.
public void _testDrillingDownMoreThanOneLevel()
Drilling down is an essential ability for most OLAP products, and Analysis Services is no exception. Several functions exist that support drilling up and down the hierarchy of dimensions within a cube. Typically, drilling up and down the hierarchy is done one level at a time; think of this functionality as a zoom feature for OLAP data.
There are times, though, when the need to drill down more than one level at the same time, or even skip levels when displaying information about multiple levels, exists for a business scenario.
For example, you would like to show report results from a query of the Sales cube in the FoodMart 2000 sample database showing sales totals for individual cities and the subtotals for each country, as shown in the following table.
The Customers dimension, however, has Country, State Province, and City levels. In order to show the above report, you would have to show the Country level and then drill down two levels to show the City level, skipping the State Province level entirely.
However, the MDX ToggleDrillState and DrillDownMember functions provide drill down functionality only one level below a specified set. To drill down more than one level below a specified set, you need to use a combination of MDX functions, including Descendants, Generate, and Except. This technique essentially constructs a large set that includes all levels between both upper and lower desired levels, then uses a smaller set representing the undesired level or levels to remove the appropriate members from the larger set.
The MDX Descendants function is used to construct a set consisting of the descendants of each member in the Customers dimension. The descendants are determined using the MDX Descendants function, with the descendants of the City level and the level above, the State Province level, for each member of the Customers dimension being added to the set.
The MDX Generate function now creates a set consisting of all members at the Country level as well as the members of the set generated by the MDX Descendants function. Then, the MDX Except function is used to exclude all members at the State Province level, so the returned set contains members at the Country and City levels.
Note, however, that the previous MDX query will still order the members according to their hierarchy. Although the returned set contains members at the Country and City levels, the Country, State Province, and City levels determine the order of the members.
public void _testTopmost()
This type of MDX query is common when only the facts for the lowest level of a dimension within a cube are needed, but information about other levels within the same dimension may also be required to satisfy a specific business scenario.
For example, a report that shows the unit sales for the store with the highest unit sales from each country is needed for marketing purposes. The following table provides an example of this report, run against the Sales cube in the FoodMart 2000 sample database.
This looks simple enough, but the Country Name column provides unexpected difficulty. The values for the Store Country column are taken from the Store Country level of the Store dimension, so the Store Country column is constructed as a calculated member as part of the MDX query, using the MDX Ancestor and Name functions to return the country names for each store.
A combination of the MDX Generate, TopCount, and Descendants functions are used to create a set containing the top stores in unit sales for each country.
public void testTopmost2()
The MDX Descendants function is used to construct a set consisting of only those members at the Store Name level in the Store dimension. Then, the MDX TopCount function is used to return only the topmost store based on the Unit Sales measure. The MDX Generate function then constructs a set based on the topmost stores, following the hierarchy of the Store dimension.
Alternate techniques, such as using the MDX Crossjoin function, may not provide the desired results because non-related joins can occur. Since the Store Country and Store Name levels are within the same dimension, they cannot be cross-joined. Another dimension that provides the same regional hierarchy structure, such as the Customers dimension, can be employed with the Crossjoin function. But, using this technique can cause non-related joins and return unexpected results.
For example, the following MDX query uses the Crossjoin function to attempt to return the same desired results.
However, some unexpected surprises occur because the topmost member in the Store dimension is cross-joined with all of the children of the Customers dimension, as shown in the following table.
In this instance, the use of a calculated member to provide store country names is easier to understand and debug than attempting to cross-join across unrelated members
public void testRank()
One of the issues commonly encountered in business scenarios is the need to rank the members of a dimension according to their corresponding measure values. The Order MDX function allows you to order a set based on a string or numeric expression evaluated against the members of a set. Combined with other MDX functions, the Order function can support several different types of ranking.
For example, the Sales cube in the FoodMart 2000 database can be used to show unit sales for each store. However, the business scenario requires a report that ranks the stores from highest to lowest unit sales, individually, of nonconsumable products.
Because of the requirement that stores be sorted individually, the hierarchy must be broken (in other words, ignored) for the purpose of ranking the stores. The Order function is capable of sorting within the hierarchy, based on the topmost level represented in the set to be sorted, or, by breaking the hierarchy, sorting all of the members of the set as if they existed on the same level, with the same parent.
The following MDX query illustrates the use of the Order function to rank the members according to unit sales.
public void testDifferentCalculationsForDifferentLevels()
This type of MDX query frequently occurs when different aggregations are needed at different levels in a dimension. One easy way to support such functionality is through the use of a calculated measure, created as part of the query, which uses the MDX Descendants function in conjunction with one of the MDX aggregation functions to provide results.
For example, the Warehouse cube in the FoodMart 2000 database supplies the [Units Ordered] measure, aggregated through the Sum function. But, you would also like to see the average number of units ordered per store. The following table demonstrates the desired results.
By using the following MDX query, the desired results can be achieved. The calculated measure, [Average Units Ordered], supplies the average number of ordered units per store by using the Avg, CurrentMember, and Descendants MDX functions.
public void testDifferentCalculations2()
This calculated measure is more powerful than it seems; if, for example, you then want to see the average number of units ordered for beer products in all of the stores in the California area, the following MDX query can be executed with the same calculated measure.
public void _testDifferentCalculationsForDifferentDimensions()
Each measure in a cube uses the same aggregation function across all dimensions. However, there are times where a different aggregation function may be needed to represent a measure for reporting purposes. Two basic cases involve aggregating a single dimension using a different aggregation function than the one used for other dimensions.
The first case involves some knowledge of the behavior of the time dimension specified in the cube. For instance, to create a calculated measure that contains the average, along a time dimension, of measures aggregated as sums along other dimensions, the average of the aggregated measures must be taken over the set of averaging time periods, constructed through the use of the Descendants MDX function. Minimum and maximum values are more easily calculated through the use of the Min and Max MDX functions, also combined with the Descendants function.
For example, the Warehouse cube in the FoodMart 2000 database contains information on ordered and shipped inventory; from it, a report is requested to show the average number of units shipped, by product, to each store. Information on units shipped is added on a monthly basis, so the aggregated measure [Units Shipped] is divided by the count of descendants, at the Month level, of the current member in the Time dimension. This calculation provides a measure representing the average number of units shipped per month, as demonstrated in the following MDX query.
public void _testDifferentCalculationsForDifferentDimensions2()
The second case is easier to resolve, because MDX provides the OpeningPeriod and ClosingPeriod MDX functions specifically to support opening and closing period values.
For example, the Warehouse cube in the FoodMart 2000 database contains information on ordered and shipped inventory; from it, a report is requested to show on-hand inventory at the end of every month. Because the inventory on hand should equal ordered inventory minus shipped inventory, the ClosingPeriod MDX function can be used to create a calculated measure to supply the value of inventory on hand, as demonstrated in the following MDX query.
public void _testDateRange()
Date ranges are a frequently encountered problem. Business questions use ranges of dates, but OLAP objects provide aggregated information in date levels.
Using the technique described here, you can establish date ranges in MDX queries at the level of granularity provided by a time dimension. Date ranges cannot be established below the granularity of the dimension without additional information. For example, if the lowest level of a time dimension represents months, you will not be able to establish a two-week date range without other information. Member properties can be added to supply specific dates for members; using such member properties, you can take advantage of the date and time functions provided by VBA and Excel external function libraries to establish date ranges.
The easiest way to specify a static date range is by using the colon (:) operator. This operator creates a naturally ordered set, using the members specified on either side of the operator as the endpoints for the ordered set. For example, to specify the first six months of 1998 from the Time dimension in FoodMart 2000, the MDX syntax would resemble:
[Time].[1998].[1]:[Time].[1998].[6]
For example, the Sales cube uses a time dimension that supports Year, Quarter, and Month levels. To add a six-month and nine-month total, two calculated members are created in the following MDX query.
public void _testRolling()
There are several techniques that can be used in MDX to support rolling date ranges. All of these techniques tend to fall into two groups. The first group involves the use of relative hierarchical functions to construct named sets or calculated members, and the second group involves the use of absolute date functions from external function libraries to construct named sets or calculated members. Both groups are applicable in different business scenarios.
In the first group of techniques, typically a named set is constructed which contains a number of periods from a time dimension. For example, the following table illustrates a 12-month rolling period, in which the figures for unit sales of the previous 12 months are shown.
The following MDX query accomplishes this by using a number of MDX functions, including LastPeriods, Tail, Filter, Members, and Item, to construct a named set containing only those members across all other dimensions that share data with the time dimension at the Month level. The example assumes that there is at least one measure, such as [Unit Sales], with a value greater than zero in the current period. The Filter function creates a set of months with unit sales greater than zero, while the Tail function returns the last month in this set, the current month. The LastPeriods function, finally, is then used to retrieve the last 12 periods at this level, including the current period.
public void testDifferentCalcsForDifferentTimePeriods()
A few techniques can be used, depending on the structure of the cube being queried, to support different calculations for members depending on the time period. The following example includes the MDX IIf function, and is easy to use but difficult to maintain. This example works well for ad hoc queries, but is not the ideal technique for client applications in a production environment.
For example, the following table illustrates a standard and dynamic forecast of warehouse sales, from the Warehouse cube in the FoodMart 2000 database, for drink products. The standard forecast is double the warehouse sales of the previous year, while the dynamic forecast varies from month to month -- the forecast for January is 120 percent of previous sales, while the forecast for July is 260 percent of previous sales.
The most flexible way of handling this type of report is the use of nested MDX IIf functions to return a multiplier to be used on the members of the Products dimension, at the Drinks level. The following MDX query demonstrates this technique.
public void _testDc4dtp2()
Other techniques, such as the addition of member properties to the Time or Product dimensions to support such calculations, are not as flexible but are much more efficient. The primary drawback to using such techniques is that the calculations are not easily altered for speculative analysis purposes. For client applications, however, where the calculations are static or slowly changing, using a member property is an excellent way of supplying such functionality to clients while keeping maintenance of calculation variables at the server level. The same MDX query, for example, could be rewritten to use a member property named [Dynamic Forecast Multiplier] as shown in the following MDX query.
public void _testWarehouseProfit()
public void _testYtdGrowth()
To answer such a common business question, MDX provides a number of functions specifically designed to navigate and aggregate information across time periods. For example, year-to-date (YTD) totals are directly supported through the YTD function in MDX. In combination with the MDX ParallelPeriod function, you can create calculated members to support direct comparison of totals across time periods.
For example, the following table represents a comparison of YTD unit sales between 1997 and 1998, run against the Sales cube in the FoodMart 2000 database.
The following MDX query uses three calculated members to illustrate how to use the YTD and ParallelPeriod functions in combination to compare time periods.
public void dont_testParallelMutliple()
public void dont_testParallelNot()
public void dont_testParallelSomewhat()
public void dont_testParallelFlushCache()
public void dont_testParallelVery()
public void testDependsOn()
[Measures].[Unit Sales] / ([Measures].[Unit Sales], [Product].[All
Products])
depends on the current member of the Product dimension, although
[Product].[All Products] is referenced from the expression.public void testFilterWithCrossJoin() throws Exception
Exception
- on errorpublic void testFilteredCrossJoin()
OutOfMemoryError
when the
BatchingCellReader did not know the values for the tuples that
were used in filters.public void testNonEmptyCrossJoin()
public void testNonEmptyNonEmptyCrossJoin1()
public void testNonEmptyNonEmptyCrossJoin2()
public void testNonEmptyNonEmptyCrossJoin3()
public void testNonEmptyNonEmptyCrossJoin4()
public void testHierDifferentKeyClass()
public void testOverlappingCalculatedMembers()
public void testEmptyProperty()
public void _testCubeWhichUsesSameSharedDimTwice()
public void testMemberVisibility()
public void testAllMemberCaption()
public void testAllLevelName()
public void testDimWithoutAll()
public void testMemberOnAxis()
public void testScalarOnAxisFails()
public void testSameDimOnTwoAxesFails()
public void _testSetArgToTupleFails()
public void _badArgsToTupleFails()
public void testNullMember()
public void testNullMemberWithOneNonNull()
public void testMultipleConstraintsOnSameColumn()
public void testOverrideDimension()
public void testBadMeasure1()
public void testBadMeasure2()
public void testInvalidMembersInQuery()
public void testMemberOrdinalCaching()
public void testCancel()
public void testQueryTimeout()
public void testFormatInheritance()
public void testFormatInheritanceWithIIF()
public void testFormatInheritanceWorksWithFirstFormatItFinds()
public void testFormatStringAppliedToStringValue()
public void testAvgCastProblem()
public void testFormatInheritanceUseSecondIfFirstHasNoFormat()
public void testFormatInheritanceUseFirstValid()
public void testQueryIterationLimit()
public void testGetCaptionUsingMemberDotCaption()
public void testGetCaptionUsingMemberDotPropertiesCaption()
public void testDefaultMeasureInCube()
public void testDefaultMeasureInCubeForIncorrectMeasureName()
public void testDefaultMeasureInCubeForCaseSensitivity()
public void testNumericToLogicalConversion()
public void testRollupQuery()
public void testBug1630754()
public void testNonEmptyCrossjoinFilter()
ClassCastException
trying to cast a List
to a Iterable
.public void testDuplicateAxisFails()
public void testInvalidAxisFails()
public void testSummingProperties()
public void testIifWithTupleFirstAndMemberNextWithMeasure()
public void testIifWithMemberFirstAndTupleNextWithMeasure()
public void testIifWithMemberFirstAndTupleNextWithoutMeasure()
public void testIifWithTupleFirstAndMemberNextWithoutMeasure()
public void testIifWithTuplesOfUnequalSizes()
public void testIifWithTuplesOfUnequalSizesAndOrder()
public void testEmptyAggregationListDueToFilterDoesNotThrowException()
public void testEmptySqlBug()
public void testHeterogeneousAxis()
public void testHierarchiesOfSameDimensionOnDifferentAxes()
public void testMondrian1432()
public void testMondrian1432_ZeroAxisSegment()
public void testHighCardSqlTupleReaderLeakingConnections()
public void testZeroValuesAreNotTreatedAsNull()
public void testDirectMemberReferenceOnDimensionWithCalculationsDefined()
public void testExplain() throws SQLException
SQLException
public void testExplainComplex() throws SQLException
SQLException
public void testExplainInvalid() throws SQLException
SQLException
public void testConcurrentStatementRun() throws Exception
Exception
public void testRollup()
public void testStatistics()
StatisticsProvider
and implementations
JdbcStatisticsProvider
and
SqlStatisticsProvider
.public void testConcurrentStatementRun_2() throws Exception
Exception
public void testCaseInsensitiveResolution()
public void testMondrian1506() throws Exception
This is a test for a concurrency problem in the old Query API. It also makes sure that we do not leak a future to a segment which we are about to cancel.
It is disabled by default because it can make the JVM crash pretty hard if it is run as part of the full test suite. Something to do with stack heap problems. Probably because the test has to run on multiple threads at the same time.
Exception
public void testArrayIndexOutOfBoundsWithEmptySegment()
When a dense object has only null values, it threw a AIOOBE because the offset resolved to 0 and was used to fetch data directly out of the array.