mondrian-olap 0.4.0-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. data/.rspec +2 -0
  2. data/Changelog.md +60 -0
  3. data/Gemfile +21 -0
  4. data/LICENSE-Mondrian.html +259 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +302 -0
  7. data/RUNNING_TESTS.rdoc +66 -0
  8. data/Rakefile +48 -0
  9. data/VERSION +1 -0
  10. data/lib/mondrian-olap.rb +1 -0
  11. data/lib/mondrian/jars/commons-collections-3.1.jar +0 -0
  12. data/lib/mondrian/jars/commons-dbcp-1.2.1.jar +0 -0
  13. data/lib/mondrian/jars/commons-logging-1.0.4.jar +0 -0
  14. data/lib/mondrian/jars/commons-math-1.0.jar +0 -0
  15. data/lib/mondrian/jars/commons-pool-1.2.jar +0 -0
  16. data/lib/mondrian/jars/commons-vfs-1.0.jar +0 -0
  17. data/lib/mondrian/jars/eigenbase-properties.jar +0 -0
  18. data/lib/mondrian/jars/eigenbase-resgen.jar +0 -0
  19. data/lib/mondrian/jars/eigenbase-xom.jar +0 -0
  20. data/lib/mondrian/jars/javacup.jar +0 -0
  21. data/lib/mondrian/jars/log4j-1.2.8.jar +0 -0
  22. data/lib/mondrian/jars/log4j.properties +5 -0
  23. data/lib/mondrian/jars/mondrian.jar +0 -0
  24. data/lib/mondrian/jars/olap4j.jar +0 -0
  25. data/lib/mondrian/olap.rb +17 -0
  26. data/lib/mondrian/olap/connection.rb +201 -0
  27. data/lib/mondrian/olap/cube.rb +297 -0
  28. data/lib/mondrian/olap/error.rb +57 -0
  29. data/lib/mondrian/olap/query.rb +342 -0
  30. data/lib/mondrian/olap/result.rb +264 -0
  31. data/lib/mondrian/olap/schema.rb +378 -0
  32. data/lib/mondrian/olap/schema_element.rb +153 -0
  33. data/lib/mondrian/olap/schema_udf.rb +282 -0
  34. data/mondrian-olap.gemspec +128 -0
  35. data/spec/connection_role_spec.rb +130 -0
  36. data/spec/connection_spec.rb +72 -0
  37. data/spec/cube_spec.rb +318 -0
  38. data/spec/fixtures/MondrianTest.xml +134 -0
  39. data/spec/fixtures/MondrianTestOracle.xml +134 -0
  40. data/spec/mondrian_spec.rb +53 -0
  41. data/spec/query_spec.rb +807 -0
  42. data/spec/rake_tasks.rb +260 -0
  43. data/spec/schema_definition_spec.rb +1249 -0
  44. data/spec/spec_helper.rb +134 -0
  45. data/spec/support/matchers/be_like.rb +24 -0
  46. metadata +278 -0
@@ -0,0 +1,72 @@
1
+ require "spec_helper"
2
+
3
+ describe "Connection" do
4
+
5
+ describe "create" do
6
+ before(:each) do
7
+ @olap = Mondrian::OLAP::Connection.new(CONNECTION_PARAMS_WITH_CATALOG)
8
+ end
9
+
10
+ it "should not be connected before connection" do
11
+ @olap.should_not be_connected
12
+ end
13
+
14
+ it "should be successful" do
15
+ @olap.connect.should be_true
16
+ end
17
+
18
+ end
19
+
20
+ describe "create with catalog content" do
21
+ before(:all) do
22
+ @schema_xml = File.read(CATALOG_FILE)
23
+ end
24
+ it "should be successful" do
25
+ @olap = Mondrian::OLAP::Connection.new(CONNECTION_PARAMS.merge(
26
+ :catalog_content => @schema_xml
27
+ ))
28
+ @olap.connect.should be_true
29
+ end
30
+
31
+ end
32
+
33
+ describe "properties" do
34
+ before(:all) do
35
+ @olap = Mondrian::OLAP::Connection.create(CONNECTION_PARAMS_WITH_CATALOG)
36
+ end
37
+
38
+ it "should be connected" do
39
+ @olap.should be_connected
40
+ end
41
+
42
+ # to check that correct database dialect is loaded by ServiceDiscovery detected class loader
43
+ it "should use corresponding Mondrian dialect" do
44
+ # read private "schema" field
45
+ schema_field = @olap.raw_schema.getClass.getDeclaredField("schema")
46
+ schema_field.setAccessible(true)
47
+ private_schema = schema_field.get(@olap.raw_schema)
48
+ private_schema.getDialect.java_class.name.should == case MONDRIAN_DRIVER
49
+ when 'mysql' then 'mondrian.spi.impl.MySqlDialect'
50
+ when 'postgresql' then 'mondrian.spi.impl.PostgreSqlDialect'
51
+ when 'oracle' then 'mondrian.spi.impl.OracleDialect'
52
+ when 'luciddb' then 'mondrian.spi.impl.LucidDbDialect'
53
+ when 'mssql' then 'mondrian.spi.impl.MicrosoftSqlServerDialect'
54
+ when 'sqlserver' then 'mondrian.spi.impl.MicrosoftSqlServerDialect'
55
+ end
56
+ end
57
+
58
+ end
59
+
60
+ describe "close" do
61
+ before(:all) do
62
+ @olap = Mondrian::OLAP::Connection.create(CONNECTION_PARAMS_WITH_CATALOG)
63
+ end
64
+
65
+ it "should not be connected after close" do
66
+ @olap.close
67
+ @olap.should_not be_connected
68
+ end
69
+
70
+ end
71
+
72
+ end
@@ -0,0 +1,318 @@
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
+ description 'Sales description'
8
+ table 'sales'
9
+ dimension 'Gender', :foreign_key => 'customer_id' do
10
+ description 'Gender description'
11
+ hierarchy :has_all => true, :primary_key => 'id' do
12
+ description 'Gender hierarchy description'
13
+ table 'customers'
14
+ level 'Gender', :column => 'gender', :unique_members => true, :description => 'Gender level description'
15
+ end
16
+ end
17
+ dimension 'Customers', :foreign_key => 'customer_id' do
18
+ hierarchy :has_all => true, :all_member_name => 'All Customers', :primary_key => 'id' do
19
+ table 'customers'
20
+ level 'Country', :column => 'country', :unique_members => true
21
+ level 'State Province', :column => 'state_province', :unique_members => true
22
+ level 'City', :column => 'city', :unique_members => false
23
+ level 'Name', :column => 'fullname', :unique_members => true
24
+ end
25
+ end
26
+ calculated_member 'Non-USA' do
27
+ dimension 'Customers'
28
+ formula '[Customers].[All Customers] - [Customers].[USA]'
29
+ end
30
+ dimension 'Time', :foreign_key => 'time_id', :type => 'TimeDimension' do
31
+ hierarchy :has_all => false, :primary_key => 'id' do
32
+ table 'time'
33
+ level 'Year', :column => 'the_year', :type => 'Numeric', :unique_members => true, :level_type => 'TimeYears'
34
+ level 'Quarter', :column => 'quarter', :unique_members => false, :level_type => 'TimeQuarters'
35
+ level 'Month', :column => 'month_of_year', :type => 'Numeric', :unique_members => false, :level_type => 'TimeMonths'
36
+ end
37
+ hierarchy 'Weekly', :has_all => false, :primary_key => 'id' do
38
+ table 'time'
39
+ level 'Year', :column => 'the_year', :type => 'Numeric', :unique_members => true, :level_type => 'TimeYears'
40
+ level 'Week', :column => 'weak_of_year', :type => 'Numeric', :unique_members => false, :level_type => 'TimeWeeks'
41
+ end
42
+ end
43
+ measure 'Unit Sales', :column => 'unit_sales', :aggregator => 'sum'
44
+ measure 'Store Sales', :column => 'store_sales', :aggregator => 'sum'
45
+ measure 'Store Cost', :column => 'store_cost', :aggregator => 'sum', :visible => false
46
+ end
47
+ end
48
+ @olap = Mondrian::OLAP::Connection.create(CONNECTION_PARAMS.merge :schema => @schema)
49
+ end
50
+
51
+ it "should get all cube names" do
52
+ @olap.cube_names.should == ['Sales']
53
+ end
54
+
55
+ it "should get cube by name" do
56
+ @olap.cube('Sales').should be_a(Mondrian::OLAP::Cube)
57
+ end
58
+
59
+ it "should return nil when getting cube with invalid name" do
60
+ @olap.cube('invalid').should be_nil
61
+ end
62
+
63
+ it "should get cube name" do
64
+ @olap.cube('Sales').name.should == 'Sales'
65
+ end
66
+
67
+ it "should get cube description" do
68
+ @olap.cube('Sales').description.should == 'Sales description'
69
+ end
70
+
71
+ describe "dimensions" do
72
+ before(:all) do
73
+ @cube = @olap.cube('Sales')
74
+ @dimension_names = ['Measures', 'Gender', 'Customers', 'Time']
75
+ end
76
+
77
+ it "should get dimension names" do
78
+ @cube.dimension_names.should == @dimension_names
79
+ end
80
+
81
+ it "should get dimensions" do
82
+ @cube.dimensions.map{|d| d.name}.should == @dimension_names
83
+ end
84
+
85
+ it "should get dimension by name" do
86
+ @cube.dimension('Gender').name.should == 'Gender'
87
+ end
88
+
89
+ it "should return nil when getting dimension with invalid name" do
90
+ @cube.dimension('invalid').should be_nil
91
+ end
92
+
93
+ it "should get dimension description" do
94
+ @cube.dimension('Gender').description.should == 'Gender description'
95
+ end
96
+
97
+ it "should get dimension full name" do
98
+ @cube.dimension('Gender').full_name.should == '[Gender]'
99
+ end
100
+
101
+ it "should get measures dimension" do
102
+ @cube.dimension('Measures').should be_measures
103
+ end
104
+
105
+ it "should get dimension type" do
106
+ @cube.dimension('Gender').dimension_type.should == :standard
107
+ @cube.dimension('Time').dimension_type.should == :time
108
+ @cube.dimension('Measures').dimension_type.should == :measures
109
+ end
110
+ end
111
+
112
+ describe "dimension hierarchies" do
113
+ before(:all) do
114
+ @cube = @olap.cube('Sales')
115
+ end
116
+
117
+ it "should get hierarchies" do
118
+ hierarchies = @cube.dimension('Gender').hierarchies
119
+ hierarchies.size.should == 1
120
+ hierarchies[0].name.should == 'Gender'
121
+ end
122
+
123
+ it "should get hierarchy description" do
124
+ hierarchies = @cube.dimension('Gender').hierarchies.first.description.should == 'Gender hierarchy description'
125
+ end
126
+
127
+ it "should get hierarchy names" do
128
+ @cube.dimension('Time').hierarchy_names.should == ['Time', 'Time.Weekly']
129
+ end
130
+
131
+ it "should get hierarchy by name" do
132
+ @cube.dimension('Time').hierarchy('Time.Weekly').name.should == 'Time.Weekly'
133
+ end
134
+
135
+ it "should return nil when getting hierarchy with invalid name" do
136
+ @cube.dimension('Time').hierarchy('invalid').should be_nil
137
+ end
138
+
139
+ it "should get default hierarchy" do
140
+ @cube.dimension('Time').hierarchy.name.should == 'Time'
141
+ end
142
+
143
+ it "should get hierarchy levels" do
144
+ @cube.dimension('Customers').hierarchy.levels.map(&:name).should == ['(All)', 'Country', 'State Province', 'City', 'Name']
145
+ end
146
+
147
+ it "should get hierarchy level names" do
148
+ @cube.dimension('Time').hierarchy.level_names.should == ['Year', 'Quarter', 'Month']
149
+ @cube.dimension('Customers').hierarchy.level_names.should == ['(All)', 'Country', 'State Province', 'City', 'Name']
150
+ end
151
+
152
+ it "should get hierarchy level depths" do
153
+ @cube.dimension('Customers').hierarchy.levels.map(&:depth).should == [0, 1, 2, 3, 4]
154
+ end
155
+
156
+ it "should get hierarchy level members count" do
157
+ @cube.dimension('Gender').hierarchy.levels.map(&:members_count).should == [1, 2]
158
+ end
159
+ end
160
+
161
+ describe "hierarchy values" do
162
+ before(:all) do
163
+ @cube = @olap.cube('Sales')
164
+ end
165
+
166
+ it "should get hierarchy all member" do
167
+ @cube.dimension('Gender').hierarchy.has_all?.should be_true
168
+ @cube.dimension('Gender').hierarchy.all_member_name.should == 'All Genders'
169
+ end
170
+
171
+ it "should not get all member for hierarchy without all member" do
172
+ @cube.dimension('Time').hierarchy.has_all?.should be_false
173
+ @cube.dimension('Time').hierarchy.all_member_name.should be_nil
174
+ end
175
+
176
+ it "should get hierarchy root members" do
177
+ @cube.dimension('Gender').hierarchy.root_members.map(&:name).should == ['All Genders']
178
+ @cube.dimension('Gender').hierarchy.root_member_names.should == ['All Genders']
179
+ @cube.dimension('Time').hierarchy.root_members.map(&:name).should == ['2010', '2011']
180
+ @cube.dimension('Time').hierarchy.root_member_names.should == ['2010', '2011']
181
+ end
182
+
183
+ it "should return child members for specified member" do
184
+ @cube.dimension('Gender').hierarchy.child_names('All Genders').should == ['F', 'M']
185
+ @cube.dimension('Customers').hierarchy.child_names('USA', 'OR').should ==
186
+ ["Albany", "Beaverton", "Corvallis", "Lake Oswego", "Lebanon", "Milwaukie",
187
+ "Oregon City", "Portland", "Salem", "W. Linn", "Woodburn"]
188
+ end
189
+
190
+ it "should return child members for hierarchy" do
191
+ @cube.dimension('Gender').hierarchy.child_names.should == ['F', 'M']
192
+ end
193
+
194
+ it "should not return child members for leaf member" do
195
+ @cube.dimension('Gender').hierarchy.child_names('All Genders', 'F').should == []
196
+ end
197
+
198
+ it "should return nil as child members if parent member not found" do
199
+ @cube.dimension('Gender').hierarchy.child_names('N').should be_nil
200
+ end
201
+
202
+ end
203
+
204
+ describe "level members" do
205
+ before(:all) do
206
+ @cube = @olap.cube('Sales')
207
+ end
208
+
209
+ it "should get level description" do
210
+ @cube.dimension('Gender').hierarchy.level('Gender').description.should == 'Gender level description'
211
+ end
212
+
213
+ it "should return nil when getting level with invalid name" do
214
+ @cube.dimension('Gender').hierarchy.level('invalid').should be_nil
215
+ end
216
+
217
+ it "should get primary hierarchy level members" do
218
+ @cube.dimension('Customers').hierarchy.level('Country').members.
219
+ map(&:name).should == ['Canada', 'Mexico', 'USA']
220
+ end
221
+
222
+ it "should get secondary hierarchy level members" do
223
+ @cube.dimension('Time').hierarchy('Time.Weekly').level('Year').members.
224
+ map(&:name).should == ['2010', '2011']
225
+ end
226
+ end
227
+
228
+ describe "members" do
229
+ before(:all) do
230
+ @cube = @olap.cube('Sales')
231
+ end
232
+
233
+ it "should return member for specified full name" do
234
+ @cube.member('[Gender].[All Genders]').name.should == 'All Genders'
235
+ @cube.member('[Customers].[USA].[OR]').name.should == 'OR'
236
+ end
237
+
238
+ it "should not return member for invalid full name" do
239
+ @cube.member('[Gender].[invalid]').should be_nil
240
+ end
241
+
242
+ it "should return child members for member" do
243
+ @cube.member('[Gender].[All Genders]').children.map(&:name).should == ['F', 'M']
244
+ @cube.member('[Customers].[USA].[OR]').children.map(&:name).should ==
245
+ ["Albany", "Beaverton", "Corvallis", "Lake Oswego", "Lebanon", "Milwaukie",
246
+ "Oregon City", "Portland", "Salem", "W. Linn", "Woodburn"]
247
+ end
248
+
249
+ it "should return empty children array if member does not have children" do
250
+ @cube.member('[Gender].[All Genders].[F]').children.should be_empty
251
+ end
252
+
253
+ it "should return member depth" do
254
+ @cube.member('[Customers].[All Customers]').depth.should == 0
255
+ @cube.member('[Customers].[USA]').depth.should == 1
256
+ @cube.member('[Customers].[USA].[CA]').depth.should == 2
257
+ end
258
+
259
+ it "should return descendants for member at specified level" do
260
+ @cube.member('[Customers].[Mexico]').descendants_at_level('City').map(&:name).should ==
261
+ ["San Andres", "Santa Anita", "Santa Fe", "Tixapan", "Acapulco", "Guadalajara",
262
+ "Mexico City", "Tlaxiaco", "La Cruz", "Orizaba", "Merida", "Camacho", "Hidalgo"]
263
+ end
264
+
265
+ it "should not return descendants for member when upper level specified" do
266
+ @cube.member('[Customers].[Mexico].[DF]').descendants_at_level('Country').should be_nil
267
+ end
268
+
269
+ it "should be drillable when member has descendants" do
270
+ @cube.member('[Customers].[USA]').should be_drillable
271
+ end
272
+
273
+ it "should not be drillable when member has no descendants" do
274
+ @cube.member('[Gender].[F]').should_not be_drillable
275
+ end
276
+
277
+ it "should not be drillable when member is calculated" do
278
+ @cube.member('[Customers].[Non-USA]').should_not be_drillable
279
+ end
280
+
281
+ it "should be calculated when member is calculated" do
282
+ @cube.member('[Customers].[Non-USA]').should be_calculated
283
+ end
284
+
285
+ it "should not be calculated when normal member" do
286
+ @cube.member('[Customers].[USA]').should_not be_calculated
287
+ end
288
+
289
+ it "should be all member when member is all member" do
290
+ @cube.member('[Customers].[All Customers]').should be_all_member
291
+ end
292
+
293
+ it "should not be all member when member is not all member" do
294
+ @cube.member('[Customers].[USA]').should_not be_all_member
295
+ end
296
+
297
+ it "should get dimension type of standard dimension member" do
298
+ @cube.member('[Customers].[USA]').dimension_type.should == :standard
299
+ end
300
+
301
+ it "should get dimension type of measure" do
302
+ @cube.member('[Measures].[Unit Sales]').dimension_type.should == :measures
303
+ end
304
+
305
+ it "should get dimension type of time dimension member" do
306
+ @cube.member('[Time].[2011]').dimension_type.should == :time
307
+ end
308
+
309
+ it "should be visble when member is visible" do
310
+ @cube.member('[Measures].[Store Sales]').should be_visible
311
+ end
312
+
313
+ it "should not be visble when member is not visible" do
314
+ @cube.member('[Measures].[Store Cost]').should_not be_visible
315
+ end
316
+ end
317
+
318
+ end
@@ -0,0 +1,134 @@
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="postgresql">
60
+ "fname" || ' ' || "lname"
61
+ </SQL>
62
+ <SQL dialect="mysql">
63
+ CONCAT(`customers`.`fname`, ' ', `customers`.`lname`)
64
+ </SQL>
65
+ <SQL dialect="luciddb">
66
+ "fname" || ' ' || "lname"
67
+ </SQL>
68
+ <SQL dialect="generic">
69
+ fullname
70
+ </SQL>
71
+ </NameExpression>
72
+ <OrdinalExpression>
73
+ <SQL dialect="oracle">
74
+ fname || ' ' || lname
75
+ </SQL>
76
+ <SQL dialect="postgresql">
77
+ "fname" || ' ' || "lname"
78
+ </SQL>
79
+ <SQL dialect="mysql">
80
+ CONCAT(`customers`.`fname`, ' ', `customers`.`lname`)
81
+ </SQL>
82
+ <SQL dialect="luciddb">
83
+ "fname" || ' ' || "lname"
84
+ </SQL>
85
+ <SQL dialect="generic">
86
+ fullname
87
+ </SQL>
88
+ </OrdinalExpression>
89
+ <Property name="Gender" column="gender"/>
90
+ </Level>
91
+ </Hierarchy>
92
+ </Dimension>
93
+ <Dimension name="Gender" foreignKey="customer_id">
94
+ <Hierarchy hasAll="true" allMemberName="All Gender" primaryKey="id">
95
+ <Table name="customers"/>
96
+ <Level name="Gender" column="gender" uniqueMembers="true"/>
97
+ </Hierarchy>
98
+ </Dimension>
99
+
100
+ <Measure name="Unit Sales" column="unit_sales" aggregator="sum"
101
+ formatString="Standard"/>
102
+ <Measure name="Store Cost" column="store_cost" aggregator="sum"
103
+ formatString="#,###.00"/>
104
+ <Measure name="Store Sales" column="store_sales" aggregator="sum"
105
+ formatString="#,###.00"/>
106
+ <Measure name="Sales Count" column="product_id" aggregator="count"
107
+ formatString="#,###"/>
108
+ <Measure name="Customer Count" column="customer_id"
109
+ aggregator="distinct-count" formatString="#,###"/>
110
+ <CalculatedMember
111
+ name="Profit"
112
+ dimension="Measures">
113
+ <Formula>[Measures].[Store Sales] - [Measures].[Store Cost]</Formula>
114
+ <CalculatedMemberProperty name="FORMAT_STRING" value="$#,##0.00"/>
115
+ </CalculatedMember>
116
+ <CalculatedMember
117
+ name="Profit last Period"
118
+ dimension="Measures"
119
+ formula="COALESCEEMPTY((Measures.[Profit], [Time].[Time].PREVMEMBER), Measures.[Profit])"
120
+ visible="false">
121
+ <CalculatedMemberProperty name="FORMAT_STRING" value="$#,##0.00"/>
122
+ <CalculatedMemberProperty name="MEMBER_ORDINAL" value="18"/>
123
+ </CalculatedMember>
124
+ <CalculatedMember
125
+ name="Profit Growth"
126
+ dimension="Measures"
127
+ formula="([Measures].[Profit] - [Measures].[Profit last Period]) / [Measures].[Profit last Period]"
128
+ visible="true"
129
+ caption="Gewinn-Wachstum">
130
+ <CalculatedMemberProperty name="FORMAT_STRING" value="0.0%"/>
131
+ </CalculatedMember>
132
+ </Cube>
133
+
134
+ </Schema>