mondrian-olap 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. data/.rspec +2 -0
  2. data/Gemfile +15 -0
  3. data/LICENSE-Mondrian.html +259 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.rdoc +219 -0
  6. data/RUNNING_TESTS.rdoc +41 -0
  7. data/Rakefile +46 -0
  8. data/VERSION +1 -0
  9. data/lib/mondrian-olap.rb +1 -0
  10. data/lib/mondrian/jars/commons-collections-3.1.jar +0 -0
  11. data/lib/mondrian/jars/commons-dbcp-1.2.1.jar +0 -0
  12. data/lib/mondrian/jars/commons-logging-1.0.4.jar +0 -0
  13. data/lib/mondrian/jars/commons-math-1.0.jar +0 -0
  14. data/lib/mondrian/jars/commons-pool-1.2.jar +0 -0
  15. data/lib/mondrian/jars/commons-vfs-1.0.jar +0 -0
  16. data/lib/mondrian/jars/eigenbase-properties.jar +0 -0
  17. data/lib/mondrian/jars/eigenbase-resgen.jar +0 -0
  18. data/lib/mondrian/jars/eigenbase-xom.jar +0 -0
  19. data/lib/mondrian/jars/javacup.jar +0 -0
  20. data/lib/mondrian/jars/log4j-1.2.8.jar +0 -0
  21. data/lib/mondrian/jars/log4j.properties +18 -0
  22. data/lib/mondrian/jars/mondrian.jar +0 -0
  23. data/lib/mondrian/jars/olap4j.jar +0 -0
  24. data/lib/mondrian/olap.rb +14 -0
  25. data/lib/mondrian/olap/connection.rb +122 -0
  26. data/lib/mondrian/olap/cube.rb +236 -0
  27. data/lib/mondrian/olap/query.rb +313 -0
  28. data/lib/mondrian/olap/result.rb +155 -0
  29. data/lib/mondrian/olap/schema.rb +158 -0
  30. data/lib/mondrian/olap/schema_element.rb +123 -0
  31. data/mondrian-olap.gemspec +116 -0
  32. data/spec/connection_spec.rb +56 -0
  33. data/spec/cube_spec.rb +259 -0
  34. data/spec/fixtures/MondrianTest.xml +128 -0
  35. data/spec/fixtures/MondrianTestOracle.xml +128 -0
  36. data/spec/query_spec.rb +582 -0
  37. data/spec/rake_tasks.rb +185 -0
  38. data/spec/schema_definition_spec.rb +345 -0
  39. data/spec/spec_helper.rb +67 -0
  40. data/spec/support/matchers/be_like.rb +24 -0
  41. metadata +217 -0
@@ -0,0 +1,259 @@
1
+ require "spec_helper"
2
+
3
+ describe "Cube" do
4
+ before(:all) do
5
+ @schema = Mondrian::OLAP::Schema.define do
6
+ cube 'Sales' do
7
+ table 'sales'
8
+ dimension 'Gender', :foreign_key => 'customer_id' do
9
+ hierarchy :has_all => true, :primary_key => 'id' do
10
+ table 'customers'
11
+ level 'Gender', :column => 'gender', :unique_members => true
12
+ end
13
+ end
14
+ dimension 'Customers', :foreign_key => 'customer_id' do
15
+ hierarchy :has_all => true, :all_member_name => 'All Customers', :primary_key => 'id' do
16
+ table 'customers'
17
+ level 'Country', :column => 'country', :unique_members => true
18
+ level 'State Province', :column => 'state_province', :unique_members => true
19
+ level 'City', :column => 'city', :unique_members => false
20
+ level 'Name', :column => 'fullname', :unique_members => true
21
+ end
22
+ end
23
+ calculated_member 'Non-USA' do
24
+ dimension 'Customers'
25
+ formula '[Customers].[All Customers] - [Customers].[USA]'
26
+ end
27
+ dimension 'Time', :foreign_key => 'time_id', :type => 'TimeDimension' do
28
+ hierarchy :has_all => false, :primary_key => 'id' do
29
+ table 'time'
30
+ level 'Year', :column => 'the_year', :type => 'Numeric', :unique_members => true, :level_type => 'TimeYears'
31
+ level 'Quarter', :column => 'quarter', :unique_members => false, :level_type => 'TimeQuarters'
32
+ level 'Month', :column => 'month_of_year', :type => 'Numeric', :unique_members => false, :level_type => 'TimeMonths'
33
+ end
34
+ hierarchy 'Weekly', :has_all => false, :primary_key => 'id' do
35
+ table 'time'
36
+ level 'Year', :column => 'the_year', :type => 'Numeric', :unique_members => true, :level_type => 'TimeYears'
37
+ level 'Week', :column => 'weak_of_year', :type => 'Numeric', :unique_members => false, :level_type => 'TimeWeeks'
38
+ end
39
+ end
40
+ measure 'Unit Sales', :column => 'unit_sales', :aggregator => 'sum'
41
+ measure 'Store Sales', :column => 'store_sales', :aggregator => 'sum'
42
+ end
43
+ end
44
+ @olap = Mondrian::OLAP::Connection.create(CONNECTION_PARAMS.merge :schema => @schema)
45
+ end
46
+
47
+ it "should get all cube names" do
48
+ @olap.cube_names.should == ['Sales']
49
+ end
50
+
51
+ it "should get cube by name" do
52
+ @olap.cube('Sales').should be_a(Mondrian::OLAP::Cube)
53
+ end
54
+
55
+ it "should return nil when getting cube with invalid name" do
56
+ @olap.cube('invalid').should be_nil
57
+ end
58
+
59
+ it "should get cube name" do
60
+ @olap.cube('Sales').name.should == 'Sales'
61
+ end
62
+
63
+ describe "dimensions" do
64
+ before(:all) do
65
+ @cube = @olap.cube('Sales')
66
+ @dimension_names = ['Measures', 'Gender', 'Customers', 'Time']
67
+ end
68
+
69
+ it "should get dimension names" do
70
+ @cube.dimension_names.should == @dimension_names
71
+ end
72
+
73
+ it "should get dimensions" do
74
+ @cube.dimensions.map{|d| d.name}.should == @dimension_names
75
+ end
76
+
77
+ it "should get dimension by name" do
78
+ @cube.dimension('Gender').name.should == 'Gender'
79
+ end
80
+
81
+ it "should get dimension full name" do
82
+ @cube.dimension('Gender').full_name.should == '[Gender]'
83
+ end
84
+
85
+ it "should get measures dimension" do
86
+ @cube.dimension('Measures').should be_measures
87
+ end
88
+
89
+ it "should get dimension type" do
90
+ @cube.dimension('Gender').dimension_type.should == :standard
91
+ @cube.dimension('Time').dimension_type.should == :time
92
+ @cube.dimension('Measures').dimension_type.should == :measures
93
+ end
94
+ end
95
+
96
+ describe "dimension hierarchies" do
97
+ before(:all) do
98
+ @cube = @olap.cube('Sales')
99
+ end
100
+
101
+ it "should get hierarchies" do
102
+ hierarchies = @cube.dimension('Gender').hierarchies
103
+ hierarchies.size.should == 1
104
+ hierarchies[0].name.should == 'Gender'
105
+ end
106
+
107
+ it "should get hierarchy names" do
108
+ @cube.dimension('Time').hierarchy_names.should == ['Time', 'Time.Weekly']
109
+ end
110
+
111
+ it "should get hierarchy by name" do
112
+ @cube.dimension('Time').hierarchy('Time.Weekly').name.should == 'Time.Weekly'
113
+ end
114
+
115
+ it "should get default hierarchy" do
116
+ @cube.dimension('Time').hierarchy.name.should == 'Time'
117
+ end
118
+
119
+ it "should get hierarchy levels" do
120
+ @cube.dimension('Customers').hierarchy.levels.map(&:name).should == ['(All)', 'Country', 'State Province', 'City', 'Name']
121
+ end
122
+
123
+ it "should get hierarchy level names" do
124
+ @cube.dimension('Time').hierarchy.level_names.should == ['Year', 'Quarter', 'Month']
125
+ @cube.dimension('Customers').hierarchy.level_names.should == ['(All)', 'Country', 'State Province', 'City', 'Name']
126
+ end
127
+
128
+ it "should get hierarchy level depths" do
129
+ @cube.dimension('Customers').hierarchy.levels.map(&:depth).should == [0, 1, 2, 3, 4]
130
+ end
131
+
132
+ it "should get hierarchy level members count" do
133
+ @cube.dimension('Gender').hierarchy.levels.map(&:members_count).should == [1, 2]
134
+ end
135
+ end
136
+
137
+ describe "hierarchy values" do
138
+ before(:all) do
139
+ @cube = @olap.cube('Sales')
140
+ end
141
+
142
+ it "should get hierarchy all member" do
143
+ @cube.dimension('Gender').hierarchy.has_all?.should be_true
144
+ @cube.dimension('Gender').hierarchy.all_member_name.should == 'All Genders'
145
+ end
146
+
147
+ it "should not get all member for hierarchy without all member" do
148
+ @cube.dimension('Time').hierarchy.has_all?.should be_false
149
+ @cube.dimension('Time').hierarchy.all_member_name.should be_nil
150
+ end
151
+
152
+ it "should get hierarchy root members" do
153
+ @cube.dimension('Gender').hierarchy.root_members.map(&:name).should == ['All Genders']
154
+ @cube.dimension('Gender').hierarchy.root_member_names.should == ['All Genders']
155
+ @cube.dimension('Time').hierarchy.root_members.map(&:name).should == ['2010', '2011']
156
+ @cube.dimension('Time').hierarchy.root_member_names.should == ['2010', '2011']
157
+ end
158
+
159
+ it "should return child members for specified member" do
160
+ @cube.dimension('Gender').hierarchy.child_names('All Genders').should == ['F', 'M']
161
+ @cube.dimension('Customers').hierarchy.child_names('USA', 'OR').should ==
162
+ ["Albany", "Beaverton", "Corvallis", "Lake Oswego", "Lebanon", "Milwaukie",
163
+ "Oregon City", "Portland", "Salem", "W. Linn", "Woodburn"]
164
+ end
165
+
166
+ it "should return child members for hierarchy" do
167
+ @cube.dimension('Gender').hierarchy.child_names.should == ['F', 'M']
168
+ end
169
+
170
+ it "should not return child members for leaf member" do
171
+ @cube.dimension('Gender').hierarchy.child_names('All Genders', 'F').should == []
172
+ end
173
+
174
+ it "should return nil as child members if parent member not found" do
175
+ @cube.dimension('Gender').hierarchy.child_names('N').should be_nil
176
+ end
177
+
178
+ end
179
+
180
+ describe "level members" do
181
+ before(:all) do
182
+ @cube = @olap.cube('Sales')
183
+ end
184
+
185
+ it "should get primary hierarchy level members" do
186
+ @cube.dimension('Customers').hierarchy.level('Country').members.
187
+ map(&:name).should == ['Canada', 'Mexico', 'USA']
188
+ end
189
+
190
+ it "should get secondary hierarchy level members" do
191
+ @cube.dimension('Time').hierarchy('Time.Weekly').level('Year').members.
192
+ map(&:name).should == ['2010', '2011']
193
+ end
194
+ end
195
+
196
+ describe "members" do
197
+ before(:all) do
198
+ @cube = @olap.cube('Sales')
199
+ end
200
+
201
+ it "should return member for specified full name" do
202
+ @cube.member('[Gender].[All Genders]').name.should == 'All Genders'
203
+ @cube.member('[Customers].[USA].[OR]').name.should == 'OR'
204
+ end
205
+
206
+ it "should not return member for invalid full name" do
207
+ @cube.member('[Gender].[invalid]').should be_nil
208
+ end
209
+
210
+ it "should return child members for member" do
211
+ @cube.member('[Gender].[All Genders]').children.map(&:name).should == ['F', 'M']
212
+ @cube.member('[Customers].[USA].[OR]').children.map(&:name).should ==
213
+ ["Albany", "Beaverton", "Corvallis", "Lake Oswego", "Lebanon", "Milwaukie",
214
+ "Oregon City", "Portland", "Salem", "W. Linn", "Woodburn"]
215
+ end
216
+
217
+ it "should return empty children array if member does not have children" do
218
+ @cube.member('[Gender].[All Genders].[F]').children.should be_empty
219
+ end
220
+
221
+ it "should return member depth" do
222
+ @cube.member('[Customers].[All Customers]').depth.should == 0
223
+ @cube.member('[Customers].[USA]').depth.should == 1
224
+ @cube.member('[Customers].[USA].[CA]').depth.should == 2
225
+ end
226
+
227
+ it "should return descendants for member at specified level" do
228
+ @cube.member('[Customers].[Mexico]').descendants_at_level('City').map(&:name).should ==
229
+ ["San Andres", "Santa Anita", "Santa Fe", "Tixapan", "Acapulco", "Guadalajara",
230
+ "Mexico City", "Tlaxiaco", "La Cruz", "Orizaba", "Merida", "Camacho", "Hidalgo"]
231
+ end
232
+
233
+ it "should not return descendants for member when upper level specified" do
234
+ @cube.member('[Customers].[Mexico].[DF]').descendants_at_level('Country').should be_nil
235
+ end
236
+
237
+ it "should be drillable when member has descendants" do
238
+ @cube.member('[Customers].[USA]').should be_drillable
239
+ end
240
+
241
+ it "should not be drillable when member has no descendants" do
242
+ @cube.member('[Gender].[F]').should_not be_drillable
243
+ end
244
+
245
+ it "should not be drillable when member is calculated" do
246
+ @cube.member('[Customers].[Non-USA]').should_not be_drillable
247
+ end
248
+
249
+ it "should be calculated when member is calculated" do
250
+ @cube.member('[Customers].[Non-USA]').should be_calculated
251
+ end
252
+
253
+ it "should not be calculated when normal member" do
254
+ @cube.member('[Customers].[USA]').should_not be_calculated
255
+ end
256
+
257
+ end
258
+
259
+ end
@@ -0,0 +1,128 @@
1
+ <?xml version="1.0"?>
2
+ <Schema name="MondrianTest">
3
+ <Dimension name="Time" type="TimeDimension">
4
+ <Hierarchy hasAll="false" primaryKey="id">
5
+ <Table name="time"/>
6
+ <Level name="Year" column="the_year" type="Numeric" uniqueMembers="true"
7
+ levelType="TimeYears"/>
8
+ <Level name="Quarter" column="quarter" uniqueMembers="false"
9
+ levelType="TimeQuarters"/>
10
+ <Level name="Month" column="month_of_year" uniqueMembers="false" type="Numeric"
11
+ levelType="TimeMonths"/>
12
+ </Hierarchy>
13
+ <Hierarchy hasAll="true" name="Weekly" primaryKey="id">
14
+ <Table name="time"/>
15
+ <Level name="Year" column="the_year" type="Numeric" uniqueMembers="true"
16
+ levelType="TimeYears"/>
17
+ <Level name="Week" column="week_of_year" type="Numeric" uniqueMembers="false"
18
+ levelType="TimeWeeks"/>
19
+ <Level name="Day" column="day_of_month" uniqueMembers="false" type="Numeric"
20
+ levelType="TimeDays"/>
21
+ </Hierarchy>
22
+ </Dimension>
23
+
24
+ <Dimension name="Product">
25
+ <Hierarchy hasAll="true" primaryKey="id" primaryKeyTable="products">
26
+ <Join leftKey="product_class_id" rightKey="id">
27
+ <Table name="products"/>
28
+ <Table name="product_classes"/>
29
+ </Join>
30
+ <Level name="Product Family" table="product_classes" column="product_family"
31
+ uniqueMembers="true"/>
32
+ <Level name="Product Department" table="product_classes" column="product_department"
33
+ uniqueMembers="false"/>
34
+ <Level name="Product Category" table="product_classes" column="product_category"
35
+ uniqueMembers="false"/>
36
+ <Level name="Product Subcategory" table="product_classes" column="product_subcategory"
37
+ uniqueMembers="false"/>
38
+ <Level name="Brand Name" table="products" column="brand_name" uniqueMembers="false"/>
39
+ <Level name="Product Name" table="products" column="product_name"
40
+ uniqueMembers="true"/>
41
+ </Hierarchy>
42
+ </Dimension>
43
+
44
+ <Cube name="Sales" defaultMeasure="Unit Sales">
45
+ <Table name="sales"/>
46
+ <DimensionUsage name="Time" source="Time" foreignKey="time_id"/>
47
+ <DimensionUsage name="Product" source="Product" foreignKey="product_id"/>
48
+ <Dimension name="Customers" foreignKey="customer_id">
49
+ <Hierarchy hasAll="true" allMemberName="All Customers" primaryKey="id">
50
+ <Table name="customers"/>
51
+ <Level name="Country" column="country" uniqueMembers="true"/>
52
+ <Level name="State Province" column="state_province" uniqueMembers="true"/>
53
+ <Level name="City" column="city" uniqueMembers="false"/>
54
+ <Level name="Name" column="id" type="Numeric" uniqueMembers="true">
55
+ <NameExpression>
56
+ <SQL dialect="oracle">
57
+ fname || ' ' || lname
58
+ </SQL>
59
+ <SQL dialect="postgres">
60
+ "fname" || ' ' || "lname"
61
+ </SQL>
62
+ <SQL dialect="mysql">
63
+ CONCAT(`customer`.`fname`, ' ', `customer`.`lname`)
64
+ </SQL>
65
+ <SQL dialect="generic">
66
+ fullname
67
+ </SQL>
68
+ </NameExpression>
69
+ <OrdinalExpression>
70
+ <SQL dialect="oracle">
71
+ fname || ' ' || lname
72
+ </SQL>
73
+ <SQL dialect="postgres">
74
+ "fname" || ' ' || "lname"
75
+ </SQL>
76
+ <SQL dialect="mysql">
77
+ CONCAT(`customer`.`fname`, ' ', `customer`.`lname`)
78
+ </SQL>
79
+ <SQL dialect="generic">
80
+ fullname
81
+ </SQL>
82
+ </OrdinalExpression>
83
+ <Property name="Gender" column="gender"/>
84
+ </Level>
85
+ </Hierarchy>
86
+ </Dimension>
87
+ <Dimension name="Gender" foreignKey="customer_id">
88
+ <Hierarchy hasAll="true" allMemberName="All Gender" primaryKey="id">
89
+ <Table name="customers"/>
90
+ <Level name="Gender" column="gender" uniqueMembers="true"/>
91
+ </Hierarchy>
92
+ </Dimension>
93
+
94
+ <Measure name="Unit Sales" column="unit_sales" aggregator="sum"
95
+ formatString="Standard"/>
96
+ <Measure name="Store Cost" column="store_cost" aggregator="sum"
97
+ formatString="#,###.00"/>
98
+ <Measure name="Store Sales" column="store_sales" aggregator="sum"
99
+ formatString="#,###.00"/>
100
+ <Measure name="Sales Count" column="product_id" aggregator="count"
101
+ formatString="#,###"/>
102
+ <Measure name="Customer Count" column="customer_id"
103
+ aggregator="distinct-count" formatString="#,###"/>
104
+ <CalculatedMember
105
+ name="Profit"
106
+ dimension="Measures">
107
+ <Formula>[Measures].[Store Sales] - [Measures].[Store Cost]</Formula>
108
+ <CalculatedMemberProperty name="FORMAT_STRING" value="$#,##0.00"/>
109
+ </CalculatedMember>
110
+ <CalculatedMember
111
+ name="Profit last Period"
112
+ dimension="Measures"
113
+ formula="COALESCEEMPTY((Measures.[Profit], [Time].[Time].PREVMEMBER), Measures.[Profit])"
114
+ visible="false">
115
+ <CalculatedMemberProperty name="FORMAT_STRING" value="$#,##0.00"/>
116
+ <CalculatedMemberProperty name="MEMBER_ORDINAL" value="18"/>
117
+ </CalculatedMember>
118
+ <CalculatedMember
119
+ name="Profit Growth"
120
+ dimension="Measures"
121
+ formula="([Measures].[Profit] - [Measures].[Profit last Period]) / [Measures].[Profit last Period]"
122
+ visible="true"
123
+ caption="Gewinn-Wachstum">
124
+ <CalculatedMemberProperty name="FORMAT_STRING" value="0.0%"/>
125
+ </CalculatedMember>
126
+ </Cube>
127
+
128
+ </Schema>
@@ -0,0 +1,128 @@
1
+ <?xml version="1.0"?>
2
+ <Schema name="MondrianTest">
3
+ <Dimension name="Time" type="TimeDimension">
4
+ <Hierarchy hasAll="false" primaryKey="ID">
5
+ <Table name="TIME"/>
6
+ <Level name="Year" column="THE_YEAR" type="Numeric" uniqueMembers="true"
7
+ levelType="TimeYears"/>
8
+ <Level name="Quarter" column="QUARTER" uniqueMembers="false"
9
+ levelType="TimeQuarters"/>
10
+ <Level name="Month" column="MONTH_OF_YEAR" uniqueMembers="false" type="Numeric"
11
+ levelType="TimeMonths"/>
12
+ </Hierarchy>
13
+ <Hierarchy hasAll="true" name="Weekly" primaryKey="ID">
14
+ <Table name="TIME"/>
15
+ <Level name="Year" column="THE_YEAR" type="Numeric" uniqueMembers="true"
16
+ levelType="TimeYears"/>
17
+ <Level name="Week" column="WEEK_OF_YEAR" type="Numeric" uniqueMembers="false"
18
+ levelType="TimeWeeks"/>
19
+ <Level name="Day" column="DAY_OF_MONTH" uniqueMembers="false" type="Numeric"
20
+ levelType="TimeDays"/>
21
+ </Hierarchy>
22
+ </Dimension>
23
+
24
+ <Dimension name="Product">
25
+ <Hierarchy hasAll="true" primaryKey="ID" primaryKeyTable="PRODUCTS">
26
+ <Join leftKey="PRODUCT_CLASS_ID" rightKey="ID">
27
+ <Table name="PRODUCTS"/>
28
+ <Table name="PRODUCT_CLASSES"/>
29
+ </Join>
30
+ <Level name="Product Family" table="PRODUCT_CLASSES" column="PRODUCT_FAMILY"
31
+ uniqueMembers="true"/>
32
+ <Level name="Product Department" table="PRODUCT_CLASSES" column="PRODUCT_DEPARTMENT"
33
+ uniqueMembers="false"/>
34
+ <Level name="Product Category" table="PRODUCT_CLASSES" column="PRODUCT_CATEGORY"
35
+ uniqueMembers="false"/>
36
+ <Level name="Product Subcategory" table="PRODUCT_CLASSES" column="PRODUCT_SUBCATEGORY"
37
+ uniqueMembers="false"/>
38
+ <Level name="Brand Name" table="PRODUCTS" column="BRAND_NAME" uniqueMembers="false"/>
39
+ <Level name="Product Name" table="PRODUCTS" column="PRODUCT_NAME"
40
+ uniqueMembers="true"/>
41
+ </Hierarchy>
42
+ </Dimension>
43
+
44
+ <Cube name="Sales" defaultMeasure="Unit Sales">
45
+ <Table name="SALES"/>
46
+ <DimensionUsage name="Time" source="Time" foreignKey="TIME_ID"/>
47
+ <DimensionUsage name="Product" source="Product" foreignKey="PRODUCT_ID"/>
48
+ <Dimension name="Customers" foreignKey="CUSTOMER_ID">
49
+ <Hierarchy hasAll="true" allMemberName="All Customers" primaryKey="ID">
50
+ <Table name="CUSTOMERS"/>
51
+ <Level name="Country" column="COUNTRY" uniqueMembers="true"/>
52
+ <Level name="State Province" column="STATE_PROVINCE" uniqueMembers="true"/>
53
+ <Level name="City" column="CITY" uniqueMembers="false"/>
54
+ <Level name="Name" column="ID" type="Numeric" uniqueMembers="true">
55
+ <NameExpression>
56
+ <SQL dialect="oracle">
57
+ fname || ' ' || lname
58
+ </SQL>
59
+ <SQL dialect="postgres">
60
+ "fname" || ' ' || "lname"
61
+ </SQL>
62
+ <SQL dialect="mysql">
63
+ CONCAT(`customer`.`fname`, ' ', `customer`.`lname`)
64
+ </SQL>
65
+ <SQL dialect="generic">
66
+ FULLNAME
67
+ </SQL>
68
+ </NameExpression>
69
+ <OrdinalExpression>
70
+ <SQL dialect="oracle">
71
+ fname || ' ' || lname
72
+ </SQL>
73
+ <SQL dialect="postgres">
74
+ "fname" || ' ' || "lname"
75
+ </SQL>
76
+ <SQL dialect="mysql">
77
+ CONCAT(`customer`.`fname`, ' ', `customer`.`lname`)
78
+ </SQL>
79
+ <SQL dialect="generic">
80
+ FULLNAME
81
+ </SQL>
82
+ </OrdinalExpression>
83
+ <Property name="Gender" column="GENDER"/>
84
+ </Level>
85
+ </Hierarchy>
86
+ </Dimension>
87
+ <Dimension name="Gender" foreignKey="CUSTOMER_ID">
88
+ <Hierarchy hasAll="true" allMemberName="All Gender" primaryKey="ID">
89
+ <Table name="CUSTOMERS"/>
90
+ <Level name="Gender" column="GENDER" uniqueMembers="true"/>
91
+ </Hierarchy>
92
+ </Dimension>
93
+
94
+ <Measure name="Unit Sales" column="UNIT_SALES" aggregator="sum"
95
+ formatString="Standard"/>
96
+ <Measure name="Store Cost" column="STORE_COST" aggregator="sum"
97
+ formatString="#,###.00"/>
98
+ <Measure name="Store Sales" column="STORE_SALES" aggregator="sum"
99
+ formatString="#,###.00"/>
100
+ <Measure name="Sales Count" column="PRODUCT_ID" aggregator="count"
101
+ formatString="#,###"/>
102
+ <Measure name="Customer Count" column="CUSTOMER_ID"
103
+ aggregator="distinct-count" formatString="#,###"/>
104
+ <CalculatedMember
105
+ name="Profit"
106
+ dimension="Measures">
107
+ <Formula>[Measures].[Store Sales] - [Measures].[Store Cost]</Formula>
108
+ <CalculatedMemberProperty name="FORMAT_STRING" value="$#,##0.00"/>
109
+ </CalculatedMember>
110
+ <CalculatedMember
111
+ name="Profit last Period"
112
+ dimension="Measures"
113
+ formula="COALESCEEMPTY((Measures.[Profit], [Time].[Time].PREVMEMBER), Measures.[Profit])"
114
+ visible="false">
115
+ <CalculatedMemberProperty name="FORMAT_STRING" value="$#,##0.00"/>
116
+ <CalculatedMemberProperty name="MEMBER_ORDINAL" value="18"/>
117
+ </CalculatedMember>
118
+ <CalculatedMember
119
+ name="Profit Growth"
120
+ dimension="Measures"
121
+ formula="([Measures].[Profit] - [Measures].[Profit last Period]) / [Measures].[Profit last Period]"
122
+ visible="true"
123
+ caption="Gewinn-Wachstum">
124
+ <CalculatedMemberProperty name="FORMAT_STRING" value="0.0%"/>
125
+ </CalculatedMember>
126
+ </Cube>
127
+
128
+ </Schema>