olap4r 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,37 @@
1
+ module Olap
2
+ class RowSet
3
+ def initialize rowset
4
+ @rowset = rowset
5
+ end
6
+
7
+ # Returns list of columns
8
+ #
9
+ def columns
10
+ @columns ||= 1.upto(@rowset.get_meta_data.get_column_count).map do |i|
11
+ {
12
+ :id => @rowset.get_meta_data.getColumnName(i),
13
+ :name => @rowset.get_meta_data.getColumnLabel(i)
14
+ }
15
+ end
16
+ end
17
+
18
+ # Returns query values
19
+ #
20
+ # ==== Attributes
21
+ #
22
+ # * +value_type+ - Returned value type (:value or :formatted_value)
23
+ #
24
+ def values value_type = :formatted_value
25
+ return @values unless @values.nil?
26
+
27
+ @values = []
28
+ while @rowset.next do
29
+ @values << 1.upto(self.columns.size).map do |i|
30
+ @rowset.getString i
31
+ end
32
+ end
33
+
34
+ @values
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,75 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "olap4r"
8
+ s.version = "0.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Filip Tepper"]
12
+ s.date = "2012-08-20"
13
+ s.description = "olap4j wrapper for JRuby"
14
+ s.email = "filip@tepper.pl"
15
+ s.extra_rdoc_files = [
16
+ "README.md"
17
+ ]
18
+ s.files = [
19
+ ".document",
20
+ ".rspec",
21
+ "Gemfile",
22
+ "Gemfile.lock",
23
+ "README.md",
24
+ "Rakefile",
25
+ "VERSION",
26
+ "lib/olap4j.jar",
27
+ "lib/olap4r.rb",
28
+ "lib/olap4r/cellset.rb",
29
+ "lib/olap4r/connection.rb",
30
+ "lib/olap4r/query_builder.rb",
31
+ "lib/olap4r/rowset.rb",
32
+ "olap4r.gemspec",
33
+ "spec/config.yml.example",
34
+ "spec/connection_spec.rb",
35
+ "spec/fixtures/FoodMart.xml",
36
+ "spec/query_builder_spec.rb",
37
+ "spec/spec_helper.rb"
38
+ ]
39
+ s.homepage = "http://github.com/Freeport-Metrics/olap4r"
40
+ s.licenses = ["MIT"]
41
+ s.require_paths = ["lib"]
42
+ s.rubygems_version = "1.8.24"
43
+ s.summary = "olap4j wrapper for JRuby"
44
+
45
+ if s.respond_to? :specification_version then
46
+ s.specification_version = 3
47
+
48
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
49
+ s.add_development_dependency(%q<jruby-openssl>, [">= 0"])
50
+ s.add_development_dependency(%q<rspec>, ["~> 2.11.0"])
51
+ s.add_development_dependency(%q<yard>, ["~> 0.8.2.1"])
52
+ s.add_development_dependency(%q<bundler>, ["~> 1.1.0"])
53
+ s.add_development_dependency(%q<jeweler>, ["~> 1.8.4"])
54
+ s.add_development_dependency(%q<olap4r-mondrian>, ["~> 0.1.0"])
55
+ s.add_development_dependency(%q<olap4r-xmla>, ["~> 0.1.0"])
56
+ else
57
+ s.add_dependency(%q<jruby-openssl>, [">= 0"])
58
+ s.add_dependency(%q<rspec>, ["~> 2.11.0"])
59
+ s.add_dependency(%q<yard>, ["~> 0.8.2.1"])
60
+ s.add_dependency(%q<bundler>, ["~> 1.1.0"])
61
+ s.add_dependency(%q<jeweler>, ["~> 1.8.4"])
62
+ s.add_dependency(%q<olap4r-mondrian>, ["~> 0.1.0"])
63
+ s.add_dependency(%q<olap4r-xmla>, ["~> 0.1.0"])
64
+ end
65
+ else
66
+ s.add_dependency(%q<jruby-openssl>, [">= 0"])
67
+ s.add_dependency(%q<rspec>, ["~> 2.11.0"])
68
+ s.add_dependency(%q<yard>, ["~> 0.8.2.1"])
69
+ s.add_dependency(%q<bundler>, ["~> 1.1.0"])
70
+ s.add_dependency(%q<jeweler>, ["~> 1.8.4"])
71
+ s.add_dependency(%q<olap4r-mondrian>, ["~> 0.1.0"])
72
+ s.add_dependency(%q<olap4r-xmla>, ["~> 0.1.0"])
73
+ end
74
+ end
75
+
@@ -0,0 +1,6 @@
1
+ mondrian:
2
+ jdbc_driver_path: "/usr/local/Cellar/tomcat/7.0.6//libexec/common/endorsed/mysql-connector-java-5.1.15-bin"
3
+ connection_string: "jdbc:mondrian:JdbcDrivers=com.mysql.jdbc.Driver;Jdbc=jdbc:mysql://localhost/mondrian_foodmart?user=root;Catalog=file:spec/fixtures/FoodMart.xml;"
4
+
5
+ xmla:
6
+ connection_string: "jdbc:xmla:Server=http://127.0.0.1:8080/mondrian/xmla;Catalog=FoodMart;"
@@ -0,0 +1,232 @@
1
+ require "spec_helper"
2
+
3
+ shared_examples_for "an Olap::Connection driver" do
4
+ context "given an instance" do
5
+ context "#cubes" do
6
+ it "returns an array of cubes" do
7
+ connection.cubes.should =~ [
8
+ { :unique_name => "[Sales Ragged]", :name => "Sales Ragged" },
9
+ { :unique_name => "[Warehouse]", :name => "Warehouse" },
10
+ { :unique_name => "[HR]", :name => "HR" },
11
+ { :unique_name => "[Warehouse and Sales]", :name => "Warehouse and Sales" },
12
+ { :unique_name => "[Sales 2]", :name => "Sales 2" },
13
+ { :unique_name => "[Store]", :name => "Store" },
14
+ { :unique_name => "[Sales]", :name => "Sales" }
15
+ ]
16
+ end
17
+ end
18
+
19
+ context "#measures" do
20
+ it "returns an array of measures" do
21
+ connection.measures("[HR]").should =~ [
22
+ { :unique_name => "[Measures].[Org Salary]", :name => "Org Salary" },
23
+ { :unique_name => "[Measures].[Count]", :name => "Count" },
24
+ { :unique_name => "[Measures].[Number of Employees]", :name => "Number of Employees" },
25
+ { :unique_name => "[Measures].[Employee Salary]", :name => "Employee Salary" },
26
+ { :unique_name => "[Measures].[Avg Salary]", :name => "Avg Salary" }
27
+ ]
28
+ end
29
+ end
30
+
31
+ context "#dimensions" do
32
+ it "returns an array of dimensions" do
33
+ connection.dimensions("[Sales 2]").length.should == 4
34
+ end
35
+
36
+ it "returns dimensions with type" do
37
+ dimensions = connection.dimensions "[Sales 2]"
38
+ dimensions[0][:type].should == :measure
39
+ dimensions[1][:type].should == :time
40
+ dimensions[2][:type].should == :other
41
+ dimensions[3][:type].should == :other
42
+ end
43
+ end
44
+
45
+ context "#children_lookup" do
46
+ it "returns an array of dimensions for null member" do
47
+ connection.children_lookup("[Sales Ragged]").should =~ [
48
+ { :unique_name => "[Measures]", :name => "Measures", :children => true, :type => :measure },
49
+ { :unique_name => "[Store]", :name => "Store", :children => true, :type => :other },
50
+ { :unique_name => "[Geography]", :name => "Geography", :children => true, :type => :other },
51
+ { :unique_name => "[Store Size in SQFT]", :name => "Store Size in SQFT", :children => true, :type => :other },
52
+ { :unique_name => "[Store Type]", :name => "Store Type", :children => true, :type => :other },
53
+ { :unique_name => "[Time]", :name => "Time", :children => true, :type => :time },
54
+ { :unique_name => "[Product]", :name => "Product", :children => true, :type => :other },
55
+ { :unique_name => "[Promotion Media]", :name => "Promotion Media", :children => true, :type => :other },
56
+ { :unique_name => "[Promotions]", :name => "Promotions", :children => true, :type => :other },
57
+ { :unique_name => "[Customers]", :name => "Customers", :children => true, :type => :other },
58
+ { :unique_name => "[Education Level]", :name => "Education Level", :children => true, :type => :other },
59
+ { :unique_name => "[Gender]", :name => "Gender", :children => true, :type => :other },
60
+ { :unique_name => "[Marital Status]", :name => "Marital Status", :children => true, :type => :other },
61
+ { :unique_name => "[Yearly Income]", :name => "Yearly Income", :children => true, :type => :other }
62
+ ]
63
+ end
64
+
65
+ it "returns an array of dimensions for a given first member in hierarchy" do
66
+ connection.children_lookup("[Sales Ragged]", "[Store]").should =~ [
67
+ { :unique_name => "[Store].[All Stores]", :name => "All Stores", :children => true }
68
+ ]
69
+ end
70
+
71
+ it "returns an array of children for a given member in hierarchy" do
72
+ connection.children_lookup("[Sales Ragged]", "[Store].[All Stores].[USA].[CA]").should =~ [
73
+ { :unique_name => "[Store].[USA].[CA].[Alameda]", :name => "Alameda", :children => true },
74
+ { :unique_name => "[Store].[USA].[CA].[Beverly Hills]", :name => "Beverly Hills", :children => true },
75
+ { :unique_name => "[Store].[USA].[CA].[Los Angeles]", :name => "Los Angeles", :children => true },
76
+ { :unique_name => "[Store].[USA].[CA].[San Francisco]", :name => "San Francisco", :children => true }
77
+ ]
78
+ end
79
+
80
+ it "returns an array of children for a given measures member in hierarchy" do
81
+ connection.children_lookup("[Sales Ragged]", "[Measures]").should =~ [
82
+ { :unique_name => "[Measures].[Unit Sales]", :name => "Unit Sales", :children => false },
83
+ { :unique_name => "[Measures].[Store Cost]", :name => "Store Cost", :children => false },
84
+ { :unique_name => "[Measures].[Store Sales]", :name => "Store Sales", :children => false },
85
+ { :unique_name => "[Measures].[Sales Count]", :name => "Sales Count", :children => false },
86
+ { :unique_name => "[Measures].[Customer Count]", :name => "Customer Count", :children => false }
87
+ ]
88
+ end
89
+
90
+ it "recursively returns an array of children for a given dimension" do
91
+ dimension = connection.children_lookup "[Sales Ragged]", "[Time]", true
92
+ dimension[0][:children].should be_an(Array)
93
+ dimension[0][:children][0][:children].should be_an(Array)
94
+ dimension[0][:children][0][:children][0][:children].should be_an(Array)
95
+ end
96
+ end
97
+
98
+ context "#execute" do
99
+ let(:cellset) { connection.execute "SELECT [Measures].[Unit Sales] ON COLUMNS, [Store] ON ROWS FROM [Sales]" }
100
+
101
+ it "returns Olap::CellSet instance for successful queries" do
102
+ cellset.should be_a(Olap::CellSet)
103
+ end
104
+
105
+ it "returns cellset axes" do
106
+ cellset.axes.should =~ [
107
+ { :axis => :columns, :values => [
108
+ [{ :name => "Unit Sales", :unique_name => "[Measures].[Unit Sales]", :drillable => false }]
109
+ ] },
110
+ { :axis => :rows, :values => [
111
+ [{ :name => "All Stores", :unique_name => "[Store].[All Stores]", :drillable => true }]
112
+ ] }
113
+ ]
114
+ end
115
+
116
+ it "returns cellset formatted values" do
117
+ cellset.values.should =~ [["266,773"]]
118
+ end
119
+
120
+ it "returns cellset raw values" do
121
+ cellset.values(:value).should =~ [[266773.0]]
122
+ end
123
+ end
124
+
125
+ context "#execute drill-down query" do
126
+ let(:cellset) { connection.execute %{
127
+ SELECT {[Measures].[Unit Sales], [Measures].[Store Cost], [Measures].[Store Sales]} ON COLUMNS,
128
+ HIERARCHIZE(UNION(CROSSJOIN({[Promotion Media].[All Media]}, {[Product].[All Products]}), CROSSJOIN({[Promotion Media].[All Media]}, [Product].[All Products].Children))) ON ROWS
129
+ FROM [Sales]
130
+ WHERE [Time].[1997]
131
+ } }
132
+
133
+ it "returns cellset axes" do
134
+ cellset.axes.should =~ [
135
+ {
136
+ :axis => :columns,
137
+ :values => [
138
+ [{ :name => "Unit Sales", :unique_name => "[Measures].[Unit Sales]", :drillable => false }],
139
+ [{ :name => "Store Cost", :unique_name => "[Measures].[Store Cost]", :drillable => false }],
140
+ [{ :name => "Store Sales", :unique_name => "[Measures].[Store Sales]", :drillable => false}
141
+ ]]
142
+ },
143
+ {
144
+ :axis => :rows, :values => [
145
+ [
146
+ { :name => "All Media", :unique_name => "[Promotion Media].[All Media]", :drillable => true},
147
+ { :name => "All Products", :unique_name => "[Product].[All Products]", :drillable => true }
148
+ ], [
149
+ { :name => "All Media", :unique_name => "[Promotion Media].[All Media]", :drillable => true },
150
+ { :name => "Drink", :unique_name => "[Product].[Drink]", :drillable => true }
151
+ ], [
152
+ { :name => "All Media", :unique_name => "[Promotion Media].[All Media]", :drillable => true },
153
+ { :name => "Food", :unique_name => "[Product].[Food]", :drillable => true}
154
+ ], [
155
+ { :name => "All Media", :unique_name => "[Promotion Media].[All Media]", :drillable => true },
156
+ { :name => "Non-Consumable", :unique_name => "[Product].[Non-Consumable]", :drillable => true }
157
+ ]
158
+ ]
159
+ }
160
+ ]
161
+ end
162
+ end
163
+ end
164
+ end
165
+
166
+ shared_examples_for "an Olap::Connection driver with drillthrough capabilities" do
167
+ context "given an instance" do
168
+ context "#drillthrough" do
169
+ let(:rowset) { connection.drillthrough "DRILLTHROUGH SELECT [Measures].[Unit Sales] ON COLUMNS, [Store] ON ROWS FROM [Sales]" }
170
+
171
+ it "returns Olap::RowSet instance for successful queries" do
172
+ rowset.should be_a(Olap::RowSet)
173
+ end
174
+
175
+ it "returns rawset axes" do
176
+ rowset.columns.should include(
177
+ { :id => "the_year", :name => "Year" },
178
+ { :id => "unit_sales", :name => "Unit Sales" }
179
+ )
180
+ end
181
+
182
+ it "returns rawset values" do
183
+ rowset.values.length.should == 86837
184
+ end
185
+ end
186
+ end
187
+ end
188
+
189
+ describe Olap::Connection, "for invalid connection string" do
190
+ context "#initialize" do
191
+ it "raises an Exception for 'jdbc' connection string" do
192
+ lambda {
193
+ connection = Olap::Connection.new "jdbc"
194
+ }.should raise_error(Olap::InvalidConnectionStringException)
195
+ end
196
+
197
+ it "raised an Exception for 'jdbc:invalid:foo' connection string" do
198
+ lambda {
199
+ connection = Olap::Connection.new "jdbc:invalid:foo"
200
+ }.should raise_error(Olap::InvalidOlapDriverException)
201
+ end
202
+ end
203
+ end
204
+
205
+ describe Olap::Connection, "for Mondrian driver" do
206
+ context "#initialize" do
207
+ it "returns Olap::Connection instance for successful connection" do
208
+ connection = Olap::Connection.new RSPEC_CONFIG["mondrian"]["connection_string"]
209
+ connection.should be_a(Olap::Connection)
210
+ end
211
+ end
212
+
213
+ context "given an instance" do
214
+ let(:connection) { Olap::Connection.new RSPEC_CONFIG["mondrian"]["connection_string"] }
215
+ it_should_behave_like "an Olap::Connection driver"
216
+ it_should_behave_like "an Olap::Connection driver with drillthrough capabilities"
217
+ end
218
+ end
219
+
220
+ describe Olap::Connection, "for XML/A driver" do
221
+ context "#initialize" do
222
+ it "returns Olap::Connection instance for successful connection" do
223
+ connection = Olap::Connection.new RSPEC_CONFIG["xmla"]["connection_string"]
224
+ connection.should be_a(Olap::Connection)
225
+ end
226
+ end
227
+
228
+ context "given an instance" do
229
+ let(:connection) { Olap::Connection.new RSPEC_CONFIG["xmla"]["connection_string"] }
230
+ it_should_behave_like "an Olap::Connection driver"
231
+ end
232
+ end
@@ -0,0 +1,802 @@
1
+ <?xml version="1.0"?>
2
+ <Schema name="FoodMart">
3
+ <!--
4
+ == $Id: //open/mondrian-release/3.2/demo/FoodMart.xml#2 $
5
+ == This software is subject to the terms of the Eclipse Public License v1.0
6
+ == Agreement, available at the following URL:
7
+ == http://www.eclipse.org/legal/epl-v10.html.
8
+ == Copyright (C) 2000-2002 Kana Software, Inc.
9
+ == Copyright (C) 2002-2009 Julian Hyde and others
10
+ == All Rights Reserved.
11
+ == You must accept the terms of that agreement to use this software.
12
+ -->
13
+
14
+ <!-- Shared dimensions -->
15
+
16
+ <Dimension name="Store">
17
+ <Hierarchy hasAll="true" primaryKey="store_id">
18
+ <Table name="store"/>
19
+ <Level name="Store Country" column="store_country" uniqueMembers="true"/>
20
+ <Level name="Store State" column="store_state" uniqueMembers="true"/>
21
+ <Level name="Store City" column="store_city" uniqueMembers="false"/>
22
+ <Level name="Store Name" column="store_name" uniqueMembers="true">
23
+ <Property name="Store Type" column="store_type"/>
24
+ <Property name="Store Manager" column="store_manager"/>
25
+ <Property name="Store Sqft" column="store_sqft" type="Numeric"/>
26
+ <Property name="Grocery Sqft" column="grocery_sqft" type="Numeric"/>
27
+ <Property name="Frozen Sqft" column="frozen_sqft" type="Numeric"/>
28
+ <Property name="Meat Sqft" column="meat_sqft" type="Numeric"/>
29
+ <Property name="Has coffee bar" column="coffee_bar" type="Boolean"/>
30
+ <Property name="Street address" column="store_street_address" type="String"/>
31
+ </Level>
32
+ </Hierarchy>
33
+ </Dimension>
34
+
35
+ <Dimension name="Store Size in SQFT">
36
+ <Hierarchy hasAll="true" primaryKey="store_id">
37
+ <Table name="store"/>
38
+ <Level name="Store Sqft" column="store_sqft" type="Numeric" uniqueMembers="true"/>
39
+ </Hierarchy>
40
+ </Dimension>
41
+
42
+ <Dimension name="Store Type">
43
+ <Hierarchy hasAll="true" primaryKey="store_id">
44
+ <Table name="store"/>
45
+ <Level name="Store Type" column="store_type" uniqueMembers="true"/>
46
+ </Hierarchy>
47
+ </Dimension>
48
+
49
+ <Dimension name="Time" type="TimeDimension">
50
+ <Hierarchy hasAll="false" primaryKey="time_id">
51
+ <Table name="time_by_day"/>
52
+ <Level name="Year" column="the_year" type="Numeric" uniqueMembers="true"
53
+ levelType="TimeYears"/>
54
+ <Level name="Quarter" column="quarter" uniqueMembers="false"
55
+ levelType="TimeQuarters"/>
56
+ <Level name="Month" column="month_of_year" uniqueMembers="false" type="Numeric"
57
+ levelType="TimeMonths"/>
58
+ </Hierarchy>
59
+ <Hierarchy hasAll="true" name="Weekly" primaryKey="time_id">
60
+ <Table name="time_by_day"/>
61
+ <Level name="Year" column="the_year" type="Numeric" uniqueMembers="true"
62
+ levelType="TimeYears"/>
63
+ <Level name="Week" column="week_of_year" type="Numeric" uniqueMembers="false"
64
+ levelType="TimeWeeks"/>
65
+ <Level name="Day" column="day_of_month" uniqueMembers="false" type="Numeric"
66
+ levelType="TimeDays"/>
67
+ </Hierarchy>
68
+ </Dimension>
69
+
70
+ <Dimension name="Product">
71
+ <Hierarchy hasAll="true" primaryKey="product_id" primaryKeyTable="product">
72
+ <Join leftKey="product_class_id" rightKey="product_class_id">
73
+ <Table name="product"/>
74
+ <Table name="product_class"/>
75
+ </Join>
76
+ <!--
77
+ <Query>
78
+ <SQL dialect="generic">
79
+ SELECT *
80
+ FROM "product", "product_class"
81
+ WHERE "product"."product_class_id" = "product_class"."product_class_id"
82
+ </SQL>
83
+ </Query>
84
+ <Level name="Product Family" column="product_family" uniqueMembers="true"/>
85
+ <Level name="Product Department" column="product_department" uniqueMembers="false"/>
86
+ <Level name="Product Category" column="product_category" uniqueMembers="false"/>
87
+ <Level name="Product Subcategory" column="product_subcategory" uniqueMembers="false"/>
88
+ <Level name="Brand Name" column="brand_name" uniqueMembers="false"/>
89
+ <Level name="Product Name" column="product_name" uniqueMembers="true"/>
90
+ -->
91
+ <Level name="Product Family" table="product_class" column="product_family"
92
+ uniqueMembers="true"/>
93
+ <Level name="Product Department" table="product_class" column="product_department"
94
+ uniqueMembers="false"/>
95
+ <Level name="Product Category" table="product_class" column="product_category"
96
+ uniqueMembers="false"/>
97
+ <Level name="Product Subcategory" table="product_class" column="product_subcategory"
98
+ uniqueMembers="false"/>
99
+ <Level name="Brand Name" table="product" column="brand_name" uniqueMembers="false"/>
100
+ <Level name="Product Name" table="product" column="product_name"
101
+ uniqueMembers="true"/>
102
+ </Hierarchy>
103
+ </Dimension>
104
+
105
+ <Dimension name="Warehouse">
106
+ <Hierarchy hasAll="true" primaryKey="warehouse_id">
107
+ <Table name="warehouse"/>
108
+ <Level name="Country" column="warehouse_country" uniqueMembers="true"/>
109
+ <Level name="State Province" column="warehouse_state_province"
110
+ uniqueMembers="true"/>
111
+ <Level name="City" column="warehouse_city" uniqueMembers="false"/>
112
+ <Level name="Warehouse Name" column="warehouse_name" uniqueMembers="true"/>
113
+ </Hierarchy>
114
+ </Dimension>
115
+
116
+ <!-- Sales -->
117
+ <Cube name="Sales" defaultMeasure="Unit Sales">
118
+ <Table name="sales_fact_1997">
119
+ <!--
120
+ <AggExclude name="agg_l_03_sales_fact_1997" />
121
+ <AggExclude name="agg_ll_01_sales_fact_1997" />
122
+ <AggExclude name="agg_pl_01_sales_fact_1997" />
123
+ <AggExclude name="agg_l_05_sales_fact_1997" />
124
+ -->
125
+ <AggExclude name="agg_c_special_sales_fact_1997" />
126
+ <!--
127
+ <AggExclude name="agg_c_14_sales_fact_1997" />
128
+ -->
129
+ <AggExclude name="agg_lc_100_sales_fact_1997" />
130
+ <AggExclude name="agg_lc_10_sales_fact_1997" />
131
+ <AggExclude name="agg_pc_10_sales_fact_1997" />
132
+ <AggName name="agg_c_special_sales_fact_1997">
133
+ <AggFactCount column="FACT_COUNT"/>
134
+ <AggIgnoreColumn column="foo"/>
135
+ <AggIgnoreColumn column="bar"/>
136
+ <AggForeignKey factColumn="product_id" aggColumn="PRODUCT_ID" />
137
+ <AggForeignKey factColumn="customer_id" aggColumn="CUSTOMER_ID" />
138
+ <AggForeignKey factColumn="promotion_id" aggColumn="PROMOTION_ID" />
139
+ <AggForeignKey factColumn="store_id" aggColumn="STORE_ID" />
140
+ <!--
141
+ <AggMeasure name="[Measures].[Avg Unit Sales]" column="UNIT_SALES_AVG"/>
142
+ -->
143
+ <AggMeasure name="[Measures].[Unit Sales]" column="UNIT_SALES_SUM" />
144
+ <AggMeasure name="[Measures].[Store Cost]" column="STORE_COST_SUM" />
145
+ <AggMeasure name="[Measures].[Store Sales]" column="STORE_SALES_SUM" />
146
+ <AggLevel name="[Time].[Year]" column="TIME_YEAR" />
147
+ <AggLevel name="[Time].[Quarter]" column="TIME_QUARTER" />
148
+ <AggLevel name="[Time].[Month]" column="TIME_MONTH" />
149
+ </AggName>
150
+ </Table>
151
+
152
+ <DimensionUsage name="Store" source="Store" foreignKey="store_id"/>
153
+ <DimensionUsage name="Store Size in SQFT" source="Store Size in SQFT"
154
+ foreignKey="store_id"/>
155
+ <DimensionUsage name="Store Type" source="Store Type" foreignKey="store_id"/>
156
+ <DimensionUsage name="Time" source="Time" foreignKey="time_id"/>
157
+ <DimensionUsage name="Product" source="Product" foreignKey="product_id"/>
158
+ <Dimension name="Promotion Media" foreignKey="promotion_id">
159
+ <Hierarchy hasAll="true" allMemberName="All Media" primaryKey="promotion_id" defaultMember="All Media">
160
+ <Table name="promotion"/>
161
+ <Level name="Media Type" column="media_type" uniqueMembers="true"/>
162
+ </Hierarchy>
163
+ </Dimension>
164
+ <Dimension name="Promotions" foreignKey="promotion_id">
165
+ <Hierarchy hasAll="true" allMemberName="All Promotions" primaryKey="promotion_id" defaultMember="[All Promotions]">
166
+ <Table name="promotion"/>
167
+ <Level name="Promotion Name" column="promotion_name" uniqueMembers="true"/>
168
+ </Hierarchy>
169
+ </Dimension>
170
+ <Dimension name="Customers" foreignKey="customer_id">
171
+ <Hierarchy hasAll="true" allMemberName="All Customers" primaryKey="customer_id">
172
+ <Table name="customer"/>
173
+ <Level name="Country" column="country" uniqueMembers="true"/>
174
+ <Level name="State Province" column="state_province" uniqueMembers="true"/>
175
+ <Level name="City" column="city" uniqueMembers="false"/>
176
+ <Level name="Name" column="customer_id" type="Numeric" uniqueMembers="true">
177
+ <NameExpression>
178
+ <SQL dialect="oracle">
179
+ "fname" || ' ' || "lname"
180
+ </SQL>
181
+ <SQL dialect="hsqldb">
182
+ "fname" || ' ' || "lname"
183
+ </SQL>
184
+ <SQL dialect="access">
185
+ fname + ' ' + lname
186
+ </SQL>
187
+ <SQL dialect="postgres">
188
+ "fname" || ' ' || "lname"
189
+ </SQL>
190
+ <SQL dialect="mysql">
191
+ CONCAT(`customer`.`fname`, ' ', `customer`.`lname`)
192
+ </SQL>
193
+ <SQL dialect="mssql">
194
+ fname + ' ' + lname
195
+ </SQL>
196
+ <SQL dialect="derby">
197
+ "customer"."fullname"
198
+ </SQL>
199
+ <SQL dialect="db2">
200
+ CONCAT(CONCAT("customer"."fname", ' '), "customer"."lname")
201
+ </SQL>
202
+ <SQL dialect="luciddb">
203
+ "fname" || ' ' || "lname"
204
+ </SQL>
205
+ <SQL dialect="neoview">
206
+ "customer"."fullname"
207
+ </SQL>
208
+ <SQL dialect="teradata">
209
+ "fname" || ' ' || "lname"
210
+ </SQL>
211
+ <SQL dialect="generic">
212
+ fullname
213
+ </SQL>
214
+ </NameExpression>
215
+ <OrdinalExpression>
216
+ <SQL dialect="oracle">
217
+ "fname" || ' ' || "lname"
218
+ </SQL>
219
+ <SQL dialect="hsqldb">
220
+ "fname" || ' ' || "lname"
221
+ </SQL>
222
+ <SQL dialect="access">
223
+ fname + ' ' + lname
224
+ </SQL>
225
+ <SQL dialect="postgres">
226
+ "fname" || ' ' || "lname"
227
+ </SQL>
228
+ <SQL dialect="mysql">
229
+ CONCAT(`customer`.`fname`, ' ', `customer`.`lname`)
230
+ </SQL>
231
+ <SQL dialect="mssql">
232
+ fname + ' ' + lname
233
+ </SQL>
234
+ <SQL dialect="neoview">
235
+ "customer"."fullname"
236
+ </SQL>
237
+ <SQL dialect="derby">
238
+ "customer"."fullname"
239
+ </SQL>
240
+ <SQL dialect="db2">
241
+ CONCAT(CONCAT("customer"."fname", ' '), "customer"."lname")
242
+ </SQL>
243
+ <SQL dialect="luciddb">
244
+ "fname" || ' ' || "lname"
245
+ </SQL>
246
+ <SQL dialect="generic">
247
+ fullname
248
+ </SQL>
249
+ </OrdinalExpression>
250
+ <Property name="Gender" column="gender"/>
251
+ <Property name="Marital Status" column="marital_status"/>
252
+ <Property name="Education" column="education"/>
253
+ <Property name="Yearly Income" column="yearly_income"/>
254
+ </Level>
255
+ </Hierarchy>
256
+ </Dimension>
257
+ <Dimension name="Education Level" foreignKey="customer_id">
258
+ <Hierarchy hasAll="true" primaryKey="customer_id">
259
+ <Table name="customer"/>
260
+ <Level name="Education Level" column="education" uniqueMembers="true"/>
261
+ </Hierarchy>
262
+ </Dimension>
263
+ <Dimension name="Gender" foreignKey="customer_id">
264
+ <Hierarchy hasAll="true" allMemberName="All Gender" primaryKey="customer_id">
265
+ <Table name="customer"/>
266
+ <Level name="Gender" column="gender" uniqueMembers="true"/>
267
+ </Hierarchy>
268
+ </Dimension>
269
+ <Dimension name="Marital Status" foreignKey="customer_id">
270
+ <Hierarchy hasAll="true" allMemberName="All Marital Status" primaryKey="customer_id">
271
+ <Table name="customer"/>
272
+ <Level name="Marital Status" column="marital_status" uniqueMembers="true" approxRowCount="111"/>
273
+ </Hierarchy>
274
+ </Dimension>
275
+ <Dimension name="Yearly Income" foreignKey="customer_id">
276
+ <Hierarchy hasAll="true" primaryKey="customer_id">
277
+ <Table name="customer"/>
278
+ <Level name="Yearly Income" column="yearly_income" uniqueMembers="true"/>
279
+ </Hierarchy>
280
+ </Dimension>
281
+
282
+ <Measure name="Unit Sales" column="unit_sales" aggregator="sum"
283
+ formatString="Standard"/>
284
+ <Measure name="Store Cost" column="store_cost" aggregator="sum"
285
+ formatString="#,###.00"/>
286
+ <Measure name="Store Sales" column="store_sales" aggregator="sum"
287
+ formatString="#,###.00"/>
288
+ <Measure name="Sales Count" column="product_id" aggregator="count"
289
+ formatString="#,###"/>
290
+ <Measure name="Customer Count" column="customer_id"
291
+ aggregator="distinct-count" formatString="#,###"/>
292
+ <Measure name="Promotion Sales" aggregator="sum" formatString="#,###.00">
293
+ <MeasureExpression>
294
+ <SQL dialect="access">
295
+ Iif("sales_fact_1997"."promotion_id" = 0, 0, "sales_fact_1997"."store_sales")
296
+ </SQL>
297
+ <SQL dialect="oracle">
298
+ (case when "sales_fact_1997"."promotion_id" = 0 then 0 else "sales_fact_1997"."store_sales" end)
299
+ </SQL>
300
+ <SQL dialect="hsqldb">
301
+ (case when "sales_fact_1997"."promotion_id" = 0 then 0 else "sales_fact_1997"."store_sales" end)
302
+ </SQL>
303
+ <SQL dialect="postgres">
304
+ (case when "sales_fact_1997"."promotion_id" = 0 then 0 else "sales_fact_1997"."store_sales" end)
305
+ </SQL>
306
+ <SQL dialect="mysql">
307
+ (case when `sales_fact_1997`.`promotion_id` = 0 then 0 else `sales_fact_1997`.`store_sales` end)
308
+ </SQL>
309
+ <!-- Workaround the fact that Infobright does not have a CASE operator.
310
+ The simpler expression gives wrong results, so some tests are
311
+ disabled. -->
312
+ <SQL dialect="neoview">
313
+ (case when "sales_fact_1997"."promotion_id" = 0 then 0 else "sales_fact_1997"."store_sales" end)
314
+ </SQL>
315
+ <SQL dialect="infobright">
316
+ `sales_fact_1997`.`store_sales`
317
+ </SQL>
318
+ <SQL dialect="derby">
319
+ (case when "sales_fact_1997"."promotion_id" = 0 then 0 else "sales_fact_1997"."store_sales" end)
320
+ </SQL>
321
+ <SQL dialect="luciddb">
322
+ (case when "sales_fact_1997"."promotion_id" = 0 then 0 else "sales_fact_1997"."store_sales" end)
323
+ </SQL>
324
+ <SQL dialect="db2">
325
+ (case when "sales_fact_1997"."promotion_id" = 0 then 0 else "sales_fact_1997"."store_sales" end)
326
+ </SQL>
327
+ <SQL dialect="generic">
328
+ (case when sales_fact_1997.promotion_id = 0 then 0 else sales_fact_1997.store_sales end)
329
+ </SQL>
330
+ </MeasureExpression>
331
+ </Measure>
332
+ <CalculatedMember
333
+ name="Profit"
334
+ dimension="Measures">
335
+ <Formula>[Measures].[Store Sales] - [Measures].[Store Cost]</Formula>
336
+ <CalculatedMemberProperty name="FORMAT_STRING" value="$#,##0.00"/>
337
+ </CalculatedMember>
338
+ <CalculatedMember
339
+ name="Profit last Period"
340
+ dimension="Measures"
341
+ formula="COALESCEEMPTY((Measures.[Profit], [Time].[Time].PREVMEMBER), Measures.[Profit])"
342
+ visible="false">
343
+ <CalculatedMemberProperty name="FORMAT_STRING" value="$#,##0.00"/>
344
+ <CalculatedMemberProperty name="MEMBER_ORDINAL" value="18"/>
345
+ </CalculatedMember>
346
+ <CalculatedMember
347
+ name="Profit Growth"
348
+ dimension="Measures"
349
+ formula="([Measures].[Profit] - [Measures].[Profit last Period]) / [Measures].[Profit last Period]"
350
+ visible="true"
351
+ caption="Gewinn-Wachstum">
352
+ <CalculatedMemberProperty name="FORMAT_STRING" value="0.0%"/>
353
+ </CalculatedMember>
354
+ </Cube>
355
+
356
+ <Cube name="Warehouse">
357
+ <Table name="inventory_fact_1997"/>
358
+
359
+ <DimensionUsage name="Store" source="Store" foreignKey="store_id"/>
360
+ <DimensionUsage name="Store Size in SQFT" source="Store Size in SQFT"
361
+ foreignKey="store_id"/>
362
+ <DimensionUsage name="Store Type" source="Store Type" foreignKey="store_id"/>
363
+ <DimensionUsage name="Time" source="Time" foreignKey="time_id"/>
364
+ <DimensionUsage name="Product" source="Product" foreignKey="product_id"/>
365
+ <DimensionUsage name="Warehouse" source="Warehouse" foreignKey="warehouse_id"/>
366
+
367
+ <Measure name="Store Invoice" column="store_invoice" aggregator="sum"/>
368
+ <Measure name="Supply Time" column="supply_time" aggregator="sum"/>
369
+ <Measure name="Warehouse Cost" column="warehouse_cost" aggregator="sum"/>
370
+ <Measure name="Warehouse Sales" column="warehouse_sales" aggregator="sum"/>
371
+ <Measure name="Units Shipped" column="units_shipped" aggregator="sum" formatString="#.0"/>
372
+ <Measure name="Units Ordered" column="units_ordered" aggregator="sum" formatString="#.0"/>
373
+ <Measure name="Warehouse Profit" aggregator="sum">
374
+ <MeasureExpression>
375
+ <SQL dialect="mysql">
376
+ `warehouse_sales` - `inventory_fact_1997`.`warehouse_cost`
377
+ </SQL>
378
+ <SQL dialect="infobright">
379
+ `warehouse_sales` - `inventory_fact_1997`.`warehouse_cost`
380
+ </SQL>
381
+ <SQL dialect="generic">
382
+ &quot;warehouse_sales&quot; - &quot;inventory_fact_1997&quot;.&quot;warehouse_cost&quot;
383
+ </SQL>
384
+ </MeasureExpression>
385
+ </Measure>
386
+ <CalculatedMember
387
+ name="Average Warehouse Sale"
388
+ dimension="Measures">
389
+ <Formula>[Measures].[Warehouse Sales] / [Measures].[Warehouse Cost]</Formula>
390
+ <CalculatedMemberProperty name="FORMAT_STRING" value="$#,##0.00"/>
391
+ </CalculatedMember>
392
+ <NamedSet name="Top Sellers">
393
+ <Formula>TopCount([Warehouse].[Warehouse Name].MEMBERS, 5, [Measures].[Warehouse Sales])</Formula>
394
+ </NamedSet>
395
+ </Cube>
396
+
397
+ <!-- Test a cube based upon a single table. -->
398
+ <Cube name="Store">
399
+ <Table name="store"/>
400
+ <!-- We could have used the shared dimension "Store Type", but we
401
+ want to test private dimensions without primary key. -->
402
+ <Dimension name="Store Type">
403
+ <Hierarchy hasAll="true">
404
+ <Level name="Store Type" column="store_type" uniqueMembers="true"/>
405
+ </Hierarchy>
406
+ </Dimension>
407
+
408
+ <!-- We don't have to specify primary key or foreign key since the shared
409
+ dimension "Store" has the same underlying table as the cube. -->
410
+ <DimensionUsage name="Store" source="Store"/>
411
+
412
+ <Dimension name="Has coffee bar">
413
+ <Hierarchy hasAll="true">
414
+ <Level name="Has coffee bar" column="coffee_bar" uniqueMembers="true"
415
+ type="Boolean"/>
416
+ </Hierarchy>
417
+ </Dimension>
418
+
419
+ <Measure name="Store Sqft" column="store_sqft" aggregator="sum"
420
+ formatString="#,###"/>
421
+ <Measure name="Grocery Sqft" column="grocery_sqft" aggregator="sum"
422
+ formatString="#,###"/>
423
+
424
+ </Cube>
425
+
426
+ <Cube name="HR">
427
+ <Table name="salary"/>
428
+ <!-- Use private "Time" dimension because key is different than public
429
+ "Time" dimension. -->
430
+ <Dimension name="Time" type="TimeDimension" foreignKey="pay_date">
431
+ <Hierarchy hasAll="false" primaryKey="the_date">
432
+ <Table name="time_by_day"/>
433
+ <Level name="Year" column="the_year" type="Numeric" uniqueMembers="true"
434
+ levelType="TimeYears"/>
435
+ <Level name="Quarter" column="quarter" uniqueMembers="false"
436
+ levelType="TimeQuarters"/>
437
+ <!-- Use the_month as source for the name, so members look like
438
+ [Time].[1997].[Q1].[Jan] rather than [Time].[1997].[Q1].[1]. -->
439
+ <Level name="Month" column="month_of_year" nameColumn="the_month"
440
+ uniqueMembers="false" type="Numeric" levelType="TimeMonths"/>
441
+ </Hierarchy>
442
+ </Dimension>
443
+
444
+ <Dimension name="Store" foreignKey="employee_id" >
445
+ <Hierarchy hasAll="true" primaryKey="employee_id"
446
+ primaryKeyTable="employee">
447
+ <Join leftKey="store_id" rightKey="store_id">
448
+ <Table name="employee"/>
449
+ <Table name="store"/>
450
+ </Join>
451
+ <Level name="Store Country" table="store" column="store_country"
452
+ uniqueMembers="true"/>
453
+ <Level name="Store State" table="store" column="store_state"
454
+ uniqueMembers="true"/>
455
+ <Level name="Store City" table="store" column="store_city"
456
+ uniqueMembers="false"/>
457
+ <Level name="Store Name" table="store" column="store_name"
458
+ uniqueMembers="true">
459
+ <Property name="Store Type" column="store_type"/>
460
+ <Property name="Store Manager" column="store_manager"/>
461
+ <Property name="Store Sqft" column="store_sqft" type="Numeric"/>
462
+ <Property name="Grocery Sqft" column="grocery_sqft" type="Numeric"/>
463
+ <Property name="Frozen Sqft" column="frozen_sqft" type="Numeric"/>
464
+ <Property name="Meat Sqft" column="meat_sqft" type="Numeric"/>
465
+ <Property name="Has coffee bar" column="coffee_bar" type="Boolean"/>
466
+ <Property name="Street address" column="store_street_address"
467
+ type="String"/>
468
+ </Level>
469
+ </Hierarchy>
470
+ </Dimension>
471
+
472
+ <Dimension name="Pay Type" foreignKey="employee_id">
473
+ <Hierarchy hasAll="true" primaryKey="employee_id"
474
+ primaryKeyTable="employee">
475
+ <Join leftKey="position_id" rightKey="position_id">
476
+ <Table name="employee"/>
477
+ <Table name="position"/>
478
+ </Join>
479
+ <Level name="Pay Type" table="position" column="pay_type"
480
+ uniqueMembers="true"/>
481
+ </Hierarchy>
482
+ </Dimension>
483
+
484
+ <Dimension name="Store Type" foreignKey="employee_id">
485
+ <Hierarchy hasAll="true" primaryKeyTable="employee" primaryKey="employee_id">
486
+ <Join leftKey="store_id" rightKey="store_id">
487
+ <Table name="employee"/>
488
+ <Table name="store"/>
489
+ </Join>
490
+ <Level name="Store Type" table="store" column="store_type"
491
+ uniqueMembers="true"/>
492
+ </Hierarchy>
493
+ </Dimension>
494
+
495
+ <Dimension name="Position" foreignKey="employee_id">
496
+ <Hierarchy hasAll="true" allMemberName="All Position"
497
+ primaryKey="employee_id">
498
+ <Table name="employee"/>
499
+ <Level name="Management Role" uniqueMembers="true"
500
+ column="management_role"/>
501
+ <Level name="Position Title" uniqueMembers="false"
502
+ column="position_title" ordinalColumn="position_id"/>
503
+ </Hierarchy>
504
+ </Dimension>
505
+
506
+ <Dimension name="Department" foreignKey="department_id">
507
+ <Hierarchy hasAll="true" primaryKey="department_id">
508
+ <Table name="department"/>
509
+ <Level name="Department Description" type="Numeric" uniqueMembers="true"
510
+ column="department_id"/>
511
+ </Hierarchy>
512
+ </Dimension>
513
+ <Dimension name="Employees" foreignKey="employee_id">
514
+ <Hierarchy hasAll="true" allMemberName="All Employees"
515
+ primaryKey="employee_id">
516
+ <Table name="employee"/>
517
+ <Level name="Employee Id" type="Numeric" uniqueMembers="true"
518
+ column="employee_id" parentColumn="supervisor_id"
519
+ nameColumn="full_name" nullParentValue="0">
520
+ <Closure parentColumn="supervisor_id" childColumn="employee_id">
521
+ <Table name="employee_closure"/>
522
+ </Closure>
523
+ <Property name="Marital Status" column="marital_status"/>
524
+ <Property name="Position Title" column="position_title"/>
525
+ <Property name="Gender" column="gender"/>
526
+ <Property name="Salary" column="salary"/>
527
+ <Property name="Education Level" column="education_level"/>
528
+ <Property name="Management Role" column="management_role"/>
529
+ </Level>
530
+ </Hierarchy>
531
+ </Dimension>
532
+
533
+ <Measure name="Org Salary" column="salary_paid" aggregator="sum"
534
+ formatString="Currency"/>
535
+ <Measure name="Count" column="employee_id" aggregator="count"
536
+ formatString="#,#"/>
537
+ <Measure name="Number of Employees" column="employee_id"
538
+ aggregator="distinct-count" formatString="#,#"/>
539
+ <CalculatedMember name="Employee Salary" dimension="Measures"
540
+ formatString="Currency"
541
+ formula="([Employees].currentmember.datamember, [Measures].[Org Salary])"/>
542
+ <CalculatedMember name="Avg Salary" dimension="Measures"
543
+ formatString="Currency"
544
+ formula="[Measures].[Org Salary]/[Measures].[Number of Employees]"/>
545
+ </Cube>
546
+
547
+ <!-- Cube with one ragged hierarchy (otherwise the same as the "Sales"
548
+ cube). -->
549
+ <Cube name="Sales Ragged">
550
+ <Table name="sales_fact_1997">
551
+ <AggExclude name="agg_pc_10_sales_fact_1997"/>
552
+ <AggExclude name="agg_lc_10_sales_fact_1997"/>
553
+ </Table>
554
+ <Dimension name="Store" foreignKey="store_id">
555
+ <Hierarchy hasAll="true" primaryKey="store_id">
556
+ <Table name="store_ragged"/>
557
+ <Level name="Store Country" column="store_country" uniqueMembers="true"
558
+ hideMemberIf="Never"/>
559
+ <Level name="Store State" column="store_state" uniqueMembers="true"
560
+ hideMemberIf="IfParentsName"/>
561
+ <Level name="Store City" column="store_city" uniqueMembers="false"
562
+ hideMemberIf="IfBlankName"/>
563
+ <Level name="Store Name" column="store_name" uniqueMembers="true"
564
+ hideMemberIf="Never">
565
+ <Property name="Store Type" column="store_type"/>
566
+ <Property name="Store Manager" column="store_manager"/>
567
+ <Property name="Store Sqft" column="store_sqft" type="Numeric"/>
568
+ <Property name="Grocery Sqft" column="grocery_sqft" type="Numeric"/>
569
+ <Property name="Frozen Sqft" column="frozen_sqft" type="Numeric"/>
570
+ <Property name="Meat Sqft" column="meat_sqft" type="Numeric"/>
571
+ <Property name="Has coffee bar" column="coffee_bar" type="Boolean"/>
572
+ <Property name="Street address" column="store_street_address" type="String"/>
573
+ </Level>
574
+ </Hierarchy>
575
+ </Dimension>
576
+
577
+ <Dimension name="Geography" foreignKey="store_id">
578
+ <Hierarchy hasAll="true" primaryKey="store_id">
579
+ <Table name="store_ragged"/>
580
+ <Level name="Country" column="store_country" uniqueMembers="true"
581
+ hideMemberIf="Never"/>
582
+ <Level name="State" column="store_state" uniqueMembers="true"
583
+ hideMemberIf="IfParentsName"/>
584
+ <Level name="City" column="store_city" uniqueMembers="false"
585
+ hideMemberIf="IfBlankName"/>
586
+ </Hierarchy>
587
+ </Dimension>
588
+
589
+ <DimensionUsage name="Store Size in SQFT" source="Store Size in SQFT"
590
+ foreignKey="store_id"/>
591
+ <DimensionUsage name="Store Type" source="Store Type" foreignKey="store_id"/>
592
+ <DimensionUsage name="Time" source="Time" foreignKey="time_id"/>
593
+ <DimensionUsage name="Product" source="Product" foreignKey="product_id"/>
594
+ <Dimension name="Promotion Media" foreignKey="promotion_id">
595
+ <Hierarchy hasAll="true" allMemberName="All Media" primaryKey="promotion_id">
596
+ <Table name="promotion"/>
597
+ <Level name="Media Type" column="media_type" uniqueMembers="true"/>
598
+ </Hierarchy>
599
+ </Dimension>
600
+ <Dimension name="Promotions" foreignKey="promotion_id">
601
+ <Hierarchy hasAll="true" allMemberName="All Promotions" primaryKey="promotion_id">
602
+ <Table name="promotion"/>
603
+ <Level name="Promotion Name" column="promotion_name" uniqueMembers="true"/>
604
+ </Hierarchy>
605
+ </Dimension>
606
+ <Dimension name="Customers" foreignKey="customer_id">
607
+ <Hierarchy hasAll="true" allMemberName="All Customers" primaryKey="customer_id">
608
+ <Table name="customer"/>
609
+ <Level name="Country" column="country" uniqueMembers="true"/>
610
+ <Level name="State Province" column="state_province" uniqueMembers="true"/>
611
+ <Level name="City" column="city" uniqueMembers="false"/>
612
+ <Level name="Name" uniqueMembers="true">
613
+ <KeyExpression>
614
+ <SQL dialect="oracle">
615
+ "fname" || ' ' || "lname"
616
+ </SQL>
617
+ <SQL dialect="hsqldb">
618
+ "fname" || ' ' || "lname"
619
+ </SQL>
620
+ <SQL dialect="access">
621
+ fname + ' ' + lname
622
+ </SQL>
623
+ <SQL dialect="postgres">
624
+ "fname" || ' ' || "lname"
625
+ </SQL>
626
+ <SQL dialect="mysql">
627
+ CONCAT(`customer`.`fname`, ' ', `customer`.`lname`)
628
+ </SQL>
629
+ <SQL dialect="mssql">
630
+ fname + ' ' + lname
631
+ </SQL>
632
+ <SQL dialect="derby">
633
+ "customer"."fullname"
634
+ </SQL>
635
+ <SQL dialect="db2">
636
+ CONCAT(CONCAT("customer"."fname", ' '), "customer"."lname")
637
+ </SQL>
638
+ <SQL dialect="luciddb">
639
+ "fname" || ' ' || "lname"
640
+ </SQL>
641
+ <SQL dialect="neoview">
642
+ "customer"."fullname"
643
+ </SQL>
644
+ <SQL dialect="generic">
645
+ fullname
646
+ </SQL>
647
+ </KeyExpression>
648
+ <Property name="Gender" column="gender"/>
649
+ <Property name="Marital Status" column="marital_status"/>
650
+ <Property name="Education" column="education"/>
651
+ <Property name="Yearly Income" column="yearly_income"/>
652
+ </Level>
653
+ </Hierarchy>
654
+ </Dimension>
655
+ <Dimension name="Education Level" foreignKey="customer_id">
656
+ <Hierarchy hasAll="true" primaryKey="customer_id">
657
+ <Table name="customer"/>
658
+ <Level name="Education Level" column="education" uniqueMembers="true"/>
659
+ </Hierarchy>
660
+ </Dimension>
661
+ <Dimension name="Gender" foreignKey="customer_id">
662
+ <Hierarchy hasAll="true" allMemberName="All Gender" primaryKey="customer_id">
663
+ <Table name="customer"/>
664
+ <Level name="Gender" column="gender" uniqueMembers="true"/>
665
+ </Hierarchy>
666
+ </Dimension>
667
+ <Dimension name="Marital Status" foreignKey="customer_id">
668
+ <Hierarchy hasAll="true" allMemberName="All Marital Status" primaryKey="customer_id">
669
+ <Table name="customer"/>
670
+ <Level name="Marital Status" column="marital_status" uniqueMembers="true"/>
671
+ </Hierarchy>
672
+ </Dimension>
673
+ <Dimension name="Yearly Income" foreignKey="customer_id">
674
+ <Hierarchy hasAll="true" primaryKey="customer_id">
675
+ <Table name="customer"/>
676
+ <Level name="Yearly Income" column="yearly_income" uniqueMembers="true"/>
677
+ </Hierarchy>
678
+ </Dimension>
679
+ <Measure name="Unit Sales" column="unit_sales" aggregator="sum"
680
+ formatString="Standard"/>
681
+ <Measure name="Store Cost" column="store_cost" aggregator="sum"
682
+ formatString="#,###.00"/>
683
+ <Measure name="Store Sales" column="store_sales" aggregator="sum"
684
+ formatString="#,###.00"/>
685
+ <Measure name="Sales Count" column="product_id" aggregator="count"
686
+ formatString="#,###"/>
687
+ <Measure name="Customer Count" column="customer_id" aggregator="distinct-count"
688
+ formatString="#,###"/>
689
+ </Cube>
690
+
691
+ <!-- a simpler version of "Sales" (with MEMBER_ORDINAL-properties) -->
692
+ <Cube name="Sales 2">
693
+ <Table name="sales_fact_1997"/>
694
+
695
+ <DimensionUsage name="Time" source="Time" foreignKey="time_id"/>
696
+ <DimensionUsage name="Product" source="Product" foreignKey="product_id"/>
697
+
698
+ <Dimension name="Gender" foreignKey="customer_id">
699
+ <Hierarchy hasAll="true" allMemberName="All Gender" primaryKey="customer_id">
700
+ <Table name="customer"/>
701
+ <Level name="Gender" column="gender" uniqueMembers="true"/>
702
+ </Hierarchy>
703
+ </Dimension>
704
+
705
+ <Measure name="Sales Count" column="product_id" aggregator="count" formatString="#,###">
706
+ <CalculatedMemberProperty name="MEMBER_ORDINAL" value="1"/>
707
+ </Measure>
708
+
709
+ <Measure name="Unit Sales" column="unit_sales" aggregator="sum" formatString="Standard">
710
+ <CalculatedMemberProperty name="MEMBER_ORDINAL" value="2"/>
711
+ </Measure>
712
+
713
+ <Measure name="Store Sales" column="store_sales" aggregator="sum" formatString="#,###.00">
714
+ <CalculatedMemberProperty name="MEMBER_ORDINAL" value="3"/>
715
+ </Measure>
716
+
717
+ <Measure name="Store Cost" column="store_cost" aggregator="sum" formatString="#,###.00">
718
+ <CalculatedMemberProperty name="MEMBER_ORDINAL" value="6"/>
719
+ </Measure>
720
+
721
+ <Measure name="Customer Count" column="customer_id" aggregator="distinct-count" formatString="#,###">
722
+ <CalculatedMemberProperty name="MEMBER_ORDINAL" value="7"/>
723
+ </Measure>
724
+
725
+ <CalculatedMember
726
+ name="Profit"
727
+ dimension="Measures">
728
+ <Formula>[Measures].[Store Sales] - [Measures].[Store Cost]</Formula>
729
+ <CalculatedMemberProperty name="FORMAT_STRING" value="$#,##0.00"/>
730
+ <CalculatedMemberProperty name="MEMBER_ORDINAL" value="4"/>
731
+ </CalculatedMember>
732
+
733
+ <CalculatedMember
734
+ name="Profit last Period"
735
+ dimension="Measures"
736
+ formula="COALESCEEMPTY((Measures.[Profit], [Time].[Time].PREVMEMBER), Measures.[Profit])"
737
+ visible="false">
738
+ <CalculatedMemberProperty name="MEMBER_ORDINAL" value="5"/>
739
+ </CalculatedMember>
740
+ </Cube>
741
+
742
+ <VirtualCube name="Warehouse and Sales" defaultMeasure="Store Sales">
743
+ <VirtualCubeDimension cubeName="Sales" name="Customers"/>
744
+ <VirtualCubeDimension cubeName="Sales" name="Education Level"/>
745
+ <VirtualCubeDimension cubeName="Sales" name="Gender"/>
746
+ <VirtualCubeDimension cubeName="Sales" name="Marital Status"/>
747
+ <VirtualCubeDimension name="Product"/>
748
+ <VirtualCubeDimension cubeName="Sales" name="Promotion Media"/>
749
+ <VirtualCubeDimension cubeName="Sales" name="Promotions"/>
750
+ <VirtualCubeDimension name="Store"/>
751
+ <VirtualCubeDimension name="Time"/>
752
+ <VirtualCubeDimension cubeName="Sales" name="Yearly Income"/>
753
+ <VirtualCubeDimension cubeName="Warehouse" name="Warehouse"/>
754
+ <VirtualCubeMeasure cubeName="Sales" name="[Measures].[Sales Count]"/>
755
+ <VirtualCubeMeasure cubeName="Sales" name="[Measures].[Store Cost]"/>
756
+ <VirtualCubeMeasure cubeName="Sales" name="[Measures].[Store Sales]"/>
757
+ <VirtualCubeMeasure cubeName="Sales" name="[Measures].[Unit Sales]"/>
758
+ <VirtualCubeMeasure cubeName="Sales" name="[Measures].[Profit]"/>
759
+ <VirtualCubeMeasure cubeName="Sales" name="[Measures].[Profit Growth]"/>
760
+ <VirtualCubeMeasure cubeName="Warehouse" name="[Measures].[Store Invoice]"/>
761
+ <VirtualCubeMeasure cubeName="Warehouse" name="[Measures].[Supply Time]"/>
762
+ <VirtualCubeMeasure cubeName="Warehouse" name="[Measures].[Units Ordered]"/>
763
+ <VirtualCubeMeasure cubeName="Warehouse" name="[Measures].[Units Shipped]"/>
764
+ <VirtualCubeMeasure cubeName="Warehouse" name="[Measures].[Warehouse Cost]"/>
765
+ <VirtualCubeMeasure cubeName="Warehouse" name="[Measures].[Warehouse Profit]"/>
766
+ <VirtualCubeMeasure cubeName="Warehouse" name="[Measures].[Warehouse Sales]"/>
767
+ <VirtualCubeMeasure cubeName="Warehouse" name="[Measures].[Average Warehouse Sale]"/>
768
+ <!--
769
+ <VirtualCubeMeasure cubeName="Sales" name="[Measures].[Store Sales Net]"/>
770
+ -->
771
+ <CalculatedMember name="Profit Per Unit Shipped" dimension="Measures">
772
+ <Formula>[Measures].[Profit] / [Measures].[Units Shipped]</Formula>
773
+ </CalculatedMember>
774
+ </VirtualCube>
775
+
776
+ <!-- A California manager can only see customers and stores in California.
777
+ They cannot drill down on Gender. -->
778
+ <Role name="California manager">
779
+ <SchemaGrant access="none">
780
+ <CubeGrant cube="Sales" access="all">
781
+ <HierarchyGrant hierarchy="[Store]" access="custom"
782
+ topLevel="[Store].[Store Country]">
783
+ <MemberGrant member="[Store].[USA].[CA]" access="all"/>
784
+ <MemberGrant member="[Store].[USA].[CA].[Los Angeles]" access="none"/>
785
+ </HierarchyGrant>
786
+ <HierarchyGrant hierarchy="[Customers]" access="custom"
787
+ topLevel="[Customers].[State Province]" bottomLevel="[Customers].[City]">
788
+ <MemberGrant member="[Customers].[USA].[CA]" access="all"/>
789
+ <MemberGrant member="[Customers].[USA].[CA].[Los Angeles]" access="none"/>
790
+ </HierarchyGrant>
791
+ <HierarchyGrant hierarchy="[Gender]" access="none"/>
792
+ </CubeGrant>
793
+ </SchemaGrant>
794
+ </Role>
795
+
796
+ <Role name="No HR Cube">
797
+ <SchemaGrant access="all">
798
+ <CubeGrant cube="HR" access="none"/>
799
+ </SchemaGrant>
800
+ </Role>
801
+
802
+ </Schema>