olap4r 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.
@@ -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>