前言
欢迎使用 MySQL多维数据仓库指南。
数据仓库可以通过集成各种多样的数据实现信息的统一,这些数据来源包括现行的事务操作和管理信息系统,以及外部各种信息源。这些源数据将被整合,清洗,转换,如果需要直接从这些数据来源读取数据的话,该数据将会在数据仓库中以更有利于操作的方式存储。
数据仓库的数据构造方式使你可以存储当前和历史的数据。当前数据是实际事务操作活动必要的,一般是定期的硬拷贝(比如打印出的报表)或者是联机报表。而历史数据,往往不是那么容易利用,可以提供基于时间点分析的商业信息,比如:追踪、推论分析,以及对比,这些对于长期计划和战略性的市场决策都是很重要的。
如何从多个信息源整合信息,定期积聚存储,有效的需求设计和发展技术,所有这些都和事务型管理信息系统所应用的技术有很大的不同。本书都是关于数据仓库的设计和开发技术,它涵盖了大部分建立数据仓库所涉及到的大部分技术问题。更为重要的是,本书提供了一个易懂的操作指南,便于开发一个实际的数据仓库。
本书的适用范围
mysql多维数据仓库指南是一个实战的书籍。你将使用mysql数据库,但该书不是关于mysql的。本书不涉及任何硬件架构方面的问题。
本书主要关心数据仓库的设计和开发技术。它不涉及开发项目的管理,理论,以及如何领导开发等相关的技术问题。
本书用一个数据仓库开发的例子来展示技术如何应用。提供数据模型和sql脚本,将能够适用于实际的数据仓库开发。这些脚本已经在Windows xp professional sp2 平台上用mysql 5。0。21 版本测试通过。
另外,本书不再对以下的几个主题进行特别的论述:
· 数据仓库的概念
· Sql
· MySQL数据库
适合阅读本书的人:
数据仓库应用于各种组织和商业机构,从政府部门,非盈利组织到学校,从制造业到零售店,从金融机构到医疗机构,从传统公司到互联网商家。
本书首先面向数据仓库开发人员。但是,it 经理和其他it职业,特别是那些对mis(经营报告)和dss(决策支撑应用)感兴趣的人将发现本书同样有用。总的说来,本书是给那些为分析应用准备数据工作相关的人,以及那些需要递交信息的人,比如打印报表,联机报表。
本书同样适用于那些数据仓库初学者。它将直接快速的为那些正在准备开发他们第一个数据仓库的人提供帮助。
教师和学生可以将该书作为教科书,以便于明确他们对于数据仓库原理,概念的理解。大部分章节可以被定制用于实验练习。
预备的技能
本书不是面向it新人,为了更有效的利用本书,读者必须有一些系统开发经验。但是,不要求先有数据仓库的建设经验。
需要实践操作本书例子的人需要有rdbms(关系数据库管理系统)和sql的实际操作技能。
你可以从本书得到
你将可以仅仅用一个示例,一个最先存储商业销售相关数据的数据仓库,并且进行实践操作,就可以磨练你的数据仓库知识和实际技能。该示例是一个精简版的实际数据仓库,可以在很多种商业类型中找到它的原型。
你将基于mysql数据库用本书介绍的技术一步步的开发该示例中销售相关数据仓库。这些技术是在通常数据仓库开发中将碰到的问题的分解技巧。通过完成本文的学习并且完成所有的练习,你将获得相关工作经验并可以为负责第一个实际数据仓库项目做好准备。
章节总览
本书包含25个章节和一个附录。所有章节组织为四个部分。第一部分涵盖数据仓库基础。第二部分描述从原始数据到数据仓库的迁移。第三部分讨论如何控制数据仓库的发展演化。第四部分涉及到一些高级的多维技术。接下来的部分将给出每个章节的预览。
第一篇 基本原理
第一篇,涵盖多维数据仓库的基础原理,有四个章节。
第1章,“基本组成”,介绍星型模式(一种数据库模式,有一个事实表,周围围绕着多个维表)以及解释该模式的基本组成。
第2章,”维度历史“ 介绍用代理健来维护维成员的历史记录。
第3章,“度量可加性”,包含维度数据仓库中最重要的特性之一,也就是存储在数据仓库事实表中的度量的可加性。
第4章,“维度查询”,介绍最适合应用于星型模式的一种sql查询,维度查询可以用来证明一个维度数据仓库是否具备两个最基本的设计指标:简单和高效。
第二篇: 提取、转换和加载
该篇的所有5个章节涉及了数据集成,事实表,维表。
第5章,“源数据提取”介绍了不同类型数据的提取。
第6章“导入时间维”涵盖了三种最通用的加载时间维的技术。
第7章 “初始化导入“和第8章,“定期导入”涉及到两种类型的导入技术,初始化和周期性。
第9章,“周期导入计划”,作为第二篇的总结,提供一个进阶式的指导,关于如何利用Windows的任务管理器实现定期导入计划。
第三篇:成长
第三篇介绍了不同的处理技术,这些技术主要处理一个成功的多维数据仓库成长过程中碰到的相关问题。该部分有十个章节。
第10章,“增加字段”讨论关于向现有数据仓库中的表增加一个字段的技术问题。
第11章,“按需载入”涉及按需载入的技术。
第12章,“维表子集”,介绍相关技术以帮助用户处理维表子集问题。
第13章,“维度角色扮演”,关于在一个事实表中多次使用同一个维度的问题。
第14章,“快照”让你在需要作出汇总数据的时候,能提供快速性能的查询。
第15章,“唯度层次”和第16章“多路径和不齐整的维度层次”是关于简单和多路径维度技术,相应的,这些技术帮助人们进行汇聚和钻取分析。
第17章,“维度退化”,展示了如何用维度退化技术去减少一个数据仓库模式的复杂性。
第18章,“垃圾维”,是关于垃圾维的技术,即将表面上不相关的但是经常会被用户进行要求分析的数据,用维的方式组织起来。
第19章,“多星型模式”展示了如何在模式中增加多个星型模式。
第四篇:高级技术
包含六个章节。
第20章,“不齐整数据源”,介绍如何在数据仓库中处理那些结构无法直接映射到目标表的数据源。
第21章,“无事实的事实表”,帮助你为客户建立一种可供分析的辅助数据,一种无事实字段的事实表,该数据无法从数据源直接取得度量。
第22章,“迟来的事实”,包含一个技术,用于处理当数据源中某个特殊的事实没有在计划载入时间之前产生的情况。
第23章,“外部数据源和维度合并“,包括两个主题:处理外部数据源和如何将多个维中分散的属性合并到一个维的技术。
第24章,“累积度量”,讨论两个相关的问题:计算度量和累积度量的不可相加性。
第25章,“分段维度”,阐述一种技术,可以帮助使用者针对那些有连续值的属性进行数据分析。
附录
附录a,“格式文件数据源”,介绍如何使用本书例子中的格式文件数据源。
源代码下载
可以从http://jtute.com.上面下载本书的示例的程序脚本。
==============================================================================
Dimensional Data Warehousing with MySQL: A Tutorial
byDjoni Darmawikarta
Brainy Software Corp. 2007 (432 pages)
ISBN:9780975212820
Computer programmers who need to build a data warehouse will find relevant examples and information written in a thorough, easy-to-follow style in this step-by-step tutorial.
封底 Back Cover
将Mysql这个最流行的开源数据库软件进行数据仓库应用,在本书之前还从来没有被在创建多维数据仓库方面用这种入门指南的方式进行介绍。主题包括 星型模式建模,数据载入(数据的提取、转换和加载:etl),测试检验,以及多维查询。本书由始至终应用一个可实践,相对精简的真实的项目。其全面,易于接受的写作形式使得那些需要建立数据仓库的程序员可以得到相关的示例和资料。
关于作者
Djoni Darmawikarta 曾就职于ibm,现在是一个加拿大保险公司的数据仓库、商业智能团队中的技术专家。目前生活在Toronto, Ontario
第一篇基本原理
章节列表:
第1章:基本组成
第2章:维度历史
第3章:维度可加性
第4章:维度查询
本篇概述
你将运用关系数据库来实施一个维度数据仓库。事实表和维表这两种类型的关系表构成了一个数据仓库模式的基本部分,在本书的第一部分,你将用MySQL数据库建立这些基本部分。
第1章:基本组成
概述
本章将了解两个重要的主题:星型模式和代理键。星型模式是一种维度数据仓库的数据结构。代理键是在数据仓库中添加到事实表以作为主键的字段。
在本章你将开始一个关于建立真实数据仓库的漫长旅程。一些任务需要在本章完成:
建立一个数据库用户。
建立两个关系数据库,一个作为数据仓库,另外一个作为源数据库。
为数据仓库建立数据库表。
产生代理键:
你需要建立一个源数据库是因为你的数据仓库还没有源数据。实际应用中这一步并不是必要的,因为你的数据仓库很有可能是基于已有的数据源建立的。你将在本书的第二篇开始使用这个源数据库。
第1节 星型模式
一个好的维度数据仓库需要有简洁的数据结构。从技术角度看,一个简洁的结构将意味着更快速的查询。在一个维度数据仓库,关系数据库的实施中有两种类型的表,事实表和维度表。事实表包含商业事实数据(或者称为度量值)。维度表包含查询该数据库的种类。
注意 在看完本书前面两个章节后,你对事实表和维度表将有更好的理解。
这些表在数据仓库中的联系模式看起来象星星状,因此该模式的术语就称为星型模式。
注意 除了星型模式外,雪花模式也会用于数据仓库中。但是较之星型结构,其建模更为困难。而且,雪花模式不容易理解和应用,而且其查询性能也低于星型模式。这些缺陷是雪花模式不适合建立维度数据仓库,因此本书只是涉及星型模式。
一个星型模式包含一个事实表,该事实表环绕着两个甚至更多的维表。单星结构只有一个事实表,而多星结构则是每个星有多个的事实表,另外,维度表可以为多个事实表所共享。本章只包括单星结构,读者将会在19章“多星模式”中学习到多星结构。
图1-1 展示了一个单星结构的维度模式,这是一个我们将会在本书中扩展的销售订单的数据仓库。
图1-1 一个单星型的维度模式(自己截图)
事实表名称的后缀通常是fact,而dim(维度dimension的缩写)通常用于维度表的后缀。根据这种命名规则,很明显,图1-1 中有一个事实表(销售订单事实表sales_order_fact)和四个维度表(客户维customer_dim,订单维 order_dim,产品维 product_dim, 以及 日期维date_dim)。事实表包含一个或者多个可测的事实值(一个可以测的事实值简称为度量),而维表则对度量进行分类。
每个维表有且只有一个代理键字段,代理键字段名称的后缀是sk。维表中的每个代理键字段在事实表中都会有对应的相同的字段,这样使得数据库的查询更为容易。但是,事实表中以sk为后缀的字段并不是代表一个代理键字段。
图1-1中连接事实表sales_order_fact和四个维表的线指明了查询这些表的连接关系。这些连接是基于维表的代理键字段的。
当建立一个维度数据仓库的时候,你将会在数据仓库中自己产生代理键的值,这些代理键值不是从源数据得到的。代理键的值是连续的数值。
注意 后面关于“代理键”的章节将对代理键进行详细的说明。
现在你已经知道了星型模式,事实表,维表的概念,我们来看一个例子,如果我们对订单的订单金额感兴趣,并且决定用图1-1 中sales_order_fact表的order_amount字段作为度量。表1-1显示sales_order_fact表的一个抽样记录。
表1-1:一个事实表的抽样
将数据用电子表格形式展现:
customer_sk
product_sk
date_sk
order_sk
order_amount
1
1
1
1
1000
表1-1所关联的维表中相应的数据记录 将在表1-2到1-5中展示。
表1-2:客户维表中关联的行
customer_sk
customer_no
customer_name
1
1
Dons Limited
表1-3:产品维表中关联的行
product_sk
product_code
product_name
1
1
Cangcung Hard Disk
表1-4:时间维表中关联的行
sk
date
1
2007–02–01
表1-5:订单维表中关联的行
order_sk
order_number
1
1
事实表的行说明该订单的金额是$1,000。这是事实的度量。sales_order_fact表中的customer_sk字段值为1表示对应customer_dim维表中customer_sk字段值为1的行。这个关联表示,提交这个订单的是名为Dons Limited的客户。用事实表中的product_sk字段的值可以在产品维表中追踪到产品信息。通过将事实表和date_dim时间表的date_sk字段进行关联,你可以得到订单日期。同样,通过将事实表和order_dim订单维表中的order_sk字段进行关联,你可以得到订单号。
第2节 代理键
维表中的代理键字段是维表的主键。代理键的值通常是连续的数值而不代表任何商业意义。相比之下,许多来自源数据的键值都有商业含义。
你将在你的数据仓库内部产生代理键值;你不可以从源数据得到代理键值,在第2章“维度历史”中我将说明这么做的目的。
在mysql中,你可以通过设置代理键字段的AUTO INCREMENT自增属性产生代理值。通过将自增字段用null值代替来插入一个递增的整数。
已经有足够的原理让我们可以开始建立我们的数据仓库了。接下来的这节,“任务”阐述了你需要在本章完成的任务的详细步骤。
第3节 任务
有四个任务需要在本章完成:
1. 建立一个数据库用户。
2.建立两个关系数据库,一个作为数据仓库,另外一个作为源数据库。
3.为数据仓库建立数据库表。
4.产生代理键。
每个任务将在独立的小节中说明。
建立一个数据库用户id
第一步产生一个数据库用户,你可以用该帐户进行操作数据仓库和数据源。
[sql] view plain copy
/*****************************************************************/ */
/* create_user_id.sql */
/* */
/*****************************************************************/
GRANT ALL ON *.* TO dwid@localhost IDENTIFIED BY ‘pw’;
/* end of script
在你开始前,确信你已经将本书随附的脚本文件放置在MySQL的安装目录下面。例如,我的安装目录是C:\mysql,所以我将我的脚本文件放在C:\mysql\scripts目录中。
我们开始将用root用户登陆mysql,请输入下面的命令:
C:\>mysql -uroot -p
你将被提示输入密码:
Enter password: ********
输入密码后,将看到控制台的欢迎信息:
[sql] view plain copy
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 6 to server version: 5.0.21-community-nt
Type ‘help;’ or ‘\h’ for help. Type ‘\c’ to clear the buffer.
mysql>
敲入密码后的这些信息是当一个root用户登陆mysql时典型的回应信息。列表1-1 所示的脚本create_user_id.sql产生一个用户id dwid 其密码是 pw 。
列表1-1: 创建dwid 用户 id
[sql] view plain copy
/*****************************************************************/ */
/* create_user_id.sql */
/* */
/*****************************************************************/
GRANT ALL ON *.* TO dwid@localhost IDENTIFIED BY ‘pw’;
/* end of script
用下面的命令方式运行该脚本:
mysql> \. c:\mysql\scripts\create_user_id.sql
回车后,你将看到回应的信息:
Query OK, 0 rows affected (0.03 sec)
用show grants命令,你可以确认用户id:dwid 是否已经被成功创建,你应该在命令中包含你的mysql服务器名称:
mysql> show grants for dwid@localhost;
假如存在用户 dwid,你可以看到如下信息:
Grants for dwid@localhost
GRANT ALL PRIVILEGES ON *.* TO ‘dwid’@’localhost’ IDENTIFIED BY
PASSWORD ‘*D821809F681A40A6E379B50D0463EFAE20BDD122’
1 row in set (0.00 sec)
注意 密码部分在你的显示屏上显示的方式将不同于上面的描述。
现在你需要退出mysq然后用dwid用户登陆,退出mysql用exit命令:
mysql> exit
为了以dwid身份登陆mysql,可以用下面的命令:
c:\>mysql -udwid -p
然后输入dwid的密码,记住,密码是pw。
建立数据库
有两个数据库需要建立,源数据(source)和数据仓库(dw)。Source数据库存储你的数据,也就是这些数据将成为你的数据仓库的源数据。Dw数据库是为了作为数据仓库建立的。
可以用列表1-2中的create_databases.sql 脚本进行创建数据库。
列表 1.2: 建立 dw 和 source 数据库
[sql] view plain copy
/*****************************************************************/
/* */
/* create_databases.sql */
/* */
/*****************************************************************/
CREATE DATABASE dw
;
CREATE DATABASE source
运行create_databases.sql脚本的命令如下:
mysql> \. c:\mysql\scripts\create_databases.sql
在你的控制台界面上,你将看到:
Query OK, 1 row affected (0.00 sec)
Query OK, 1 row affected (0.00 sec)
你可以用show databases命令来确认上面的命令是否成功运行。 为了确认dw数据库被创建用下面命令:
mysql> show databases like ‘dw’;
回应信息将如下面所示:
+—————+
| Database (dw) |
+—————+
| dw |
+—————+
1 row in set (0.00 sec)
为了确认source数据库被成功创建用下面命令:
mysql> show databases like ‘source’;
回应信息将如下面所示:
+——————-+
| Database (source) |
+——————-+
| source |
+——————-+
1 row in set (0.00 sec)
创建数据仓库表
第二步是在dw数据库中创建数据仓库表。你可以用列表1-3所示的create_dw_tables.sql脚本来创建图1-1中的sales_order_fact表和四个维表。
列表1-3:创建数据仓库表
[sql] view plain copy
/*****************************************************************/
/* */
/* create_dw_tables.sql */
/* */
/*****************************************************************/
/* default to dw database */
USE dw;
/* creating customer_dim table */
CREATE TABLE customer_dim
( customer_sk INT NOT NULL AUTO_INCREMENT PRIMARY KEY
, customer_number INT
, customer_name CHAR (50)
, customer_street_address CHAR (50)
, customer_zip_code INT (5)
, customer_city CHAR (30)
, customer_state CHAR (2)
, effective_date DATE
, expiry_date DATE )
;
/* creating product_dim table */
CREATE TABLE product_dim
( product_sk INT NOT NULL AUTO_INCREMENT PRIMARY KEY
, product_code INT
, product_name CHAR (30)
, product_category CHAR (30)
, effective_date DATE
, expiry_date DATE )
;
/* creating order_dim table */
CREATE TABLE order_dim
( order_sk INT NOT NULL AUTO_INCREMENT PRIMARY KEY
, order_number INT
, effective_date DATE
, expiry_date DATE )
;
/* creating date_dim table */
CREATE TABLE date_dim
( date_sk INT NOT NULL AUTO_INCREMENT PRIMARY KEY
, date DATE
, month_name CHAR (9)
, month INT (1)
, quarter INT (1)
, year INT (4)
, effective_date DATE
, expiry_date DATE )
;
/* creating sales_order_fact_table */
CREATE TABLE sales_order_fact
( order_sk INT
, customer_sk INT
, product_sk INT
, order_date_sk INT
, order_amount DECIMAL (10, 2) )
;
现在运行create_dw_tables.sql脚本:
mysql> \. c:\mysql\scripts\create_dw_tables.sql
你的控制台将显示如下类似的信息:
Database changed
Query OK, 0 rows affected (0.13 sec)
Query OK, 0 rows affected (0.12 sec)
Query OK, 0 rows affected (0.12 sec)
Query OK, 0 rows affected (0.10 sec)
Query OK, 0 rows affected (0.11 sec)
你可以用show create table命令确认某个表是否被创建成功,例如,为了验证customer_dim表是否创建成功,用这个命令:
mysql> show create table customer_dim \G
在你的控制台界面上,将可以看到:
[sql] view plain copy
*************************** 1. row ***************************
Table: customer_dim
Create Table: CREATE TABLE ‘customer_dim’ (
‘customer_sk’ int(11) NOT NULL auto_increment,
‘customer_number’ int(11) default NULL,
‘customer_name’ char (50) default NULL,
‘customer_street_address’ char (50) default NULL,
‘customer_zip_code’ int (5) default NULL,
‘customer_city’ char (30) default NULL,
‘customer_state’ char (2) default NULL,
‘effective_date’ date default NULL,
‘expiry_date’ date default NULL,
PRIMARY KEY (‘customer_sk’)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
1 row in set (0.00 sec)
用相同的方式,可以确认其他表是否成功创建。
产生代理键
本节的最后一个任务是用列表1-4所示的customer_sk.sql脚本创建代理键,这个脚本将向customer_dim表插入3条记录。
列表 1-4: 产生 客户代理键值
[html] view plain copy
/******************************************************************/
/* */
/* customer_sk.sql */
/* */
/******************************************************************/
/* default to dw */
USE dw;
INSERT INTO customer_dim
( customer_sk
, customer_number
, customer_name
, customer_street_address
, customer_zip_code
, customer_city
, customer_state
, effective_date
, expiry_date )
VALUES
(NULL, 1, ‘Big Customers’, ‘7500 Louise Dr.’, ‘17050’,
‘Mechanicsburg’, ‘PA’, CURRENT_DATE, ‘9999-12-31’)
, (NULL, 2, ‘Small Stores’, ‘2500 Woodland St.’, ‘17055’,
‘Pittsburgh’, ‘PA’, CURRENT_DATE, ‘9999-12-31’)
, (NULL, 3, ‘Medium Retailers’, ‘1111 Ritter Rd.’, ‘17055’
‘Pittsburgh’, ‘PA’, CURRENT_DATE, ‘9999-12-31’)
;
/* end of script
运行customer_sk.sql脚本之前,你必须将你的mysql时间设置为:2007-02-01,这是因为customer_sk.sql脚本中的CURRENT_DATE函数用操作系统的时间作为载入effective_date字段的值,而我就是在这个时间运行customer_sk.sql脚本的。你可以通过将mysql运行的机器的系统的时间修改为特定的值来修改mysql时间。
注意要牢记,这里你改变mysql的时间只是为了能比较平滑结合这个学习教程,实际的生产环境中,你不能在运行脚本的前后改变你的数据库或者系统的时间。事实上,你应该在数据库仓库环境中安排部署你的各个脚本定期的运行。
如何定期安排将在第8节“定期载入”中讨论。
注意你的mysql监控端将在系统时间被修改后断开和mysql服务端的连接,为了确保你运行脚本不会有什么问题,在调整时间后,试着运行一下诸如“use dw”的简单的命令,你将得到一个错误的信息提示,但是你的监控端将重新连接上mysql。你可以再次运行“use dw”命令来确定已经连接上myslq服务器,这次,你不会再得到任何错误提示了。
现在,你已经将mysql的时间调整到2007-02-01,可以用如下的命令运行列表1-4中所示的脚本,
mysql> \. c:\mysql\scripts\customer_sk.sql
你将在你的控制台上看到如下信息:
Database changed
Query OK, 3 rows affected (0.06 sec)
Records: 3 Duplicates: 0 Warnings: 0
查询该表,你可以看到这个脚本已经准确的插入代理键值了。
mysql> select * from customer_dim \G
*************************** 1. row ***************************
customer_sk: 1
customer_number: 1
customer_name: Big Customers
customer_street_address: 7500 Louise Dr.
customer_zip_code: 17050
customer_city: Mechanicsburg
customer_state: PA
effective_date: 2007-02-01
expiry_date: 9999-12-31
*************************** 2. row ***************************
customer_sk: 2
customer_number: 2
customer_name: Small Stores
customer_street_address: 2500 Woodland St.
customer_zip_code: 17055
customer_city: Pittsburgh
customer_state: PA
effective_date: 2007-02-01
expiry_date: 9999-12-31
*************************** 3. row ***************************
customer_sk: 3
customer_number: 3
customer_name: Medium Retailers
customer_street_address: 1111 Ritter Rd.
customer_zip_code: 17055
customer_city: Pittsburgh
customer_state: PA
effective_date: 2007-02-01
expiry_date: 9999-12-31
3 rows in set (0.00 sec)
mysql>
现在运行列表1-5中的脚本more_customer_sk.sq来增加更多的行。
列表 1-5: 插入更多客户资料
[sql] view plain copy
/*****************************************************************/
/* */
/* more_customer_sk.sql */
/* */
/*****************************************************************/
USE dw;
INSERT INTO customer_dim (
customer_sk
, customer_number
, customer_name
, customer_street_address
, customer_zip_code
, customer_city
, customer_state
, effective_date
, expiry_date
)
VALUES
(NULL, 4, ‘Good Companies’, ‘9500 Scott St.’, ‘17050’,
‘Mechanicsburg’, ‘PA’, CURRENT_DATE, ‘9999-12-31’)
, (NULL, 5, ‘Wonderful Shops’, ‘3333 Rossmoyne Rd.’, ‘17050’,
‘Mechanicsburg’, ‘PA’, CURRENT_DATE, ‘9999-12-31’)
, (NULL, 6, ‘Loyal Clients’, ‘7070 Ritter Rd.’, ‘17055’,
‘Pittsburgh’, ‘PA’, CURRENT_DATE, ‘9999-12-31’)
;
下面说明如何运行more_customer_sk.sql 脚本
mysql> \. c:\mysql\scripts\more_customer_sk.sql
你将看到:
Database changed
Query OK, 3 rows affected (0.06 sec)
Records: 3 Duplicates: 0 Warnings: 0
如果你查询customer_dim表,将发现有6条记录在该表中:
[sql] view plain copy
mysql> select * from customer_dim \G
*************************** 1. row ***************************
customer_sk: 1
customer_number: 1
customer_name: Big Customers
customer_street_address: 7500 Louise Dr.
customer_zip_code: 17050
customer_city: Mechanicsburg
customer_state: PA
effective_date: 2007-02-01
expiry_date: 9999-12-31
*************************** 2. row ***************************
customer_sk: 2
customer_number: 2
customer_name: Small Stores
customer_street_address: 2500 Woodland St.
customer_zip_code: 17055
customer_city: Pittsburgh
customer_state: PA
effective_date: 2007-02-01
expiry_date: 9999-12-31
*************************** 3 row ***************************
customer_sk: 3
customer_number: 3
customer_name: Medium Retailers
customer_street_address: 1111 Ritter Rd.
customer_zip_code: 17055
customer_city: Pittsburgh
customer_state: PA
effective_date: 2007-02-01
expiry_date: 9999-12-31
*************************** 4. row ***************************
customer_sk: 4
customer_number: 4
customer_name: Good Companies
customer_street_address: 9500 Scott St.
customer_zip_code: 17050
customer_city: Mechanicsburg
customer_state: PA
effective_date: 2007-02-01
expiry_date: 9999-12-31
*************************** 5. row ***************************
customer_sk: 5
customer_number: 5
customer_name: Wonderful Shops
customer_street_address: 3333 Rossmoyne Rd.
customer_zip_code: 17050
customer_city: Mechanicsburg
customer_state: PA
effective_date: 2007-02-01
expiry_date: 9999-12-31
*************************** 6. row ***************************
customer_sk: 6
customer_number: 6
customer_name: Loyal Clients
customer_street_address: 7070 Ritter Rd.
customer_zip_code: 17055
customer_city: Pittsburgh
customer_state: PA
effective_date: 2007-02-01
expiry_date: 9999-12-31
6 rows in set (0.01 sec)
不要删除这些客户记录,在下个章节中,你将用到它们。
第4节 小结
这一个节,你学习了星型模式和代理键。你还建立两个数据库以及操作这两个数据库的mysql用户。下面的章节,你将使用和扩展这些数据库。
第二章 维的历史记录
存储在维表中得数值叫维成员。比如第1章的product_dim维表就存储关于产品的维成员。
很多维成员会随着时间而变化。客户改变地址,产品更名和重新规类,销售订单修正,等等。当一个维成员发生改变时,比如产品建立一个新的类别,你将必须维护维成员的历史记录。例如:一旦发生产品的重新归类,你必须维护该产品维的历史记录,在这个例子中,你必须在product_dim维表中存储该产品的以前的类别和现在新类别信息。另外,以往的订单记录中,产品类别将保持对应的旧类别。
缓慢变化维(SCD)是在维度数据仓库中记录维历史的技术。本章将教你如何用SCD技术维护维度的历史记录。你将学习不同的SCD脚本,并且用实验进行验证这些脚本是如何正确地维护维历史记录的。
缓慢变化维技术
有三种不同的处理缓慢变化维的技术:第一类缓慢变化维(SCD1),第二类缓慢变化维(SCD2),第三类缓慢变化维(SCD3)。SCD1更新维成员记录而不存储维成员的历史记录。SCD1用于直接校正错误的维成员。
SCD2维护维历史记录,当一个维成员改变时,将产生一个新版本的维成员。SCD2不删除或者改变现有的维成员记录。
SCD3只是保存一个版本的维成员记录。它使用多个字段来保存不同维成员值的方法来维护维的历史记录。比如记录客户地址,customer_dim维表就必须有customer_address字段和previous_customer_address字段来分别存储前后两次的地址信息。相比较SCD2维护全部历史记录而言,SCD3只能维护有限的历史记录。SCD3很少被应用。它只能用在那种对数据库空间有限制的场合,并且数据仓库的使用者可以接受有限的维成员历史记录。
注意 本书只涉及SCD1 和SCD2。
第一类缓慢变化维技术(SCD1)
如果你不需要维护维度变化历史,你可以用SCD1。当源数据改变,你更新相应维表中现有的数据记录。
可以举例子:在第1章中,我就应用SCD1为customer_dim表进行建表和载入数据。回顾一下,该表有表2-1所示的6条记录。
表格2-1 未发生改变的customer_dim table 表
customer _number
customer _name
customer _street _address
customer _zip _code
customer _city
customer _state
1
Big Customers
7500 Louise Dr.
17050
Mechanicsburg
PA
2
Small Stores
2500 Woodland St.
17055
Pittsburgh
PA
3
Medium Retailers
1111 Ritter Rd.
17055
Pittsburgh
PA
4
Good Companies
9500 Scott St
17050
Mechanicsburg
PA
5
Wonderful Shops
3333 Rossmoyne Rd.
17050
Mechanicsburg
PA
6
Loyal Clients
7070 Ritter Rd.
17055
Pittsburgh
PA
假设客户详细信息发生改变,现在源数据中,客户表的内容如表2-2所示:
表2-2 修正后的客户信息
customer _number
customer _name
customer _street _address
customer _zip _code
customer _city
customer _state
1
Really Large Customers
7500 Louise Dr.
17050
Mechanicsburg
PA
2
Small Stores
2500 Woodland St.
17055
Pittsburgh
PA
3
Medium Retailers
1111 Ritter Rd.
17055
Pittsburgh
PA
4
Good Companies
9500 Scott St.
17050
Mechanicsburg
PA
5
Wonderful Shops
3333 Rossmoyne Rd.
17050
Mechanicsburg
PA
6
Loyal Clients
7070 Ritter Rd.
17055
Pittsburgh
PA
7
Distinguished Partners
9999 Scott St.
17050
Mechanicsburg
PA
正如你所看到的,第一个客户的名称发生变化,并且多了一个编号是“7”的客户记录。
你可以运行列表2-1中所示的脚本将SCD1应用于数据仓库中的customer_dim表中,这里假设新增的客户信息已经被载入到中间(临时)表customer_stg。
列表2-1:在表customer_dim中对客户名称应用SCD1。
[sql] view plain copy
/ *****************************************************************/
/* */
/* scd1.sql */
/* */
/ *****************************************************************/
/* default database to dw */
USE dw;
/* update existing customers */
UPDATE customer_dim a, customer_stg b
SET a.customer_name = b.customer_name
WHERE a.customer_number = b.customer_number
AND a.expiry_date = ‘9999-12-31’
AND a.customer_name <> b.customer_name
;
/* add new customers */
INSERT INTO customer_dim
SELECT
NULL
, customer_number
, customer_name
, customer_street_address
, customer_zip_code
, customer_city
, customer_state
, CURRENT_DATE
, ‘9999-12-31’
FROM customer_stg
WHERE customer_number NOT IN (
SELECT b.customer_number
FROM customer_dim a, customer_stg b
WHERE a.customer_number = b.customer_number )
;
/* end of script */
列表2-1中的脚本包含两个SQL语句,一个更新语句和一个插入语句。更新语句将临时表的customer_name字段复制到customer_dim表的customer_name字段。插入语句则将临时表中有而customer_dim表中没有的记录插入到customer_dim表中。运行该脚本将在customer_dim表中更新临时表中的第一个客户的名称并插入临时表中第七个客户的信息。
尽管在你运行这个脚本文件之前我们还没有临时表,但是在customer.csv文件中我们有当前客户资料表(在source数据库中)的内容。因此,在customer_dim表中应用SCD1将需要两个步骤:
1. 建立临时表customer_stg,并将customer.csv文件中的记录载入该表。
2. 运行列表2-1中所示的的脚本。
这些步骤将在下面小节中说明。
建立、载入客户临时表
customer.csv文件包含当前的客户资料信息,可以在本书附带的Zip文件中找到它。其内容如下所示:
CUSTOMER NO
CUSTOMER NAME
STREET ADDRESS,
ZIP CODE
CITY
STATE
1
Really Large Customers
7500 Louise Dr.
17050
Mechanicsburg
PA
2
Small Stores
2500 Woodland St.
17055
Pittsburgh
PA
3
Medium Retailers
1111 Ritter Rd.
17055
Pittsburgh
PA
4
Good Companies
9500 Scott St.
17050
Mechanicsburg
PA
5
Wonderful Shops
3333 Rossmoyne Rd.
17050
Mechanicsburg
PA
6
Loyal Client
7070 Ritter Rd.
17055
Pittsburgh
PA
7
Distinguished Partners
9999 Scott St.
17050
Mechanicsburg
PA
列表2-2所示的脚本在数据仓库(dw)中建立customer_stg 表,并把customer.csv文件的信息载入表中。
列表 2-2: 建立并载入 customer_stg 表
[sql] view plain copy
/****************************************************************/
/* */
/* create_customer_stg.sql */
/* */
/****************************************************************/
/* default database to dw */
USE dw;
/* create customer_stg table */
CREATE TABLE customer_stg
(customer_number INT
customer_name CHAR (30)
customer_street_address CHAR (30)
customer_zip_code INT (5)
customer_city CHAR (30)
customer_state CHAR (2) )
;
/* clean up customer_stg table and load customer.csv */
TRUNCATE customer_stg;
LOAD DATA INFILE ‘customer.csv’
INTO TABLE customer_stg
FIELDS TERMINATED BY ‘,’
OPTIONALLY ENCLOSED BY “”
LINES TERMINATED BY ‘\r\n’
IGNORE 1 LINES
( customer_number
, customer_name
, customer_street_address
, customer_zip_code
, customer_city
, customer_state )
;
/* end of script
用以下命令运行列表2-2脚本:
MySQL> \. c:\mysql\scripts\create_customer_stg.sql
应用SCD1
现在你可以开始准备运行列表2-1中的SCD1脚本,在此之前,你需要将mysql数据库时间调整到2007-02-02(一个比你在第1章所设置的日期更后面的日期)。设置完日期后就可以运行该scd1.sql脚本了。
[sql] view plain copy
mysql> \. c:\mysql\scripts\scd1.sql
现在可以查询customer_dim表来确认列表2-1 是否以及成功运行了。
mysql> select * from customer_dim \G
你将会看到如下结果:
[sql] view plain copy
*************************** 1. row ***************************
customer_sk: 1
customer_number: 1
customer_name: Really Large Customers
customer_street_address: 7500 Louise Dr.
customer_zip_code: 17050
customer_city: Mechanicsburg
customer_state: PA
effective_date: 2007-02-01
expiry_date: 9999-12-31
*************************** 2. row ***************************
customer_sk: 2
customer_number: 2
customer_name: Small Stores
customer_street_address: 2500 Woodland St.
customer_zip_code: 17055
customer_city: Pittsburgh
customer_state: PA
effective_date: 2007-02-01
expiry_date: 9999-12-31
*************************** 3. row ***************************
customer_sk: 3
customer_number: 3
customer_name: Medium Retailers
customer_street_address: 1111 Ritter Rd.
customer_zip_code: 17055
customer_city: Pittsburgh
customer_state: PA
effective_date: 2007-02-01
expiry_date: 9999-12-31
*************************** 4. row ***************************
customer_sk: 4
customer_number: 4
customer_name: Good Companies
customer_street_address: 9500 Scott St.
customer_zip_code: 17050
customer_city: Mechanicsburg
customer_state: PA
effective_date: 2007-02-01
expiry_date: 9999-12-31
*************************** 5. row ***************************
customer_sk: 5
customer_number: 5
customer_name: Wonderful Shops
customer_street_address: 3333 Rossmoyne Rd.
customer_zip_code: 17050
customer_city: Mechanicsburg
customer_state: PA
effective_date: 2007-02-01
expiry_date: 9999-12-31
*************************** 6. row ***************************
customer_sk: 6
customer_number: 6
customer_name: Loyal Clients
customer_street_address: 7070 Ritter Rd.
customer_zip_code: 17055
customer_city: Pittsburgh
customer_state: PA
effective_date: 2007-02-01
expiry_date: 9999-12-31
*************************** 7. row ***************************
customer_sk: 7
customer_number: 7
customer_name: Distinguished Partners
customer_street_address: 9999 Scott St.
customer_zip_code: 17050
customer_city: Mechanicsburg
customer_state: PA
effective_date: 2007-02-02
expiry_date: 9999-12-31
7 rows in set (0.00 sec)
结果分析
上述查询的显示表明:
l 第一个客户的名称已经改为‘Really Large Customers’,这里只有第一个客户的名称,也就是说没有为该客户保留历史数据。前面六个客户的生效、失效日期并没有改变,只有第一个客户的名称改变了。
l 第7个客户的信息已经加入表中,它的生效日期是2007-02-02(你运行该脚本的日期)。
第二类缓慢变化维技术(SCD2)
我在第1章“基本组成”中讨论过代理键,但是到目前为止,还没有让你理解,为什么你一定需要一个代理键。你可能仍然感到困惑,为什么你需要另外建立一个键,你已经有一个来自源数据的键了(也称为自然键).很快你就会明白,你可以用代理键(结合生效、失效日期)来应用SCD2以维护维成员的历史记录。
你无法用自然键来应用SCD2。比如产品维的自然键,存储在product_code字段中,应用SCD2的时候,如果产品名称改变了,为了维护历史记录,你必须保持原来的记录,并用新的名称来增加新的记录。然而,你不可以有两个记录而用一个相同的键,就是product_code产品编号。这就是为什么你需要一个代理键。
用生效、失效日期来定义一个记录是否有效。当需要对一个现有的维成员记录增加新的版本时,你可以对原有记录设置一个早于新记录生效日期的失效日期来使它失效。例如,假如新记录的生效日期是2008-01-19, 你可以设置原有记录的失效日期为2008-01-18。同时你可以设置新记录的失效期为9999–12–31,相反,SCD1不需要用到生效、失效日期。
列表2-3 的脚本对product_dim表应用SCD2。同理,不管是product_name或者product_category字段发生变化,SCD2使得原有的记录失效,并且增加新的记录来描述同一个产品。注意这里列表2-3中的脚本假设新的产品信息已经存在名为product_stg的临时表。
列表2-3:应用SCD2到product_dim 表的product_name 和 product_category 字段。
[sql] view plain copy
/*****************************************************************/
/* */
/* scd2.sql */
/* */
/*****************************************************************/
/* default database to dw */
USE dw;
/* expire the existing product */
UPDATE
product_dim a
, product_stg b
SET
expiry_date = SUBDATE (CURRENT_DATE, 1)
WHERE
a.product_code = b.product_code
AND ( a.product_name <> b.product_name
OR a.product_category <> b.product_category )
AND expiry_date = ‘9999-12-31’
;
/* add a new row for the changing product */
INSERT INTO product_dim
SELECT
NULL
, b.product_code
, b.product_name
, b.product_category
, CURRENT_DATE
, ‘9999-12-31’
FROM
product_dim a
, product_stg b
WHERE
a.product_code = b.product_code
AND ( a.product_name <> b.product_name
OR a.product_category <> b.product_category )
AND EXISTS
( SELECT * FROM product_dim x
WHERE b.product_code = x.product_code
AND a.expiry_date = SUBDATE (CURRENT_DATE, 1)
AND NOT EXISTS
( SELECT *
FROM product_dim y
WHERE b.product_code = y.product_code
AND y.expiry_date = ‘9999-12-31’ )
;
/* add new product */
INSERT INTO product_dim
SELECT
NULL
, product_code
, product_name
, product_category
, CURRENT_DATE
, ‘9999-12-31’
FROM product_stg
WHERE product_code NOT IN(
SELECT y.product_code
FROM product_dim x, product_stg y
WHERE x.product_code = y.product_code )
;
/* end of script
注意 在实际生产环境中,你将计划调度该脚本,在数据仓库的载入循环周期中,定期运行该脚本。(定期载入会在第8章“定期载入”中讨论)。
在你运行列表2-3之前,首先你需要先准备数据然后建立product_stg表。实际上,这是两个准备阶段。第一个阶段包含三步:
1. 准备产品信息源数据文件。
2. 通过运行create_product_stg.sql脚本(列表2-4 )和load_product_stg.sql脚本(列表2-5),建立product_stg表并且载入产品信息源文件。
3. 运行scd2.sql脚本,初始化product_dim表,载入表数据。
第二个阶段包含2步:
1. 改变产品信息源文件,并把数据载入到临时表中。
2. 再次运行scd2.sql 脚本来实施SCD2,并确认SCD2已经被有效的应用了。
所有步骤在接下来的小节中讨论。
准备产品信息源数据文件
你将从名为product.txt的文件中导入两个产品信息到product_dim表中,product.txt是固定宽度格式的文件,包含在本书随书附带的zip文件中。以下是product.txt文件的内容。
产品编号 产品名称 产品分组
1 Hard Disk Storage
2 Floppy Drive Storage
你需要做的是将该文件拷贝到c:\MySQL\data\dw目录中。事实上,你需要拷贝所有的特定格式的文件到该目录下面。
建立临时表及载入数据
运行列表2-4中的create_product_stg.sql脚本来建立product_stg表。
列表 2.4: 建立 product_stg 表
[sql] view plain copy
/*****************************************************************/
/* */
/* create_product_stg.sql */
/* */
/*****************************************************************/
/* default database to dw */
USE dw;
CREATE TABLE product_stg
( product_code INT
, product_name CHAR (30)
, product_category CHAR (30))
;
/* end of script
现在运行create_product_stgsql脚本:
mysql> \. c:\mysql\scripts\create_product_stg.sql
接下来用mysql的LOAD DATA INFILE命令,将特定格式的文本文件内容载入到mysql数据库的表中。列表2-5中的load_product_stg.sql脚本将product.txt文件内容载入到product_stg表中。
列表 2-5: 导入产品信息到临时表中
[sql] view plain copy
/*****************************************************************/
/* */
/* load_product_stg.sql */
/* */
/*****************************************************************/
/* default database to dw */
USE dw;
/* clean up the staging table */
TRUNCATE product_stg;
/* use LOAD DATA INFILE */
LOAD DATA INFILE ‘product.txt’
INTO TABLE product_stg
FIELDS TERMINATED BY ”
OPTIONALLY ENCLOSED BY ”
LINES TERMINATED BY ‘\r\n’
IGNORE 1 LINES
( product_code
, product_name
, product_category )
;
/* end of script
用下面的命令格式运行列表2-5中的脚本:
mysql> \. c:\mysql\scripts\load_product_stg.sql
响应信息将如下所示:
Database changed
Query OK, 1 row affected (0.09 sec)
Query OK, 2 rows affected (0.09 sec)
Records: 2 Deleted: 0 Skipped: 0 Warnings: 0
接下来查询product_stg表,确认已经成功载入两个产品信息。
mysql> select * from product_stg;
product_stg表包含内容将如下所示:
+—————-+—————+——————-+
| product_code | product_name | product_category |
+—————-+—————+——————-+
| 1 | Hard Disk | Storage |
| 2 | Floppy Drive | Storage |
+—————-+—————+——————-+
2 rows in set (0.00 sec)
执行初始化导入
将系统时间设置为2007-02-03,然后运行列表2-3中的scd2.sql脚本向product_dim表初始化载入两个产品信息。
mysql> \. c:\mysql\scripts\scd2.sql
在你的控制台界面上将显示:
Database changed
Query OK, 0 rows affected (0.16 sec)
Rows matched: 0 Changed: 0 Warnings: 0
Query OK, 0 rows affected (0.02 sec)
Records: 0 Duplicates: 0 Warnings: 0
Query OK, 2 rows affected (0.06 sec)
Records: 2 Duplicates: 0 Warnings: 0
查询product_dim表确认已经成功载入两个产品信息。
mysql> select * from product_dim \G
在你的控制台界面上将显示:
*************************** 1. row ***************************
product_sk: 1
product_code: 1
product_name: Hard Disk
product_category: Storage
effective_date: 2007-02-03
expiry_date: 9999-12-31
*************************** 2 row ***************************
product_sk: 2
product_code: 2
product_name: Floppy Drive
product_category: Storage
effective_date: 2007-02-03
expiry_date: 9999-12-31
2 rows in set (0.00 sec)
改变产品信息源文件
接下来用文本编辑器改变刚才用到的product.txt文件,你需要改变编号1的产品名称为“Hard Disk Drive”,并新增编号3的产品信息。
PRODUCT CODE PRODUCT NAME PRODUCT GROUP
1 Hard Disk Drive Storage
2 Floppy Drive Storage
3 LCD Panel Monitor
应用SCD2
设置mysql的日期为2007-02-05。模拟一个比刚才初始化载入的日期(2007-02-03)更晚的日期是必要的。
现在多次运行列表2-5所示的load_product_stg.sql脚本,然后运行列表2-3中的scd2.sql。
mysql> \. c:\mysql\scripts\load_product_stg.sql
在你的控制台界面上将显示:
Database changed
Query OK, 2 rows affected (0.11 sec)
Query OK, 3 rows affected (0.06 sec)
Records: 3 Deleted: 0 Skipped: 0 Warnings: 0
mysql> \. c:\mysql\scripts\scd2.sql
Database changed
Query OK, 1 row affected (0.43 sec)
Rows matched: 1 Changed: 1 Warnings: 0
Query OK, 1 row affected (0.08 sec)
Records: 1 Duplicates: 0 Warnings: 0
Query OK, 1 row affected (0.05 sec)
Records: 1 Duplicates: 0 Warnings: 0
为了确保SCD2已经实施正常,查询product_dim表
mysql> select * from product_dim \G
*************************** 1. row ***************************
product_sk: 1
product_code: 1
product_name: Hard Disk
product_category: Storage
effective_date: 2007-02-03
expiry_date: 2007-02-04
*************************** 2. row ***************************
product_sk: 2
product_code: 2
product_name: Floppy Drive
product_category: Storage
effective_date: 2007-02-03
expiry_date: 9999-12-31
*************************** 3. row ***************************
product_sk: 3
product_code: 1
product_name: Hard Disk Drive
product_category: Storage
effective_date: 2007-02-05
expiry_date: 9999-12-31
*************************** 4. row ***************************
product_sk: 4
product_code: 3
product_name: LCD Panel
product_category: Monitor
effective_date: 2007-02-05
expiry_date: 9999-12-31
4 rows in set (0.00 sec)
结果分析
下面对product_dim的分析表明SCD2已经正确的实施。
l 产品1有两条记录,其中一条记录(product_sk字段值是1)已经过期失效,其失效日期是2007-02-04,这是一个早于你实施SCD2的日期(2007-02-05)。另外一条记录(product_sk字段值是3)有一个新的名称。它的生效日期是2007-02-05,失效日期是9999–12–31,也就是说它仍未失效。
l 编号是3的新产品是已经被加入,它的生效日期是2007-02-05。
小结
本章你学习到什么是维成员和维成员的历史记录,以及为什么你需要维护维成员的历史记录。你学习了SCD(缓慢变化维)技术和分别对客户维和产品维实施SCD1和SCD2。
在第7章的“初始化载入”和第8章的“定期载入”你开始相应的应用这些技术在你的数据仓库的初始化载入和定期载入中。然而在这之前你还要在接下来的章节中学习一个事实表的重要特性(度量的可加性)。
第3章
度量可加性
一个度量总是一个可计算的值。事实表关于度量最重要的特性之一就是度量的可加性。从度量的可加性看,度量可以分为完全可加或者半可加。如果你在各种情况下都可以累计一个度量的值,这个度量就是完全可加的。如果你只能在部分情况下累加它的值,则该度量是半可加的。理解度量可加性的关键是在事实表中选取一个数据项作为度量。例如,销售订单事实表sales_order_fact的订单金额order_amount是完全可加的,因为你可以沿着它的任何一个或者全部维正确的累加它。就是说,你可以在任何时间,为任何产品,任何客户以及任何订单类型对订单金额order_amount度量进行累加处理。
本章主要介绍度量的全可加性,度量的半可加性将在24章”累积度量”进行解释。
完全可加度量
如果一个度量沿着任何一个维累加,它所有值的总和与沿着任何其他的维或者任何一些(包括所有)维的组合进行累加的总和一样,则该度量是完全可加的。
本小节用事实表sales_order_fact的订单金额order_amount度量对完全可加性进行说明。我将通过沿着所有维和其他维的组合进行查询事实表sales_order_fact,来说明这个度量是完全可加的。你可以用这种查询方式对任何度量进行测试。
你可以用列表3-1的脚本来添加数据记录。该脚本往order_dim表插入10条记录,往date_dim表插入1条记录,往sales_order_fact插入10条记录。
列表3-1: 为证明完全可加度量插入数据
[sql] view plain copy
/*****************************************************************/
/* */
/* additive_data.sql */
/* */
/*****************************************************************/
USE dw;
INSERT INTO order_dim VALUES
(NULL, 1, CURRENT_DATE, ‘9999-12-31’)
, (NULL, 2, CURRENT_DATE, ‘9999-12-31’)
, (NULL, 3, CURRENT_DATE, ‘9999-12-31’)
, (NULL, 4, CURRENT_DATE, ‘9999-12-31’)
, (NULL, 5, CURRENT_DATE, ‘9999-12-31’)
, (NULL, 6, CURRENT_DATE, ‘9999-12-31’)
, (NULL, 7, CURRENT_DATE, ‘9999-12-31’)
, (NULL, 8, CURRENT_DATE, ‘9999-12-31’)
, (NULL, 9, CURRENT_DATE, ‘9999-12-31’)
, (NULL, 10, CURRENT_DATE, ‘9999-12-31’)
INSERT INTO date_dim VALUES
(NULL, ‘2005-10-31’, ‘October’, 10, 4, 2005, CURRENT_DATE,
‘9999-12-31’)
;
INSERT INTO sales_order_fact VALUES
(1, 1, 2, 1, 1000)
, (2, 2, 3, 1, 1000)
, (3, 3, 4, 1, 4000)
, (4, 4, 2, 1, 4000)
, (5, 5, 3, 1, 6000)
, (6, 1, 4, 1, 6000)
, (7, 2, 2, 1, 8000)
, (8, 3, 3, 1, 8000)
, (9, 4, 4, 1, 10000)
, (10, 5, 2, 1, 10000)
;
/* end of script
*/
在你的MySQL控制终端上键入如下所示的该脚本的全路径名称
,来运行该脚本:
mysql> \. c:\mysql\scripts\additive_data.sql
回车后,在你的控制台上将显示如下信息:
Database changed
Query OK, 10 rows affected (0.26 sec)
Records: 10 Duplicates: 0 Warnings: 0
Query OK, 1 row affected (0.09 sec)
Query OK, 10 rows affected (0.11 sec)
Records: 10 Duplicates: 0 Warnings: 0
测试完全可加性
如果所有的查询结果都是一样的,则订单金额的度量是完全可加的。为了验证订单金额的完全可加性,我们使用列表3-2,3-3,3-4,3-5中所列的查询。我们将验证,所有这四种的查询都将得到一个值为58,000的订单总额。
第一个查询,列表3-2中的across_all_dimensions.sql 脚本,沿着所有的维对order_amounts字段累加求和(也就是选择所有的维,对order_amounts值累加)。
列表3-2 沿着所有的维查询
[sql] view plain copy
/*****************************************************************/
/* */
/* across_all_dimensions.sql */
/* */
/*****************************************************************/
USE dw;
SELECT SUM (order_amount) sum_of_order_amount
FROM sales order fact a
;
/* end of script <span style=”font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);”> */</span>
用如下命令运行该脚本。
mysql> \. c:\mysql\scripts\across_all_dimensions.sql
你将看到:
Database changed
+———————+
| sum_of_order_amount |
+———————+
| 58000.00 |
+———————+
1 row in set (0.04 sec)
第二个查询,是列表3-3所示的across_date_product_order.sql 脚本。沿着日期,产品,订单类型维对order_amount值进行累加(只选择客户维累加order_amount值)。
列表3-3:沿着时间,产品,订单类型维查询。
[sql] view plain copy
/**********************************************************************/
/* */
/* across_date_product_order.sql */
/* */
/**********************************************************************/
USE dw;
SELECT
customer_number
, SUM (order_amount) sum_of_order_amount
FROM
sales_order_fact a
, customer_dim b
WHERE
a.customer_sk = b.customer_sk
GROUP BY
customer_number
;
/* end of script
用如下命令运行列表3-3脚本。
mysql> \. c:\mysql\scripts\across_date_product.sql
结果如下所示:
The result is as follows.
Database changed
+———————–+—————————-+
| customer_number | sum_of_order_amount |
+———————–+—————————+
| 1 | 7000.00 |
| 2 | 9000.00 |
| 3 | 12000.00 |
| 4 | 14000.00 |
| 5 | 16000.00 |
+———————–+—————————-+
5 rows in set (0.10 sec)
所有订单总计累加的总额是:7,000+9,000+12,000+14,000+16,000=58,000.
第三个查询,是列表3-4所列的across_date_customer_order.sql脚本,它将沿着日期,客户,订单维对订单值进行累加求和。
列表3-4 沿着日期,客户,订单维的查询
[sql] view plain copy
/*****************************************************************/
/* */
/* across_date_customer_order.sql */
/* */
/*****************************************************************/
USE dw;
SELECT
product_code
, SUM (order_amount) sum_of_order_amount
FROM
sales_order_fact a
, product_dim b
WHERE
a.product_sk = b.product_sk
GROUP BY
product_code
;
/* end of script */
你可以用如下命令运行该脚本:
mysql> \. c:\mysql\scripts\across_date_customer.sql
结果如下:
Database changed
+—————+———————-+
| product_code | sum_of_order_amount |
+——————–+——————————+
| 1 | 15000.00 |
| 2 | 23000.00 |
| 3 | 20000.00 |
+——————–+——————————+
3 rows in set (0.09 sec)
同样的,这个查询得到了订单金额的累加总和是58,000 (15,000+23,000+20,000).
第四个查询,是列表3-5所列的across_date_order.sql脚本,沿着日期,订单维对订单总额进行累加求和。
列表3-5 沿着日期,订单维的查询
[sql] view plain copy
/*****************************************************************/
/* */
/* across_date_order.sql */
/* */
/*****************************************************************/
USE dw;
SELECT
customer_number
, product_code
, SUM (order_amount) sum_of_order_amount
FROM
sales_order_fact a
, customer_dim b
, product_dim c
WHERE
a.customer_sk = b.customer_sk
AND a.product_sk = c.product_sk
GROUP BY
customer_number
, product_code
/* end of script
用如下命令运行该脚本
mysql> \. c:\mysql\scripts\across_date.sql
你将在你的控制台界面上看到:
Database changed
+———————–+——————–+—————————+
| customer_number | product_code | sum_of_order_amount |
+———————–+——————-+—————————-+
| 1 | 2 | 1000.00 |
| 1 | 3 | 6000.00 |
| 2 | 1 | 1000.00 |
| 2 | 2 | 8000.00 |
| 3 | 1 | 8000.00 |
| 3 | 3 | 4000.00 |
| 4 | 2 | 4000.00 |
| 4 | 3 | 10000.00 |
| 5 | 1 | 6000.00 |
| 5 | 2 | 10000.00 |
+———————–+——————-+—————————-+
10 rows in set (0.03 sec)
累加求和的总值再次等于:
58,000 (1,000+6,000+1,000+8,000+8,000+4,000+4,000+10,000+6,000+10,000).
所有四个查询得到相同的总计值(58,000),可以确定,该度量是完全可加的。
小结
本章你学习了度量的可加性。也证明了sales_order_fact 表中order_amount度量是完全可加的。
第四章 维度查询
维度查询是指在数据仓库中,通过一个或者多个代理键将事实表和它的维度表进行关联的查询。本章将教你维度查询的模式,以及以及如何应用其最常见的三种查询类型:聚集,特定和翻转。
聚集查询将个体的事实进行合计,比如,通过累加度量值。在特定查询中,你通过指定维度值来查询事实表。虽然大部分的查询会指定一个或者多个维度的值作为查询标准(约束),翻转查询的约束则是度量值。理解了这三种最基本的查询类型,你也将能应用其他类型的维度查询模式。
应用维度查询
本节我将向你解释如何应用维度查询中最基本的三种查询类型:聚集,特定和翻转。
为了应用维度查询,你首先必须运行列表4-1中的脚本以向你的数据仓库增加数据记录。你需要这些新增的数据来测试脚本4-2――4-7中的维度查询。
列表4-1 为测试维度查询增加数据的脚本:
[sql] view plain copy
/*****************************************************************/
/* */
/* dimensional_query_data.sql */
/* */
/*****************************************************************/
USE dw;
INSERT INTO order dim VALUES
(NULL, 11, CURRENT_DATE, ‘9999-12-31’)
, (NULL, 12, CURRENT_DATE, ‘9999-12-31’)
, (NULL, 13, CURRENT_DATE, ‘9999-12-31’)
, (NULL, 14, CURRENT_DATE, ‘9999-12-31’)
, (NULL, 15, CURRENT_DATE, ‘9999-12-31’)
, (NULL, 16, CURRENT_DATE, ‘9999-12-31’)
;
INSERT INTO date_dim VALUES
(NULL, ‘20075-0211-016’, ‘FebruaryNovember’, 112, 41, 20057,
CURRENT_DATE, ‘9999-12-31’)
;
INSERT INTO sales_order_fact VALUES
(11, 1, 2, 2, 20000)
, (12, 2, 3, 2, 25000)
, (13, 3, 4, 2, 30000)
, (14, 4, 2, 2, 35000)
, (15, 5, 3, 2, 40000)
, (16, 1, 4, 2, 45000)
;
/* end of script
在你开始之前,改变你的MySQL数据库日期为2007-02-06;然后运行列表4-1 的脚本插入6条记录到order_dim表,一个日期记录到date_dim表,6个订单记录到sales_order_fact表。
mysql> \. c:\mysql\scripts\dimensional_query_data.sql
你将在mysql控制台上看到:
Database changed
Query OK, 6 rows affected (0.05 sec)
Records: 6 Duplicates: 0 Warnings: 0
Query OK, 1 row affected (0.06 sec)
Query OK, 6 rows affected (0.06 sec)
Records: 6 Duplicates: 0 Warnings: 0
现在你已经有了必需的数据,你已经可以准备应用之前提及的三种维度查询类型。
聚集查询
聚集查询对个体的事实进行总结(聚集)。最典型的是累加度量值,甚至count(计数)也是一种常见的聚集。本节将讨论两个示例。
日营业统计
列表4-2所列的维度查询给出日营业情况的总结。订单金额和数量将按日期(每天)进行合计。注意,sales_prder_fact 表和 date_dim 表间的连接将通过它们的代理键。
列表4-2 日合计
[sql] view plain copy
/*****************************************************************/
/* */
/* daily_aggregation.sql */
/* */
/*****************************************************************/
SELECT
date
, SUM (order_amount)
, COUNT(*)
FROM
sales_order_fact a
, date_dim b
WHERE
a.order_date_sk = b.date_sk
GROUP BY date
ORDER BY date
;
/* end of script
用如下形式运行该查询
mysql> \. c:\mysql\scripts\daily_aggregation.sql
下面是查询的结果显示
+—————-+——————–+———–+
| date | SUM (order_amount) | COUNT(*) |
+—————-+——————–+———–+
| 2007-02-05 | 58000.00 | 10 |
| 2007-02-06 | 195000.00 | 6 |
+—————-+——————–+———–+
2 rows in set (0.03 sec)
该结果显示了每日所有订单总额(累加)和总数(计数)的值。
年度统计
列表4-3将给出年度营业情况总结。订单总额和订单总数不仅根据日期还根据产品类型及客户城市进行汇总。在事实表和对应三个维表(日期,产品,客户维)之间的三个关联也是通过代理键。
列表4-3:年度汇总
[sql] view plain copy
/*****************************************************************/
/* */
/* annual_aggregation.sql */
/* */
/*****************************************************************/
SELECT year, product_name, customer_city, SUM (order_amount),
COUNT(*)
FROM
sales_order_fact a
, date_dim b
, product_dim c
, customer_dim d
WHERE
a.order_date_sk = b.date_sk
AND a.product_sk = c.product_sk
AND a.customer_sk = d.customer_sk
GROUP BY year, product_name, customer_city
ORDER BY year, product_name, customer_city
;
/* end of script
按下面所示运行该脚本
mysql> \. c:\mysql\scripts\annual_aggregation.sql
结果显示如下:
+——+—————–+—————+—————+———+
| year | product_name | customer_city | SUM |COUNT(*) |
| | | |(order_amount) | |
+——+—————–+—————+—————+———+
| 2007 | Floppy Drive | Mechanicsburg | 70000.00 | 5 |
| 2007 | Floppy Drive | Pittsburgh | 8000.00 | 1 |
| 2007 | Hard Disk Drive | Mechanicsburg | 46000.00 | 2 |
| 2007 | Hard Disk Drive | Pittsburgh | 34000.00 | 3 |
| 2007 | LCD Panel | Mechanicsburg | 61000.00 | 3 |
| 2007 | LCD Panel | Pittsburgh | 34000.00 | 2 |
+——+—————–+—————+—————+———+
6 rows in set (0.03 sec)
该查询通过年,产品,和城市进行分组统计,给出了订单总额(累加)和订单总数(计数)的汇总值。
特定查询
特定查询基于某个特定的维度值对事实进行挑选和聚集。下面将给出两个例子关于特定查询类型的维度查询。
每月存储类商品营业情况
列表4-4所示的monthly_storage.sql脚本汇总每个月销售额和订单数。
列表4-4:特定查询(每月存储类商品营业情况)
[sql] view plain copy
/*****************************************************************/
/* */
/* monthly_storage.sql */
/* */
/*****************************************************************/
USE dw;
SELECT
product_name
, month_name
, year
, SUM (order_amount)
, COUNT(*)
FROM
sales_order_fact a
, product_dim b
, date_dim c
WHERE
a.product_sk = b.product_sk
AND a.order_date_sk = c.date_sk
GROUP BY
product_name
, product_category
, month_name
, year
HAVING product_category = ‘Storage’
ORDER BY
year
, month name
;
/* end of script
用以下命令运行该脚本:
mysql> \. c:\mysql\scripts\monthly_storage.sql
结果显示如下:
Database changed
+—————–+————+——+——————+———+
| product_name | month_name | year | SUM(order_amount)| COUNT(*)|
+—————–+————+——+——————+———+
| Hard Disk Drive | February | 2007 | 65000.00 | 2 |
| Floppy Drive | February | 2007 | 55000.00 | 2 |
| Hard Disk Drive | February | 2007 | 15000.00 | 3 |
| Floppy Drive | February | 2007 | 23000.00 | 4 |
+—————–+————+——+——————+———+
4 rows in set (0.00 sec)
该结果显示了通过将每个存储类产品分组统计,每月的订单金额(累加)和订单总数(计数)的汇总值。
每季度Mechanisburg城市的营业情况
列表4-5的查询是另一个特定查询。它给出了每个季度在Mechanisburg城市发生的订单总数。
列表4-5:特定查询(每季度Mechanisburg城市的营业情况)
[sql] view plain copy
/*****************************************************************/
/* */
/* quarterly_mechanicsburg.sql */
/* */
/*****************************************************************/
USE dw;
SELECT
customer_city
, quarter
, year
, SUM (order_amount)
, COUNT (order_sk)
FROM
sales_order_fact a
, customer_dim b
, date_dim c
WHERE
a.customer_sk = b.customer_sk
AND a.order_date_sk = c.date_sk
GROUP BY
customer_city
, quarter
, year
HAVING customer_city = ‘Mechanicsburg’
ORDER BY
year
, quarter;
/* end of script
以如下命令运行该脚本:
mysql> \. c:\mysql\scripts\quarterly_mechanicsburg.sql
结果显示如下:
Database changed
+—————+———+——+—————–+—————+
| customer_city | quarter | year |SUM(order_amount)|COUNT(order_sk)|
+—————+———+——+—————–+—————+
| Mechanicsburg | 4 | 2007 | 177000.00 | 10 |
+—————+———+——+—————–+—————+
1 row in set (0.00 sec)
该结果显示了每个季度的Mechanisburg城市的订单金额(累加)和订单总数(计数)的汇总值。
翻转查询
上述的查询都是有维度值约束的(基于维度选择事实)。翻转的维度查询是基于一个或者多个度量值来选择事实的。换句话说,你的查询将从事实表(星型模式的中心)到维表,这就是为什么会称为:翻转查询了。接下来的两个例子就是翻转查询类型的维度查询。
明星产品
列表4-6给出的的维查询给出了包含月销售总额大于(等于)75,000的产品的销售订单。
列表4-6:翻转查询-每月明星产品
[sql] view plain copy
/*****************************************************************/
/* */
/* monthly_product_performer.sql */
/* */
/*****************************************************************/
USE dw;
SELECT
month_name
, year
, product_name
, SUM (order_amount)
, COUNT(*)
FROM
sales_order_fact a
, product_dim b
, date_dim c
WHERE
a.product_sk = b.product_sk
AND a.order_date_sk = c.date_sk
GROUP BY
month_name
, year
, product_name
HAVING SUM (order_amount) >= 75000
ORDER BY
month
, year
, product_name
;
/* end of script
用以下命令运行该脚本:
MySQL> \. c:\mysql\scripts\monthly_product_performer.sql
查询结果如下:
Database changed
+————+——+————+——————-+———-+
| month_name | year |product_name| SUM (order_amount)| COUNT(*) |
+————+——+————+——————-+———-+
| November | 2007 | LCD Panel | 75000.00| 2 |
+————+——+————+——————-+———-+
1 row in set (0.00 sec)
忠实客户
列表4-7是比列表4-6更为复杂的翻转查询。如果你的用户需要知道在每年的后面7个月(6-12月)中,哪些客户每年有3以上的订单,你可以利用列表4-7中的脚本来获得结果。该查询说明,既便是这么复杂的一个查询,你仍然可以用维度查询。(译者注:原文中该说明似乎和脚本不一致,上面这段文字根据脚本翻译)。
列表4-7:翻转(忠实客户)
[sql] view plain copy
/*****************************************************************/
/* */
/* loyal_customer.sql */
/* */
/*****************************************************************/
USE dw;
SELECT
customer_number
, year
, COUNT(*)
FROM
sales_order_fact a
, customer_dim b
, date_dim c
WHERE
a.customer_sk = b.customer_sk
AND a.order_date_sk = c.date_sk
GROUP BY
customer_number
, year
HAVING
COUNT(*) > 3
AND (12 – MONTH (MAX (date))) < 7
;
/* end of script */
以下面命令运行该脚本:
mysql> \. c:\mysql\scripts\loyal_customer.sql
结果显示如下:
Database changed
+—————–+——+———-+
| customer_number | year | COUNT(*) |
+—————–+——+———-+
| 1 | 2007 | 4 |
+—————–+——+———-+
1 row in set (0.02 sec)
该结果显示了客户编号为1的客户的订单数(计数),该客户是唯一满足上述选择约束条件的客户。
小结
本章你学习和应用了维度查询,即总是用代理键进行关联的查询。另外,你将在接下来的章节中用到维度查询更复杂的查询形式。
第二篇 提取、转换和加载
章节目录
第5章:源数据提取
第6章:导入时间维
第7章:初始化导入
第8章:定期导入
第9章:周期导入计划
本篇概述
在这部分,第二篇,讨论如何装载一个多维数据仓库的过程。该过程被称为:ETL,提取、转换和加载的简写。提取就是从数据源获得数据仓库需要的数据。转换是预处理数据的过程。而载入是在数据仓库中存储数据的过程。
E,T和L并不总是相互独立的3个步骤。比如,如果源数据在MySQL数据库中,那ETL过程将只是“insert select”的SQL语句。而在其他情况下,这个转换过程可能是非常麻烦的,不仅需要增加代理键,维护历史记录,而且集成多种数据源,处理源数据的异常,以及源数据的汇聚等。
本篇包括以下几个主题:
l 源数据提取
l 装载日期维
l 初始化装载
l 定期装载
l 定期调度和计划
第5章 源数据提取
概述
装载数据仓库的第一个步骤是提取源数据。你既可以通过从数据源取得(拉动)数据还可以通过要求数据源发送(推动)数据到数据仓库中。
源数据的数量和有效性是提取数据的重要考虑因数,这取决于你是全部提取,还是只是提取最近一次提取后发生变化的数据(增量提取)。
本章讨论以下两个源数据提取的主题:
l 哪部分源数据需要提取并载入到数据仓库中?通常有两种方法:全部数据和变化数据的捕获。
l 源数据提取方法。有两种可能的模式,拉取模式(数据仓库拉取)和推送模式(由数据源推送)。
接下来的章节将说明上面提及的不同的数据源抽取模式,然后用销售订单数据的抽取来作为例子,展示如何用“源数据推送”的模式来改变抽取机制。
全部源数据还是变化数据捕获(CDC)
如果数据的量可以承受的情况下你通常可以抽取全部的数据(文件的所有记录或者数据库表的所有行)。该模式一般适合参数型数据的抽取,比如邮政编码。参数型数据通常是维表的数据源。
如果数据源的量是巨大的,并且提取所有数据是效率很低或者更本不可能的,你将不得不只提取改变过的数据(最后一次提取后新增或者发生变化的数据)。这种数据提取模式叫做变化数据捕获(CDC)。该模式通常用于提取操作事务型数据,比如销售订单。
接下来的章节我将解释如何应用“数据源推送”的CDC模式进行销售订单数据的提取。第8章“定期装载“将涵盖另外的模式。
拉取数据还是由源推送
如果你希望源数据只能等待数据仓库来提取数据,可以用拉取模式。然而你必须确定,当你的数据仓库开始提取数据的时候,源数据已经准备好并且可以使用。当你在第2章“维度历史“中运行scd1.sql脚本和scd2.sql脚本时,就是在使用这种拉取模式。
如果你的数据提取时间点是苛刻的,并希望源数据准备好后能及时发送给你,则你可以使用由源推送的数据提取模式。一个让你不得不用这种模式的因素是:数据源受保护且不允许存取。
由源推送的CDC模式运用于销售订单数据提取
我将在本节演示如何对销售订单数据源提取应用由源推送的CDC模式。由源推送的CDC模式意味着源数据只有在最后一次提取后发生改变才进行数据提取。这里的销售订单源数据,源系统指的是第1章“基本组成“中建立的source数据库。
列表5-1中的脚本是一个存储过程,它将从source数据库中的sales_order表提取销售订单数据。该脚本每天捕获这个表的变化(这里假设你以天为周期向数据仓库载入数据)。
列表5-1:推送CDC 销售订单
[sql] view plain copy
/*****************************************************************/
/* */
/* push_sales_order.sql */
/* */
/*****************************************************************/
/* point to source database */
USE source;
DELIMITER // ;
DROP PROCEDURE IF EXISTS push_sales_order //
CREATE PROCEDURE push_sales_order()
BEGIN
INSERT INTO dw.sales_order_fact
SELECT
a.order_amount
, b.order_sk
, c.customer_sk
, d.product_sk
, e.date_sk
FROM
sales_order a
, dw.order_dim b
, dw.customer_dim c
, dw.product_dim d
, dw.date_dim e
WHERE
a.entry_date = CURRENT_DATE
AND a.order_number = b.order_number
AND a.customer_number = c.customer_number
AND a.product_code = d.product_code
AND a.order_date >= d.effective_date
AND a.order_date <= d.expiry_date
AND a.order date = e.date
;
END
//
DELIMITER ; //
/* end of script
你将在编译该存储过程并部署在source数据库中,并且将每天运行该脚本。这样,该脚本实现由源推送CDC模式的数据提取。
注意 该脚本后面粗体的那两行代码将通过订单日期来确保包含合法产品信息的销售订单被提取出来。
编译和存储该存储过程可以用下面的命令
mysql> \. c:\mysql\scripts\push_sales_order.sql
你的控制台将显示:
Database changed
Query OK, 0 rows affected, 1 warning (0.08 sec)
Query OK, 0 rows affected (0.05 sec)
为了确保存储过程已经成功建立,调用Show procedure命令:
mysql> show procedure status like ‘push_sales_order’ \G;
你的控制台将显示:
*************************** 1. row ***************************
Db: source
Name: push_sales_order
Type: PROCEDURE
Definer: root@localhost
Modified: 2007-02-05 22:26:56
Created: 2007-02-05 22:26:56
Security_type: DEFINER
Comment:
1 row in set (0.00 sec)
mysql>
测试
本节我将向你展示,事先成功编译好的存储过程将每天完成由源推送CDC模式功能,从source数据库中的sales_order表提取数据,载入到数据仓库的sales_order_fact表中。
第一步,你需要运行列表5-2中的脚本来在source数据库中创建sales_order表。运行列表5-3脚本往数据仓库(dw)数据库中增加一些测试数据(四个销售订单记录和一个日期记录)。
列表5-2 建立sales_order 表
[sql] view plain copy
/*****************************************************************/
/* */
/* create_sales_order.sql */
/* */
/*****************************************************************/
USE source;
CREATE TABLE sales_order
( order_number INT
, customer_number INT
, product_code INT
, order_date DATE
, entry_date DATE
, order_amount DECIMAL (10, 2))
;
/* end of script */
列表 5-3 测试推送模式的数据
[sql] view plain copy
/*****************************************************************/
/* */
/* push_data.sql */
/* */
/*****************************************************************/
USE dw;
INSERT INTO order_dim VALUES
(NULL, 17, CURRENT_DATE, ‘9999-12-31’)
, (NULL, 18, CURRENT_DATE, ‘9999-12-31’)
, (NULL, 19, CURRENT_DATE, ‘9999-12-31’)
, (NULL, 20, CURRENT_DATE, ‘9999-12-31’)
;
INSERT INTO date_dim VALUES
(NULL, ‘2007-02-06’, ‘February’, 2, 6, 2007, CURRENT_DATE, ‘9999
12-31’)
;
/* load sales orders in the source database */
USE source;
INSERT INTO sales_order VALUES
(17, 1, 1, ‘2007-02-06’, ‘2007-02-06’, 1000)
, (18, 2, 1, ‘2007-02-06’, ‘2007-02-06’, 1000)
, (19, 3, 1, ‘2007-02-06’, ‘2007-02-06’, 4000)
, (20, 4, 1, ‘2007-02-06’, ‘2007-02-06’, 4000)
;
/* end of script */
在你开始之前,你必须把你的MySQL日期设置为:2007-02-07(销售订单测试数据的截至日期)并且切换到Source数据库(有那个存储过程的数据库)。
切换到source数据库可以键入下面的命令并且按回车
mysql> use source;
mysql 将打印出如下回应:
Database changed
接下来,运行push_sales_order存储过程,用如下命令向销售订单事实表载入数据。
mysql> call push_sales_order();
回应表示有4条记录有效。
Query OK, 4 rows affected (0.09 sec)
最后通过查询表来确定当前的销售订单被载入到sales_order_fact表中。回顾一下在上一章中你载入的16条销售订单记录,最后四条的时间是2007-02-06。
现在切换数据库为dw
mysql> use dw;
然后,运行下面命令查询sales_order_fact表。
mysql> select * from sales_order_fact;
结果如下所示:
+———-+————-+————+————-+————–+
| order_sk | customer_sk | product_sk |order_date_sk| order_amount |
+———-+————-+————+————-+————–+
| 1 | 1 | 2 | 1 | 1000.00 |
| 2 | 2 | 3 | 1 | 1000.00 |
| 3 | 3 | 4 | 1 | 4000.00 |
| 4 | 4 | 2 | 1 | 4000.00 |
| 5 | 5 | 3 | 1 | 6000.00 |
| 6 | 1 | 4 | 1 | 6000.00 |
| 7 | 2 | 2 | 1 | 8000.00 |
| 8 | 3 | 3 | 1 | 8000.00 |
| 9 | 4 | 4 | 1 | 10000.00 |
| 10 | 5 | 2 | 1 | 10000.00 |
| 11 | 1 | 2 | 2 | 20000.00 |
| 12 | 2 | 3 | 2 | 25000.00 |
| 13 | 3 | 4 | 2 | 30000.00 |
| 14 | 4 | 2 | 2 | 35000.00 |
| 15 | 5 | 3 | 2 | 40000.00 |
| 16 | 1 | 4 | 2 | 45000.00 |
| 17 | 1 | 3 | 3 | 1000.00 |
| 18 | 2 | 3 | 3 | 1000.00 |
| 19 | 3 | 3 | 3 | 4000.00 |
| 20 | 4 | 3 | 3 | 4000.00 |
+———-+————-+————+————-+————–+
20 rows in set (0.00 sec)
现在最后四条就是四条记录。
小结
本章你学习了全部源数据和CDC方法以及拉取和推送模式的源数据提取。你实施了CDC和推送模式进行销售订单数据的提取。接下来这章,你将学习装载日期维的各种技术。
第6章 装载日期维
概述
日期维在维度数据仓库中扮演特殊的角色。首先,也是最主要的,日期维包含时间信息,而时间信息是最重要的,因为数据仓库的一个最基本的功能就是存储历史数据。因此,数据仓库中的数据总是有一个时间特征。日期维的另一个特别的方面是,在数据仓库中,你可以选择产生什么样的日期数据用来装载日期维。
注意,一些数据仓库既要求日期也要求时间。
在你的数据仓库中,你从建立date_dim 表开始,该表包含一个日期或者是日期时间字段。该表中的值将成为其他拥有日期值的表的参照。在我们的数据仓库中,date_dim 表通过date_sk代理键关联到sales_order_fact表。比如,在销售订单事实表中,所有的拥有2007-02-06订单日期的订单记录都有一个为1的值,因为在date_dim 表中,2007-02-06日期的date_sk值是1。除了日期外,日期维还包含其他信息,比如月份名称和季度。通过代理键的关系,使得这些其他信息可以关联到销售订单记录中。这意味着你可以通过月份名称和季度查询销售订单。
本章将教你在多维数据仓库中3种最常用的装载日期维的技术。这3种技术是:
n 预装载
n 每天一个日期
n 从源数据获得日期信息
预装载
三种技术中,预装载是装载日期维最容易的一种技术。通过预装载,你插入一段时期中的所有日期信息。举个例子,你可以预先装载日期维近10年内的所有日期信息,例如从2005-01-01到2015-12-31。通过这个技术,你既可以一次性把你的数据仓库寿命范围的日期预装载到日期维,也可以根据需要在追加日期数据。
n 预装载的缺点是:
n 提早占用磁盘空间资源
n 你可能没有必要使用所有的日期(稀疏使用)
列表6-1显示了你可以执行预装载的存储过程。存储过程有两个输入参数,start_dt (开始日期) 和 end_dt (结束日期)。存储过程中的While循环,增量的产生从start_dt到end_dt的所有日期,并将这些日期数据插入到date_dim 表中。
注意,生效日期值为0000-00-00表示这些日期记录从过去的任何时间开始生效,到期日期值为9999-12-31表示这些日期记录不会失效。
列表6-1:预装载时间维的存储过程
[sql] view plain copy
/*****************************************************************/
/* */
/* pre_populate_date.sql */
/* */
/*****************************************************************/
USE dw;
DELIMITER // ;
DROP PROCEDURE IF EXISTS pre_populate_date //
CREATE PROCEDURE pre_populate_date (IN start_dt DATE, IN end_dt
DATE)
BEGIN
WHILE start_dt <= end_dt DO
INSERT INTO date_dim(
date_sk
, date
, month_name
, month
, quarter
, year
, effective_date
, expiry_date
)
VALUES(
NULL
, start_dt
, MONTHNAME (start_dt)
, MONTH (start_dt)
, QUARTER (start_dt)
, YEAR (start_dt)
, ‘0000-00-00’
, ‘9999-12-31’
)
;
SET start_dt = ADDDATE (start_dt, 1);
END WHILE;
END
//
DELIMITER ; //
/* end of script
你可以用如下命令编译该存储过程:
mysql> \. c:\mysql\scripts\pre_populate_date.sql
用show procedure 命令确定这个存储过程已经成功的建立。
mysql> show procedure status like ‘pre_populate_date’ \G
该存储过程信息如下:
The response from the stored procedure is as follows.
[sql] view plain copy
*************************** 1. row ***************************
Db: dw
Name: pre_populate_date
Type: PROCEDURE
Definer: root@localhost
Modified: 2007-02-07 22:42:03
Created: 2007-02-07 22:42:03
Security_type: DEFINER
Comment:
1 row in set (0.43 sec)
预装载测试
本节中我将向你展示如何确定预装载日期的存储过程已经正确的向date_dim表产生日期数据。
在我们开始之前,用下面的命令清空date_dim表中的记录。
MySQL> truncate date_dim;
mysql 将显示3条记录被删除:
Query OK, 3 rows affected (0.59 sec)
现在运行列表6-1中的存储过程,把从2007-01-01到2010-12-31这段时期的日期预装载到日期维中。这可能会花几分钟来插入1461条记录,这就是从2007-01-01到2010-12-31这段时期的日期的个数。
mysql> call pre_populate_date (‘2007-01-01’, ‘2010-12-31’);
为了确保日期记录已经被正确的预装载到date_dim表中,查询date_dim表中的记录数:
mysql> select count(0) from date_dim;
你将看到结果是1461。
+———-+
| count(0) |
+———-+
| 1461 |
+———-+
1 row in set (0.06 sec)
如果你要查询前面10条日期记录用这个命令:
mysql> select * from date_dim limit 10 \G
你将看到这些结果:
*************************** 1. row ***************************
date_sk: 1
date: 2007-01-01
month_name: January
month: 1
quarter: 1
year: 2007
effective_date: 0000-00-00
expiry_date: 9999-12-31
*************************** 2. row ***************************
date_sk: 2
date: 2007-01-02
month_name: January
month: 1
quarter: 1
year: 2007
effective_date: 0000-00-00
expiry_date: 9999-12-31
*************************** 3. row ***************************
date_sk: 3
date: 2007-01-03
month_name: January
month: 1
quarter: 1
year: 2007
effective_date: 0000-00-00
expiry_date: 9999-12-31
*************************** 4. row ***************************
date_sk: 4
date: 2007-01-04
month_name: January
month: 1
quarter: 1
year: 2007
effective_date: 0000-00-00
expiry_date: 9999-12-31
*************************** 5. row ***************************
date_sk: 5
date: 2007-01-05
month_name: January
month: 1
quarter: 1
year: 2007
effective_date: 0000-00-00
expiry_date: 9999-12-31
*************************** 6. row ***************************
date_sk: 6
date: 2007-01-06
month_name: January
month: 1
quarter: 1
year: 2007
effective_date: 0000-00-00
expiry_date: 9999-12-31
*************************** 7. row ***************************
date_sk: 7
date: 2007-01-07
month_name: January
month: 1
quarter: 1
year: 2007
effective date: 0000-00-00
expiry_date: 9999-12-31
*************************** 8. row ***************************
date_sk: 8
date: 2007-01-08
month_name: January
month: 1
quarter: 1
year: 2007
effective_date: 0000-00-00
expiry_date: 9999-12-31
*************************** 9. row ***************************
date_sk: 9
date: 2007-01-09
month_name: January
month: 1
quarter: 1
year: 2007
effective_date: 0000-00-00
expiry_date: 9999-12-31
*************************** 10. row **************************
date_sk: 10
date: 2007-01-10
month_name: January
month: 1
quarter: 1
year: 2007
effective_date: 0000-00-00
expiry_date: 9999-12-31
10 rows in set (0.00 sec)
If you query the last ten dates using this command
mysql> select * from date_dim limit 1451, 1461 \G
you’ll see these records shown.
*************************** 1. row ***************************
date_sk: 1452
date: 2010-12-22
month_name: December
month: 12
quarter: 4
year: 2010
effective_date: 0000-00-00
expiry_date: 9999-12-31
*************************** 2. row ***************************
date_sk: 1453
date: 2010-12-23
month_name: December
month: 12
quarter: 4
year: 2010
effective_date: 0000-00-00
expiry_date: 9999-12-31
*************************** 3. row ***************************
date_sk: 1454
date: 2010-12-24
month_name: December
month: 12
quarter: 4
year: 2010
effective_date: 0000-00-00
expiry_date: 9999-12-31
*************************** 4. row ***************************
date_sk: 1455
date: 2010-12-25
month_name: December
month: 12
quarter: 4
year: 2010
effective date: 0000-00-00
expiry_date: 9999-12-31
*************************** 5. row **************************
date_sk: 1456
date: 2010-12-26
month_name: December
month: 12
quarter: 4
year: 2010
effective date: 0000-00-00
expiry_date: 9999-12-31
*************************** 6. row ***************************
date_sk: 1457
date: 2010-12-27
month_name: December
month: 12
quarter: 4
year: 2010
effective_date: 0000-00-00
expiry_date: 9999-12-31
*************************** 7. row ***************************
date_sk: 1458
date: 2010-12-28
month_name : December
month: 12
quarter: 4
year: 2010
effective_date: 0000-00-00
expiry_date: 9999-12-31
*************************** 8. row ***************************
date_sk: 1459
date: 2010-12-29
month_name: December
month: 12
quarter: 4
year: 2010
effective date: 0000-00-00
expiry_date: 9999-12-31
*************************** 9. row ***************************
date_sk: 1460
date: 2010-12-30
month_name: December
month: 12
quarter: 4
year: 2010
effective date: 0000-00-00
expiry_date: 9999-12-31
*************************** 10. row ***************************
date_sk: 1461
date: 2010-12-31
month_name: December
month: 12
quarter: 4
year: 2010
effective_date: 0000-00-00
expiry_date: 9999-12-31
10 rows in set (0.00 sec)
每天一个日期
第二种装载日期维的技术是“每天一个日期”,这种方式与预装载相似。然而,运用“每天一个日期”你每天只预装载一个日期记录,而不是一个时期的所有日期记录。用这个方法,你仍然拥有所有的日期不管它们是否有用,但你不是一次装载所有的日期。
列表6-2中的脚本向date_dim表装载当天的日期。你将安排该脚本每天运行一次。如果你的数据仓库数据装载也是每天一次,你可以将每天日期装载结合到它的维度装载部分中。
列表6-2:每天日期装载
[sql] view plain copy
/*****************************************************************/
/* */
/* daily_date.sql */
/* */
/*****************************************************************/
USE dw;
INSERT INTO date_dim VALUES
( NULL
, CURRENT_DATE
, MONTHNAME (CURRENT_DATE)
, MONTH (CURRENT_DATE)
, QUARTER (CURRENT_DATE)
, YEAR (CURRENT_DATE)
, ‘0000-00-00’
, ‘9999-12-31’
)
/* end of script <span style=”font-family: ‘Times New Roman’; background-color: rgb(255, 255, 255);”> </span>
清空date_dim表然后运行列表6-2中的daily_date.sql脚本:
MySQL> \. c:\mysql\scripts\daily_date.sql
如果你是在设置mysql日期为:2007-02-07后才运行该脚本,你将看到:
Database changed
Query OK, 1 row affected (0.27 sec)
为了证实date_dim表已经成功装载,查询该表:
mysql> select * from date_dim \G
date_dim表内容为:
*************************** 1. row ***************************
date_sk: 1
date: 2007-02-07
month_name: February
month: 2
quarter: 1
year: 2007
effective_date: 0000-00-00
expiry_date: 9999-12-31
1 row in set (0.00 sec)
mysql>
从源数据获得日期信息
本小节,我将阐述并介绍一个关于另外一种装载技术的例子。用这种技术,你能够通过从源数据获得日期来装载日期维。
当你从源数据获得日期信息来装载date_dim表,你的date_dim表将只存储需要用到的日期数据,这将节省你的磁盘空间。不幸的是,这种技术会比较复杂,因为你必须向日期维加载那些你的源数据拥有的所有日期信息。
列表6-3的脚本通过source数据库中的sales_order表向date_dim表载入销售订单日期。你应用DISTINCT关键字来确保没有重复的日期被装载。
列表6-3:从源数据载入日期
[sql] view plain copy
/*****************************************************************/
/* */
/* source_date.sql */
/* */
/*****************************************************************/
USE dw;
INSERT INTO date_dim
SELECT DISTINCT
NULL
, order_date
, MONTHNAME (order_date)
, MONTH (order_date)
, QUARTER (order_date)
, YEAR (order_date)
, ‘0000-00-00’
, ‘9999-12-31’
FROM source.sales_order
WHERE order_date NOT IN
(SELECT date FROM date_dim)
;
/* end of script
在你运行列表6-3脚本之前,截去(truncate)date_dim表数据,然后用下面命令运行source_date.sql脚本:
mysql> \. c:\mysql\scripts\source_date.sql
结果如下:
Database changed
Query OK, 1 row affected (0.23 sec)
Records: 1 Duplicates: 0 Warnings: 0
查询sales_order表和date_dim表确定正确的装载。所有源数据的日期必须插入到date_dim表中。你可以用这个命令查询源数据表:
mysql> select * from source.sales_order \G
结果如下:
*************************** 1. row ***************************
order_number: 17
customer_number: 1
product_code: 1
order_date: 2007-02-06
entry_date: 2007-02-06
order_amount: 1000.00
*************************** 2. row ***************************
order_number: 18
customer_number: 2
product_code: 1
order_date: 2007-02-06
entry_date: 2007-02-06
order_amount: 1000.00
*************************** 3. row ***************************
order_number: 19
customer_number: 3
product_code: 1
order_date: 2007-02-06
entry_date: 2007-02-06
order_amount: 4000.00
*************************** 4. row ***************************
order_number: 20
customer_number: 4
product_code: 1
order_date: 2007-02-06
entry_date: 2007-02-06
order_amount: 4000.00
4 rows in set (0.05 sec)
现在用这个命令查询date_dim表:
mysql> select * from date_dim \G
你将看到:
*************************** 1. row ***************************
date_sk: 1
date: 2007-02-06
month_name: February
month: 2
quarter: 1
year: 2007
effective_date: 0000-00-00
expiry_date: 9999-12-31
1 row in set (0.00 sec)
你会发现这个例子的源数据只有一个日期2007-02-06,因为只有这个日期被拷贝到date_dim表。
现在运行列表6-4 的脚本来增加一些销售订单数据,然后再次运行装载脚本(source_date.sql)。
列表6-4:通过新增的销售订单增加更多的日期
[sql] view plain copy
/*****************************************************************/
/* */
/* more_sales_order.sql */
/* */
/*****************************************************************/
USE source;
INSERT INTO sales_order VALUES
(21, 1, 3, ‘2007-02-07’, ‘2007-02-07’, 1000)
, (22, 2, 3, ‘2007-02-08’, ‘2007-02-08’, 1000)
, (23, 3, 3, ‘2007-02-09’, ‘2007-02-09’, 4000)
, (24, 4, 3, ‘2007-02-10’, ‘2007-02-10’, 4000)
;
/* end of script
你可以用如下命令运行列表6-4脚本
mysql> \. c:\mysql\scripts\more_sales_order.sql
然后用下面命令再次运行source_date.sql脚本
mysql> \. c:\mysql\scripts\source_date.sql
最后,来自源数据的四个日期被正确的装载到date_dim表中。
mysql> select * from date_dim \G
你的结果将如下所示:
*************************** 1. row ***************************
date_sk: 1
date: 2007-02-06
month_name: February
month: 2
quarter: 1
year: 2007
effective_date: 0000-00-00
expiry_date: 9999-12-31
*************************** 2. row ***************************
date_sk: 2
date: 2007-02-07
month_name: February
month: 2
quarter: 1
year: 2007
effective_date: 0000-00-00
expiry_date: 9999-12-31
*************************** 3. row ***************************
date_sk: 3
date: 2007-02-08
month_name: February
month: 2
quarter: 1
year: 2007
effective_date: 0000-00-00
expiry_date: 9999-12-31
*************************** 4. row ***************************
date_sk: 4
date: 2007-02-09
month_name: February
month: 2
quarter: 1
year: 2007
effective_date: 0000-00-00
expiry_date: 9999-12-31
*************************** 5. row ***************************
date_sk: 5
date: 2007-02-10
month_name: February
month: 2
quarter: 1
year: 2007
effective_date: 0000-00-00
expiry_date: 9999-12-31
5 row in set (0 .00 sec)
小结
本章,你学习到三种最常用的日期装载技术,包括他们的优点和缺点。你将测试一些脚本来完成日期维的装载。下一章,你将应用预装载技术。
第七章 初始化导入
概述
在第6章,“装载日期维”,你学习了如何载入日期维度表。本章,你将学习如何载入事实表和其他维度表。
在你的数据仓库开始运作之前,你需要载入历史数据。历史数据是你装载数据库仓库最初的一批数据。这个最初的装载被称为初始化装载。
你数据仓库的用户将决定在数据仓库中他们需要多少的历史数据。比如,如果你的数据仓库是从2007-03-01开始,你的用户希望载入2年的历史数据,你将载入从2005-03-1到2007-02-28期间的源数据。然后,在数据仓库开始运作的2007-03-01这天,你将载入2007-03-01的数据。
在你装载事实表之前,你将载入所有的维度表,因为事实表需要维度表的代理键。这不仅在初始化载入,而且在以后定期装载的过程中都需要这么做。(定期装载在第8章讨论)。
本章,我将说明如何完成初始化装载,包括鉴别源数据,开发初始化装载脚本,以及测试脚本。
源数据
本小节,我将解释,在你可以开始写初始化装载脚本之前,有那些关于源数据的主要方面需要去判别。
第一步是判别哪些源数据是数据仓库中的事实表和维度表必须或者有用的。你还需要知道数据源的特性,比如文件类型,数据结构以及如何获取。
表格7-1是一个示例文档,概述了在这个销售订单数据仓库中源数据的关键信息,包括文件类型,格式和数据仓库目标表。
这种类型的表格通常称为数据源映射,因为它标识了每个数据从源到目标的对应关系。维护该表格的活动称为数据源的配置管理。
注意:客户和产品维相关的源数据被直接映射到它们在数据仓库中的目标表,及customer_dim 和 product_dim 表。另一方面,销售订单事务则是多个数据仓库表的源数据。你将在第20章中学习一个更为复杂的源数据,““不齐整数据源”。
表7-1:你的数据仓库的数据源
以表格的形式展示该表
源数据
数据类型
文件或者表名
数据仓库中的目标表
Customer
CSV text file
customer.csv
customer_dim
Product
Fixed-width text file
product.txt
product_dim
Sales order transaction
MySQL
sales_order in source database
order_dim
sales_order_fact
date_dim, if you use “Loading the Date from Source” option, but you’ll use pre-population here
初始化载入脚本
现在你已经鉴别了数据源,是时候开始编写用来初始化装载的脚本了。假设你的数据仓库将从2007-03-01开始运作,而你希望载入两年的历史数据,你需要载入从2005-03-01到2007-02-28期间的源数据。
列表7-1的脚本作为一个示例,注意以下:
客户维和产品维的生效日期是2007-03-01,没有更早日期的销售订单被载入,这意味着,没有更早的客户维和产品维是必须的。
订单维的生效日期理所当然是是订单日期。
销售订单事实表的代理键字段被载入维度表的代理键值。
你需要另外用预装载的方式预载入日期维date_dim表,从2005-03-1到2010-12-31。
列表7-1:数据仓库初始化载入
[sql] view plain copy
/*****************************************************************/
/* */
/* dw_initial.sql */
/* */
/*****************************************************************/
USE dw;
LOAD DATA INFILE ‘customer.csv’
INTO TABLE customer_stg
FIELDS TERMINATED BY ‘,’
OPTIONALLY ENCLOSED BY ‘”‘
LINES TERMINATED BY ‘\r\n’
IGNORE 1 LINES
( customer_number
, customer_name
, customer_street_address
, customer_zip_code
, customer_city
, customer_state )
;
INSERT INTO customer_dim
SELECT
NULL
, customer_number
, customer_name
, customer_street_address
, customer_zip_code
, customer_city
, customer_state
, ‘2005-03-01’
, ‘9999-12-31’
FROM
customer_stg
;
LOAD DATA INFILE ‘product.txt’
INTO TABLE product_stg
FIELDS TERMINATED BY ”
OPTIONALLY ENCLOSED BY ”
LINES TERMINATED BY ‘\r\n’
IGNORE 1 LINES
( product_code
, product_name
, product_category )
;
INSERT INTO product_dim
SELECT
NULL
, product_code
, product_name
, product_category
, ‘2005-03-01’
, ‘9999-12-31’
FROM
product_stg
;
INSERT INTO order_dim
SELECT
NULL
, order_number
, order_date
, ‘9999-12-31’
FROM
source.sales_order
WHERE order_date >= ‘2005-03-01’
AND order_date < ‘2007-02-28’
;
INSERT INTO sales_order_fact
SELECT
order_sk
, customer_sk
, product_sk
, date_sk
, order_amount
FROM
source.sales_order a
, order_dim b
, customer_dim c
, product_dim d
, date_dim e
WHERE
a.order_number = b.order_number
AND a.customer_number = c.customer_number
AND a.product_code = d.product_code
AND a.order_date = e.date
AND order_date >= ‘2005-03-01’
AND order_date < ‘2007-02-28’
;
/* end of script
执行初始化载入脚本
在你运行列表7-1所示的初始化载入脚本之前,你需要完成下面的几个步骤:
u 清空所有的表,包括中间过程表。
u 预装载日期维
u 准备客户和产品测试数据
u 在源数据库中产生测试数据
这些步骤将在下面的子节中讨论。
清空表
用列表7-2的脚本清空所有的表。
列表7-2:截去所有表的脚本
/*****************************************************************/
/* */
/* truncate_tables.sql */
/* */
/*****************************************************************/
USE dw;
TRUNCATE customer_dim;
TRUNCATE product_dim;
TRUNCATE order_dim;
TRUNCATE date_dim;
TRUNCATE sales_order_fact;
TRUNCATE customer_stg;
TRUNCATE product_stg;
USE source;
TRUNCATE sales_order;
/* end of script
你可以调用以下命令运行列表7-2的脚本。
mysql> \. c:\mysql\scripts\truncate_tables.sql
mysql数据库将回应你每个表各有多少记录被删除。
Database changed
Query OK, 7 rows affected (0.17 sec)
Query OK, 4 rows affected (0.05 sec)
Query OK, 20 rows affected (0.06 sec)
Query OK, 5 rows affected (0.06 sec)
Query OK, 20 rows affected (0.05 sec)
Query OK, 7 rows affected (0.06 sec)
Query OK, 3 rows affected (0.06 sec)
Database changed
Query OK, 8 rows affected (0.07 sec)
预装载日期维
一旦表清空了,你可以通过运行我们曾在第6章“装载日期维”中讨论过的pre_populate_date存储过程,向日期维预装载从2005-03-01到2010-12-31这个期间的日期数据。通过调用下面命令来确保当前的数据库是dw。
mysql> use dw;
然后调用存储过程,传入开始日期和结束日期作为参数。
mysql> call pre_populate_date (‘2005-03-01’, ‘2010-12-31’);
准备客户和产品测试数据
在装载date_dim表后的第二个步骤是准备客户和产品的测试数据。我已经用一个csv格式的文件准备了客户数据,在一个固定宽度格式的文件中准备产品数据。
下面是客户资料源数据:
CUSTOMER NO,CUSTOMER NAME,STREET ADDRESS,ZIP CODE,CITY,STATE
1,Really Large Customers, 7500 Louise Dr.,17050, Mechanicsburg,PA
2,Small Stores, 2500 Woodland St.,17055, Pittsburgh,PA
3,Medium Retailers,1111 Ritter Rd.,17055,Pittsburgh,PA
4,Good Companies,9500 Scott St.,17050,Mechanicsburg,PA
5,Wonderful Shops,3333 Rossmoyne Rd.,17050,Mechanicsburg,PA
6,Loyal Clients,7070 Ritter Rd.,17055,Pittsburgh,PA
7,Distinguished Partners,9999 Scott St.,17050,Mechanicsburg,PA
下面是产品数据
PRODUCT CODE,PRODUCT NAME,PRODUCT GROUP
1 Hard Disk Drive Storage
2 Floppy Drive Storage
3 LCD Panel Monitor
准备销售订单数据
你所需要的最后一组测试数据是销售订单。列表7-3的脚本能够被用来向source数据库中的sales_order表插入21条销售订单。
列表7-3:测试初始化装载的销售订单
/*****************************************************************/
/* */
/* sales_order_initial.sql */
/* */
/*****************************************************************/
USE source;
INSERT INTO sales_order VALUES
(1, 1, 1, ‘2005-02-01’, ‘2005-02-01’, 1000)
, (2, 2, 2, ‘2005-02-10’, ‘2005-02-10’, 1000)
, (3, 3, 3, ‘2005-03-01’, ‘2005-03-01’, 4000)
, (4, 4/ 1, ‘2005-04-15’, ‘2005-04-15’, 4000)
, (5, 5, 2, ‘2005-05-20’, ‘2005-05-20’, 6000)
, (6, 6, 3, ‘2005-07-30’, ‘2005-07-30’, 6000)
, (7, 7, 1, ‘2005-09-01’, ‘2005-09-01’, 8000)
, (8, 1, 2, ‘2005-11-10’, ‘2005-11-10’, 8000)
, (9, 2, 3, ‘2006-01-05’, ‘2006-01-05’, 1000)
, (10, 3, 1, ‘2006-02-10’, ‘2006-02-10’, 1000)
, (11, 4, 2, ‘2006-03-15’, ‘2006-03-15’, 2000)
, (12, 5, 3, ‘2006-04-20’, ‘2006-04-20’, 2500)
, (13, 6, 1, ‘2006-05-30’, ‘2006-05-30’, 3000)
, (14, 7, 2, ‘2006-06-01’, ‘2006-06-01’, 3500)
, (15, 1, 3, ‘2006-07-15’, ‘2006-07-15’, 4000)
, (16, 2, 1, ‘2006-08-30’, ‘2006-08-30’, 4500)
, (17, 3, 2, ‘2006-09-05’, ‘2006-09-05’, 1000)
, (18, 4, 3, ‘2006-10-05’, ‘2006-10-05’, 1000)
, (19, 5, 1, ‘2007-01-10’, ‘2007-01-10’, 4000)
, (20, 6, 2, ‘2007-02-20’, ‘2007-02-20’, 4000)
, (21, 7, 3, ‘2007-02-28’, ‘2007-02-28’, 4000)
;
/* end of script */
现在用下面的命令方式运行sales_order_initial.sql脚本
mysql> \. c:\mysql\scripts\sales_order_initial.sql
你将看到21条新的记录被插入到数据库中
Database changed
Query OK, 21 rows affected (0.05 sec)
Records: 21 Duplicates: 0 Warnings: 0
注意历史数据从2005-03-01开始载入,所以开始的两个订单将不会被载入。
运行和确认初始化装载
现在测试数据已经准备好,是时候执行初始化装载了。但是,首先要作的是设置你的mysql数据库时间是2007-02-28,这是你的数据仓库开始运作的前一天。你运行初始化脚本在这天结束的时候,即再也没有新的数据可以获得并且源数据库已经准备好了。
你现在已经准备好测试初始化装载了。用下面的命令方式运行列表7-1所示的装载脚本dw_initial.sql。
mysql> \. c:\mysql\scripts\dw_initial.sql
在你的控制台上,将看到下面的信息:
Database changed
Query OK, 7 rows affected (0.06 sec)
Records: 7 Deleted: 0 Skipped: 0 Warnings: 0
Query OK, 7 rows affected (0.06 sec)
Records: 7 Duplicates: 0 Warnings: 0
Query OK, 3 rows affected (0.06 sec)
Records: 3 Deleted: 0 Skipped: 0 Warnings: 0
Query OK, 3 rows affected (0.07 sec)
Records: 3 Duplicates: 0 Warnings: 0
Query OK, 19 rows affected (0.06 sec)
Records: 19 Duplicates: 0 Warnings: 0
Query OK, 19 rows affected (0.10 sec)
Records: 19 Duplicates: 0 Warnings: 0
你可以用列表7-4所示的查询语句来检查19条销售订单记录被正确的载入。
列表7-4 通过查询确认销售订单被准确的载入
/*****************************************************************/
/* */
/* confirm_initial_population.sql */
/* */
/*****************************************************************/
USE dw;
SELECT
order_number on
, customer_name
, product_name
, date
, order_amount amount
FROM
sales_order_fact a
, customer_dim b
, product_dim c
, order_dim d
, date_dim e
WHERE
a.customer_sk = b.customer_sk
AND a.product_sk = c.product_sk
AND a.order_sk = d.order_sk
AND a.order date sk = e.date_sk
;
/* end of script */
用下面的命令方式运行列表7-4脚本。
mysql> \. c:\mysql\scripts\confirm_initial_population.sql
结果如下所示
Database changed
+–+————————+—————–+———–+———+
|no| customer_name | product _name | date | amount |
+–+————————+—————–+———–+———+
| 3| Medium Retailers | LCD Panel | 2005-03-01| 4000.00 |
| 4| Good Companies | Hard Disk Drive | 2005-04-15| 4000.00 |
| 5| Wonderful Shops | Floppy Drive | 2005-05-20| 6000.00 |
| 6| Loyal Clients LCD | Panel | 2005-07-30| 6000.00 |
| 7| Distinguished Partners | Hard Disk Drive | 2005-09-01| 8000.00 |
| 8| Really Large Customers | Floppy Drive | 2005-11-10| 8000.00 |
| 9| Small Stores | LCD Panel | 2006-01-05| 1000.00 |
|10| Medium Retailers | Hard Disk Drive | 2006-02-10| 1000.00 |
|11| Good Companies | Floppy Drive | 2006-03-15| 2000.00 |
|12| Wonderful Shops | LCD Panel | 2006-04-20| 2500.00 |
|13| Loyal Clients | Hard Disk Drive | 2006-05-30| 3000.00 |
|14| Distinguished Partners | Floppy Drive | 2006-06-01| 3500.00 |
|15| Really Large Customers | LCD Panel | 2006-07-15| 4000.00 |
|16| Small Stores | Hard Disk Drive | 2006-08-30| 4500.00 |
|17| Medium Retailers | Floppy Drive | 2006-09-05| 1000.00 |
|18| Good Companies | LCD Panel | 2006-10-05| 1000.00 |
|19| Wonderful Shops | Hard Disk Drive | 2007-01-10| 4000.00 |
|20| Loyal Clients | Floppy Drive | 2007-02-20| 4000.00 |
|21| Distinguished Partners | LCD Panel | 2007-02-28| 4000.00 |
+–+————————+—————–+———–+———+
19 rows in set (0.00 sec)
小结
本章你将学习到源数据映射以及被称为初始化装载的过程,来根据你的用户要求载入历史数据。下一章,你将学习到你运作数据仓库所必须的定期装载。
第8章:定期装载
本章包含定期装载。和初始化装载不同,初始化装载你只需要在开始数据仓库运作前执行一次,定期装载需要你安排一个定期装载计划来定期的载入源数据。
在本章,我将向你展示,通过运行一个脚本在我们的dw数据库中完成定期装载之前,如何准备数据。
鉴别数据源和装载类型
部署一个定期装载计划的第一个步骤是为数据仓库中的每个事实表和维度表鉴别哪些源数据是必须的以及可用的。然后,你才决定适合该装载的提取模式和载入类型。表8-1所示的范例文档概括了这些信息。
表8-1:定期装载的数据源和载入类型
Source Data
源数据
Data Warehouse Table
数据仓库表
Extraction Mode
提取模式
Loading Type
载入类型
Customer
customer_dim
Whole, Pull
SCD2 on address
SCD1 on name
Product
product_dim
Whole, Pull
SCD2
Sales order Transaction
order_dim
CDC (daily), Pull
Unique order number
sales_order_fact
CDC (daily), Pull
Daily sales orders
n/a
date_dim
n/a
Pre-populate
注你已经在第5章“源数据提取”学习了提取模式,以及在第2章“维度历史”学习了SCD2。
另外一个源数据的特性可能影响你设计,即当一个特殊的数据可以用来定期载入时的时间窗问题,这点对于事务型的源数据特别重要,因为这种源数据量通常是巨大的,比如销售订单。
另外,你需要知道每个源数据的细节特征,比如文件类型,数据结构,甚至到个别的字段。
定期装载脚本\
我将用列表8-1所示的脚本来解释如何使定期装载起作用。你可以用它以每天为周期来装载数据仓库。抽取模式和装载类型如下所示:
n customer.csv 和product.txt的固定格式文件分别通过customer_stg 和 product_stg 表为中间表载入到customer_dim和 product_dim 表中。载入是通过mysql的LOAD DATA INFILE工具完成的。
n SCD2被应用到客户地址,产品名称,以及产品组中。SCD1应用到客户名称中。
n 只有开始于当前时间的销售订单记录被载入到order_dim 和 sales_order_fact 表中。
列表8-1:每日的dw定期装载
/*****************************************************************/
/* */
/* dw_regular.sql */
/* */
/*****************************************************************/
USE dw;
/* customer dimension loading */
TRUNCATE customer_stg
;
LOAD DATA INFILE ‘customer.csv’
INTO TABLE customer_stg
FIELDS TERMINATED BY ‘, ‘
OPTIONALLY ENCLOSED BY ‘ ” ‘
LINES TERMINATED BY ‘\r\n’
IGNORE 1 LINES
( customer_number
, customer_name
, customer_street_address
, customer_zip_code
, customer_city
, customer_state)
;
/* SCD2 on customer street addresses */
/* first, expire the existing customers */
UPDATE
customer_dim a
, customer_stg b
SET
expiry_date = SUBDATE (CURRENT_DATE, 1)
WHERE
a.customer_number = b.customer_number
AND a.customer_street_address <> b.customer_street_address
AND expiry_date = ‘9999–12–31’
;
/* then, add a new row for the customer */
INSERT INTO customer_dim
SELECT
NULL
, b.customer_number
, b.customer_name
, b.customer_street_address
, b.customer_zip_code
, b.customer_city
, b.customer_state
, CURRENT_DATE
, ‘9999–12–31’
FROM
customer_dim a
, customer_stg b
WHERE
a.customer_number = b.customer_number
AND (a.customer_street_address <> b.customer_street_address)
AND EXISTS(
SELECT *
FROM customer_dim x
WHERE
b.customer_number=x.customer_number
AND a.expiry_date=SUBDATE (CURRENT_DATE, 1))
AND NOT EXISTS (
SELECT *
FROM customer_dim y
WHERE
b.customer_number = y. customer_number
AND y.expiry_date = ‘9999–12–31′)
;
/* SCD1 on customer name */
UPDATE customer_dim a, customer_stg b
SET a.customer_name = b.customer_name
WHERE a.customer_number = b.customer_number
AND a.customer name <> b.customer name
;
/* add new customer */
INSERT INTO customer_dim
SELECT
NULL
, customer_number
, customer_name
, customer_street_address
, customer_zip_code
, customer_city
, customer_state
, CURRENT_DATE
,’9999–12–31’
FROM customer_stg
WHERE customer_number NOT IN(
SELECT y.customer_number
FROM customer_dim x, customer_stg y
WHERE x.customer_number = y.customer_number)
;
/* product dimension loading */
TRUNCATE product_stg
;
LOAD DATA INFILE ‘product.txt’
INTO TABLE product_stg
FIELDS TERMINATED BY ‘ ‘
OPTIONALLY ENCLOSED BY ‘ ‘
LINES TERMINATED BY ‘\r\n’
IGNORE 1 LINES
( product_code
, product_name
, product_category)
;
/* SCD2 on product name and group */
/* first, expire the existing product */
UPDATE
product_dim a
, product_stg b
SET
expiry_date = SUBDATE (CURRENT_DATE, 1)
WHERE
a.product_code = b.product_code
AND ( a.product_name <> b.product_name
OR a.product_category <> b.product_category)
AND expiry_date = ‘9999–12–31′
;
/* then, add a new row for the product */
INSERT INTO product_dim
SELECT
NULL
, b.product_code
, b.product_name
, b.product_category
, CURRENT_DATE
,’9999–12–31’
FROM
product_dim a
, product_stg b
WHERE
a.product_code = b.product_code
AND ( a.product_name <> b.product_name
OR a.product_category <> b.product_category)
AND EXISTS(
SELECT *
FROM product_dim x
WHERE b.product_code = x.product_code
AND a.expiry_date = SUBDATE (CURRENT_DATE, 1))
AND NOT EXISTS (
SELECT *
FROM product_dim y
WHERE b.product_code = y.product_code
AND y.expiry_date = ‘9999–12–31’)
;
/* add new product */
INSERT INTO product_dim
SELECT
NULL
, product_code
, product_name
, product_category
, CURRENT_DATE
, ‘9999–12–31’
FROM product_stg
WHERE product_code NOT IN(
SELECT y.product_code
FROM product_dim x, product_stg y
WHERE x.product_code = y.product_code)
;
/* end of product_dim loading */
INSERT INTO order_dim (
order_sk
, order_number
, effective_date
, expiry_date)
SELECT
NULL
, order_number
, order_date
, ‘9999–12–31’
FROM source.sales_order
WHERE entry_date = CURRENT_DATE
;
INSERT INTO sales_order_fact
SELECT
order_sk
, customer_sk
, product_sk
, date_sk
, order_amount
FROM
source.sales_order a
, order_dim b
, customer_dim c
, product_dim d
, date_dim e
WHERE
a.order_number = b.order_number
AND a.customer_number = c.customer_number
AND a.order_date >= c.effective_date
AND a.order_date <= c.expiry_date
AND a.product_code = d.product_code
AND a.order_date >= d.effective_date
AND a.order_date <= d.expiry_date
AND a.order_date = e.date
AND a.entry_date = CURRENT_DATE
;
/* end of script */
实验数据
为了实验定期装载,你需要准备客户,产品和销售订单实验数据。每种数据的来源将在下面的小节中讨论。
关于Customer.csv 文件
customer.csv文件有以下几个变化:
n 编号6的客户的街道编号现在是7777 Ritter Rd(它原来是7000 Ritter Rd.)。
n 编号7的客户名称现在是Distinguished Agencies(它原来是Distinguished Partners)。
n 新增了一个客户是第8个客户。
customer.csv文件内容如下:
CUSTOMER NO, CUSTOMER NAME,STREET ADDRESS, ZIP CODE,CITY,STATE
1, Really Large Customers, 7500 Louise Dr., 17050, Mechanicsburg, PA
2, Small Stores, 2500 Woodland St., 17055, Pittsburgh, PA
3, Medium Retailers, 1111 Ritter Rd., 17055, Pittsburgh, PA
4, Good Companies, 9500 Scott St., 17050, Mechanicsburg, PA
5, Wonderful Shops, 3333 Rossmoyne Rd., 17050, Mechanicsburg, PA
6, Extremely Loyal Clients, 7777 Ritter Rd., 17055, Pittsburgh, PA
7, Distinguished Agencies, 9999 Scott St., 17050, Mechanicsburg, PA
8, Subsidiaries, 10000 Wetline Blvd., 17055, Pittsburgh, PA
关于product.txt文件
Product.txt文件发生的变化是:
n 产品3的名称现在是Flat Pan(它原来是LCD Panel)。
n 新增一个产品为第4个产品。
修改后的product.txt文件:
PRODUCT CODE,PRODUCT NAME,PRODUCT GROUP
1 Hard Disk Drive Storage
2 Floppy Drive Storage
3 Flat Panel Monitor
4 Keyboard Peripheral
销售订单交易数据
最后一个你需要准备的测试数据是销售订单。假设你在2007-03-01(你第一次执行定期装载的日期)开始数据仓库运作。列表8-2中的脚本新增16个订单日期是2007-03-01的销售订单。
列表8-2:新增销售订单
/*****************************************************************/
/* */
/* sales_order_regular.sql */
/* */
/*****************************************************************/
USE source;
INSERT INTO sales_order VALUES
(22, 1, 1, ‘2007–03–01’, ‘2007–03–01’, 1000)
, (23, 2, 2, ‘2007–03–01’, ‘2007–03–01’, 2000)
, (24, 3, 3, ‘2007–03–01’, ‘2007–03–01’, 3000)
, (25, 4, 4, ‘2007–03–01’, ‘2007–03–01’, 4000)
, (26, 5, 2, ‘2007–03–01’, ‘2007–03–01’, 1000)
, (27, 6, 2, ‘2007–03–01’, ‘2007–03–01’, 3000)
, (28, 7, 3, ‘2007–03–01’, ‘2007–03–01’, 5000)
, (29, 8, 4, ‘2007–03–01’, ‘2007–03–01’, 7000)
, (30, 1, 1, ‘2007–03–01’, ‘2007–03–01’, 1000)
, (31, 2, 2, ‘2007–03–01’, ‘2007–03–01’, 2000)
, (32, 3, 3, ‘2007–03–01’, ‘2007–03–01’, 4000)
, (33, 4, 4, ‘2007–03–01’, ‘2007–03–01’, 6000)
, (34, 5, 1, ‘2007–03–01’, ‘2007–03–01’, 2500)
, (35, 6, 2, ‘2007–03–01’, ‘2007–03–01’, 5000)
, (36, 7, 3, ‘2007–03–01’, ‘2007–03–01’, 7500)
, (37, 8, 4, ‘2007–03–01’, ‘2007–03–01’, 1000)
;
/* end of script */
用下面命令运行sales_order_regular.sql脚本:
mysql> \. c:\mysql\scripts\sales_order_regular.sql
你的控制台将出现如下显示:
Database changed
Query OK, 16 rows affected (0.08 sec)
Records: 16 Duplicates: 0 Warnings: 0
sales_order表现在有37条记录。
运行定期装载脚本
在你运行列表 8-1所示的dw_regular.sql 脚本之前,你需要设置mysql数据库时间为2007-03-01。然后运行该脚本:
mysql> \. c:\mysql\scripts\dw_regular.sql
你将在你的控制台上看到
Database changed
Query OK, 7 rows affected (0.12 sec)
Query OK, 8 rows affected (0.05 sec)
Records: 8 Deleted: 0 Skipped: 0 Warnings: 0
Query OK, 1 row affected (0.06 sec)
Rows matched: 1 Changed: 1 Warnings: 0
Query OK, 1 row affected (0.07 sec)
Records: 1 Duplicates: 0 Warnings: 0
Query OK, 2 rows affected (0.06 sec)
Rows matched: 2 Changed: 2 Warnings: 0
Query OK, 1 row affected (0.06 sec)
Records: 1 Duplicates: 0 Warnings: 0
Query OK, 3 rows affected (0.05 sec)
Query OK, 4 rows affected (0.08 sec)
Records: 4 Deleted: 0 Skipped: 0 Warnings: 0
Query OK, 1 row affected (0.05 sec)
Rows matched: 1 Changed: 1 Warnings: 0
Query OK, 1 row affected (0.06 sec)
Records: 1 Duplicates: 0 Warnings: 0
Query OK, 1 row affected (0.06 sec)
Records: 1 Duplicates: 0 Warnings: 0
Query OK, 16 rows affected (0.07 sec)
Records: 16 Duplicates: 0 Warnings: 0
Query OK, 16 rows affected (0.11 sec)
Records: 16 Duplicates: 0 Warnings: 0
确定定期装载成功
为了确认你的定期装载执行成功,你需要查询维度和事实表。查询customer_dim用如下SQL语句:
mysql> select * from customer_dim \G
结果如下:
The result is as follows.
*************************** 1. row ***************
customer_sk: 1
customer_number: 1
customer_name: Really Large Customers
customer_street_address: 7500 Louise Dr.
customer_zip_code: 17050
customer_city: Mechanicsburg
customer_state: PA
effective date: 2005–03–01
expiry_date: 9999–12–31
*************************** 2. row **************
customer_sk: 2
customer_number: 2
customer_name: Small Stores
customer_street_address: 2500 Woodland St.
customer_zip_code: 17055
customer_city: Pittsburgh
customer_state: PA
effective_date: 2005–03–01
expiry_date: 9999–12–31
*************************** 3. row ***************************
customer_sk: 3
customer_number: 3
customer_name: Medium Retailers
customer_street_address: 1111 Ritter Rd.
customer_zip_code: 17055
customer_city: Pittsburgh
customer state: PA
effective_date: 2005–03–01
expiry_date: 9999–12–31
*************************** 4. row ***************************
customer_sk: 4
customer_number: 4
customer_name: Good Companies
customer_street_address: 9500 Scott St.
customer_zip_code: 17050
customer_city: Mechanicsburg
customer_state: PA
effective_date: 2005–03–01
expiry_date: 9999–12–31
*************************** 5. row ***************************
customer_sk: 5
customer_number: 5
customer_name: Wonderful Shops
customer_street_address: 3333 Rossmoyne Rd.
customer_zip_code: 17050
customer_city: Mechanicsburg
customer_state: PA
effective date: 2005–03–01
expiry_date: 9999–12–31
*************************** 6. row ***********************************
customer_sk: 6
customer_number: 6
customer_name: Extremely Loyal Clients
customer_street_address: 7070 Ritter Rd.
customer_zip_code: 17055
customer_city: Pittsburgh
customer_state: PA
effective_date: 2005–03–01
expiry_date: 2007–02–28
*************************** 7. row ***************************
customer_sk: 7
customer_number: 7
customer_name: Distinguished Agencies
customer_street_address: 9999 Scott St.
customer_zip_code: 17050
customer_city: Mechanicsburg
customer_state: PA
effective_date: 2005–03–01
expiry_date: 9999–12–31
*************************** 8. row ***************************
customer_sk: 8
customer_number: 6
customer_name: Extremely Loyal Clients
customer_street_address: 7777 Ritter Rd.
customer_zip_code: 17055
customer_city: Pittsburgh
customer_state: PA
effective_date: 2007–03–01
expiry_date: 9999–12–31
*************************** 9. row ***************************
customer_sk: 9
customer_number: 8
customer_name: Subsidiaries
customer_street_address: 10000 Wetline Blvd.
customer_zip_code: 17055
customer_city: Pittsburgh
customer_state: PA
effective_date: 2007–03–01
expiry_date: 9999–12–31
9 rows in set (0.00 sec)
查询结果说明:
n SCD2被作用于客户6的街道地址
n SCD1被作用于所有客户6记录的名称上。
n SCD1被作用于客户7的名称上。
n 新增了客户记录:客户8。
查询product_dim表用如下sql语句
mysql> select * from product_dim \G
结果如下:
*************************** 1. row ***************************
product_sk: 1
product_code: 1
product_name: Hard Disk Drive
product_category: Storage
effective_date: 2005–03–01
expiry_date: 9999–12–31
*************************** 2. row ***************************
product_sk: 2
product_code: 2
product_name: Floppy Drive
product_category: Storage
effective_date: 2005–03–01
expiry_date: 9999–12–31
*************************** 3. row ***************************
product_sk: 3
product_code: 3
product_name: LCD Panel
product_category: Monitor
effective_date: 2005–03–01
expiry_date: 2007–02–28
*************************** 4. row ***************************
product_sk: 4
product_code: 3
product_name: Flat Panel
product_category: Monitor
effective date: 2007–03–01
expiry_date: 9999–12–31
*************************** 5. row **************************
product_sk: 5
product_code: 4
product_name: Keyboard
product_category: Peripheral
effective_date: 2007–03–01
expiry_date: 9999–12–31
5 rows in set (0.01 sec)
查询product_dim表的结果说明:
n SCD2被应用到产品3的名称
n 新增新的产品:产品4
现在,查询order_dim表:
mysql> select * from order_dim;
这里是结果,你将有35条订单记录,其中19条是第7章“初始化装载”载入的,16条是本章载入的。
+———-+————–+—————-+————-+
| order_sk | order_number | effective_date | expiry_date |
+———-+————–+—————-+————-+
| 1 | 3 | 2005–03–01 | 9999–12–31 |
| 2 | 4 | 2005–04–15 | 9999–12–31 |
| 3 | 5 | 2005–05–20 | 9999–12–31 |
| 4 | 6 | 2005–07–30 | 9999–12–31 |
| 5 | 7 | 2005–09–01 | 9999–12–31 |
| 6 | 8 | 2005–11–10 | 9999–12–31 |
| 7 | 9 | 2006–01–05 | 9999–12–31 |
| 8 | 10 | 2006–02–10 | 9999–12–31 |
| 9 | 11 | 2006–03–15 | 9999–12–31 |
| 10 | 12 | 2006–04–20 | 9999–12–31 |
| 11 | 13 | 2006–05–30 | 9999–12–31 |
| 12 | 14 | 2006–06–01 | 9999–12–31 |
| 13 | 15 | 2006–07–15 | 9999–12–31 |
| 14 | 16 | 2006–08–30 | 9999–12–31 |
| 15 | 17 | 2006–09–05 | 9999–12–31 |
| 16 | 18 | 2006–10–05 | 9999–12–31 |
| 17 | 19 | 2007–01–10 | 9999–12–31 |
| 18 | 20 | 2007–02–20 | 9999–12–31 |
| 19 | 21 | 2007–02–28 | 9999–12–31 |
| 20 | 22 | 2007–03–01 | 9999–12–31 |
| 21 | 23 | 2007–03–01 | 9999–12–31 |
| 22 | 24 | 2007–03–01 | 9999–12–31 |
| 23 | 25 | 2007–03–01 | 9999–12–31 |
| 24 | 26 | 2007–03–01 | 9999–12–31 |
| 25 | 27 | 2007–03–01 | 9999–12–31 |
| 26 | 28 | 2007–03–01 | 9999–12–31 |
| 27 | 29 | 2007–03–01 | 9999–12–31 |
| 28 | 30 | 2007–03–01 | 9999–12–31 |
| 29 | 31 | 2007–03–01 | 9999–12–31 |
| 30 | 32 | 2007–03–01 | 9999–12–31 |
| 31 | 33 | 2007–03–01 | 9999–12–31 |
| 32 | 34 | 2007–03–01 | 9999–12–31 |
| 33 | 35 | 2007–03–01 | 9999–12–31 |
| 34 | 36 | 2007–03–01 | 9999–12–31 |
| 35 | 37 | 2007–03–01 | 9999–12–31 |
+———-+————–+—————-+————-+
35 rows in set (0.00 sec)
销售订单事实表
现在你可以查询sales_order_fact表
mysql> select * from sales_order_fact;
这里是结果显示:
+———-+————-+————+—————+————–+
| order_sk | customer_sk | product_sk | order_date_sk | order_amount |
+———-+————-+————+—————+————–+
| 1 | 3 | 3 | 1 | 4000.00 |
| 2 | 4 | 1 | 46 | 4000.00 |
| 3 | 5 | 2 | 81 | 6000.00 |
| 4 | 6 | 3 | 152 | 6000.00 |
| 5 | 7 | 1 | 185 | 8000.00 |
| 6 | 1 | 2 | 255 | 8000.00 |
| 7 | 2 | 3 | 311 | 1000.00 |
| 8 | 3 | 1 | 347 | 1000.00 |
| 9 | 4 | 2 | 380 | 2000.00 |
| 10 | 5 | 3 | 416 | 2500.00 |
| 11 | 6 | 1 | 456 | 3000.00 |
| 12 | 7 | 2 | 458 | 3500.00 |
| 13 | 1 | 3 | 502 | 4000.00 |
| 14 | 2 | 1 | 548 | 4500.00 |
| 15 | 3 | 2 | 554 | 1000.00 |
| 16 | 4 | 3 | 584 | 1000.00 |
| 17 | 5 | 1 | 681 | 4000.00 |
| 18 | 6 | 2 | 722 | 4000.00 |
| 19 | 7 | 3 | 730 | 4000.00 |
| 20 | 1 | 1 | 731 | 1000.00 |
| 21 | 2 | 2 | 731 | 2000.00 |
| 22 | 3 | 4 | 731 | 3000.00 |
| 23 | 4 | 5 | 731 | 4000.00 |
| 24 | 5 | 2 | 731 | 1000.00 |
| 25 | 8 | 2 | 731 | 3000.00 |
| 26 | 7 | 4 | 731 | 5000.00 |
| 27 | 9 | 5 | 731 | 7000.00 |
| 28 | 1 | 1 | 731 | 1000.00 |
| 29 | 2 | 2 | 731 | 2000.00 |
| 30 | 3 | 4 | 731 | 4000.00 |
| 31 | 4 | 5 | 731 | 6000.00 |
| 32 | 5 | 1 | 731 | 2500.00 |
| 33 | 8 | 2 | 731 | 5000.00 |
| 34 | 7 | 4 | 731 | 7500.00 |
| 35 | 9 | 5 | 731 | 1000.00 |
+———-+————-+————+—————+————–+
35 rows ir n set (0.00 sec 🙂
结果说明:
n 开始于2007-03-01的16条订单被加入。
n 以订单日期为日期基准,有效的产品和客户被准确的获得。
u 代理键值为4代表产品3而不是代理键值为3的那个记录。
u 代理键值为8代表客户6,而不是代理键值为6的那个记录。
小结
本章你学习本实验了一个每日定期装载过程,在其获取模式中,全部数据源和变化数据捕获的抽取方式,即SCD1和SCD2被应用。在下一章,你将学习如何部署这个定期装载以每日为周期在window平台上运行。
第9章:定期装载计划
一旦数据仓库开始运作,你应该定期的从源向你的数据仓库供给新的数据,就像第8章“定期装载”讨论的那样。为了获得一个稳定的数据供应流,你将用你的操作系统平台可能提供的任务计划方式来安排定期装载。本章,第二篇的最后,将向你说明如何在window平台上用任务计划的方式部署定期装载。该计划将执行一个批处理,该批处理依次调用第8章讨论过的那些脚本。
准备批处理文件
第8章讨论的dw_regular.sql脚本用来载入新的数据。该脚本需要以每天为周期运行。列表9-1表示一个批处理文件,该批处理将被调用进而运行dw_regular.sql脚本。
列表9-1:DW 定期装载批处理
mysql.exe -udwid uuserid -ppw -D dw <
c:\mysql\scripts\dw_regular.sql
你需要用正确的用户id和密码来调用mysql.exe命令,并且指定数据库名称和dw_regular.sql脚本的全路径。
安排批处理工作
现在你有一个批处理文件,该文件调用regular_dw.sql脚本,进而用window操作系统的任务计划来部署它。接下来的详细步骤介绍说明如何设置。
1.打开你的控制面板,选择任务计划。
2.点击Select Add Scheduled Task。任务计划向导的第一个屏幕将显示,如图9-1
图9-1:任务计划向导的第一个窗口
3.点击下一步,你将看到任务计划向导第二个窗口,从应用列表中定位到Command Prompt。图9-2 显示的是你选择被计划的应用后的窗口。
4.再次点击下一步,将看的如图9-3所示的任务计划向导第3个窗口。键入“DW Regular Load”作为计划任务的名称,并在“Perform this task:”选项列表中选择“每天”。
5.再次点击下一步,接下来的画面提示你设置一个运行该任务计划的时间点,如图9-4所示:
6.再次点击下一步,显示下面的窗口,如图9-5所示,你将被要求输入一个有权限来运行这个任务的用户名和密码。
7.点击下一步,接下来的画面如图9-6所示。
8.将Open advanced properties for this task when I click Finish选项打勾,然后点击完成。
9.高级设置窗口将显示,如图9-7所示。
10. 点击Browse按钮,浏览文件系统,选择dw_regular_load.bat 文件。
11. 点击Set password按钮,你将看到Set Account Information对话框,如图9-8所示。键入你的用户名和密码点击ok
用于数据仓库定期装载的每日运行的任务计划现在以及配置完成。它将开始在你定义的日期和时间上运行。你可以看到新的图标在任务计划窗口上,如图9-9所示。
小结
本章你学习了如何配置一个定期装载的工作作为window平台的任务计划。完成这个ETL步骤和不同类型的装载包括这个定期装载计划,现在你以及准备好可以让你的数据仓库启动并且运行了。
第三篇:成长
章节列表:
第10章:增加字段
第11章:按需载入
第12章:维表子集
第13章:维度角色扮演
第14章:快照
第15章:唯度层次
第16章:多路径和不齐整的维度层次
第17章:维度退化
第18章:垃圾维
第19章:多星型模式
本篇概述
如果的数据仓库有个成功的开始,你的客户一定想要更多。他们可能要求你载入更多的数据到数据仓库中。到时候,你将为数据仓库的成长做好准备。
第3篇将讨论如何通过以下的方式可以使你扩充你的数据仓库:
n 增加字段和表
n 分离维度
n 根据特殊的目的用不同的方式应用维度
n 增加一个衍生的星型或者一个完整的新的星型
第10章: 增加字段
本章教你一个刚开始的数据仓库最常遇到的扩充情况:向一个现有的维和事实表增加字段。本章将以讨论模式在你需要增加一个或者两个字段的时候会发生的情况为开始。进而向你示范如何能往客户维和销售订单事实表增加一个新的字段。并且向这个新的字段实施SCD2。
扩展模式
本节我将向你展示如何修正数据仓库的模式。图10.1显示扩展后的模式,在customer_dim表和sales_order_fact表中有新增的字段。在customer_dim表中新增的字段是shipping_address, shipping_zip_code, shipping_city,和shipping_state。sales_order_fact表中有一个新增的字段:order_quantity.
列表10-1中的shipping_address.sql在customer_dim和customer_stg表中增加新的字段。在你运行这个脚本前,将你的mysql时间设置为:2007-03-01
图10-1: customer_dim 和 sales_order_fact表中的新字段
列表10-1:向客户维度添加新字段
/*****************************************************************/
/* */
/* shipping_address.sql */
/* */
/*****************************************************************/
USE dw;
ALTER TABLE customer_dim
ADD shipping_address CHAR (50) AFTER customer_state
, ADD shipping_zip_code INT (5) AFTER shipping_address
, ADD shipping_city CHAR (30) AFTER shipping_zip_code
, ADD shipping_state CHAR (2) AFTER shipping_city
;
ALTER TABLE customer_stg
ADD shipping_address CHAR (50) AFTER customer_state
, ADD shipping_zip_code INT (5) AFTER shipping_address
, ADD shipping_city CHAR (30) AFTER shipping_zip_code
, ADD shipping_state CHAR (2) AFTER shipping_city
;
/* end of script */
你可以用如下命令运行上述脚本:
mysql> \. c:\mysql\scripts\shipping_address.sql
你的msql控制台上将打印出如下信息:
Database changed
Query OK, 9 rows affected (0.73 sec)
Records: 9 Duplicates: 0 Warnings: 0
Query OK, 8 rows affected (0.49 sec)
Records: 8 Duplicates: 0 Warnings: 0
为了确保新的字段已经加入,查询customer_dim表:
mysql> select * from customer_dim \G
下面是查询结果:
*************************** 1. row ***************************
customer_sk: 1
customer_number: 1
customer_name: Really Large Customers
customer_street_address: 7500 Louise Dr.
customer_zip_code: 17050
customer_city: Mechanicsburg
customer_state: PA
shipping_address: NULL
shipping_zip_code: NULL
shipping_city: NULL
shipping_state: NULL
effective_date: 2005–03–01
expiry_date: 9999–12–31
*************************** 2. row ***************************
customer_sk: 2
customer_number: 2
customer_name: Small Stores
customer_street_address: 2500 Woodland St.
customer_zip_code: 17055
customer_city: Pittsburgh
customer_state: PA
shipping_address: NULL
shipping_zip_code: NULL
shipping_city: NULL
shipping_state: NULL
effective_date: 2005–03–01
expiry_date: 9999–12–31
*************************** 3. row ***************************
customer_sk: 3
customer_number: 3
customer_name: Medium Retailers
customer_street_address: 1111 Ritter Rd.
customer_zip_code: 17055
customer_city: Pittsburgh
customer_state: PA
shipping_address: NULL
shipping_zip_code: NULL
shipping_city: NULL
shipping_state: NULL
effective_date: 2005–03–01
expiry_date: 9999–12–31
*************************** 4. row ***************************
customer_sk: 4
customer_number: 4
customer_name: Good Companies
customer_street_address: 9500 Scott St.
customer_zip_code: 17050
customer_city: Mechanicsburg
customer_state: PA
shipping_address: NULL
shipping_zip_code: NULL
shipping_city: NULL
shipping_state: NULL
effective date: 2005–03–01
expiry_date: 9999–12–31
*************************** 5. row ***************************
customer_sk: 5
customer_number: 5
customer_name: Wonderful Shops
customer_street_address: 3333 Rossmoyne Rd.
customer_zip_code: 17050
customer_city: Mechanicsburg
customer_state: PA
shipping_address: NULL
shipping_zip_code: NULL
shipping_city: NULL
shipping_state: NULL
effective_date: 2005–03–01
expiry_date: 9999–12–31
*************************** 6. row ***************************
customer_sk: 6
customer_number: 6
customer_name: Extremely Loyal Clients
customer_street_address: 7070 Ritter Rd.
customer_zip_code: 17055
customer_city: Pittsburgh
customer_state: PA
shipping_address: NULL
shipping_zip_code: NULL
shipping_city: NULL
shipping_state: NULL
effective date: 2005–03–01
expiry_date: 2007–02–28
*************************** 7. row ***************************
customer_sk: 7
customer_number: 7
customer_name: Distinguished Agencies
customer_street_address: 9999 Scott St.
customer_zip_code: 17050
customer_city: Mechanicsburg
customer_state: PA
shipping_address: NULL
shipping_zip_code: NULL
shipping_city: NULL
shipping_state: NULL
effective date: 2005–03–01
expiry_date: 9999–12–31
*************************** 8. row ***************************
customer_sk: 8
customer_number: 6
customer_name: Extremely Loyal Clients
customer_street_address: 7777 Ritter Rd.
customer_zip_code: 17055
customer_city: Pittsburgh
customer_state: PA
shipping_address: NULL
shipping_zip_code: NULL
shipping_city: NULL
shipping_state: NULL
effective_date: 2007–03–01
expiry_date: 9999–12–31
*************************** 9. row ***************************
customer_sk: 9
customer_number: 8
customer_name: Subsidiaries
customer_street_address: 10000 Wetline Blvd.
customer_zip_code: 17055
customer_city: Pittsburgh
customer_state: PA
shipping_address: NULL
shipping_zip_code: NULL
shipping_city: NULL
shipping_state: NULL
effective_date: 2007–03–01
expiry_date: 9999–12–31
9 rows in set (0.00 sec)
注:新的字段还没有装载数据,所有它们的值都是空(NULL)
列表10-2中的order_quantity.sql脚本向sales_prder_fact表增加order_quantity字段。
列表10-2:添加order_quantity字段
/*****************************************************************/
/* */
/* order_quantity.sql */
/* */
/*****************************************************************/
USE dw;
ALTER TABLE sales_order_fact
ADD order_quantity INT AFTER order_amount
;
/* end of script */
你可以用如下的命令运行上述脚本:
mysql> \. c:\mysql\scripts\order_quantity.sql
回车后你在控制台上可以看到:
Database changed
Query OK, 35 rows affected (0.56 sec)
Records: 35 Duplicates: 0 Warnings: 0
为了确保新的字段被加入,查询sales_order_fact表
mysql> select order_sk osk, customer_sk csk, product_sk psk,
order_date_sk odsk,
-> order_amount amt, order_quantity qty
-> from sales_order_fact;
结果如下:
+—–+—–+—–+——+———+——+
| osk | csk | psk | odsk | amt | qty |
+—–+—–+—–+——+———+——+
| 1 | 3 | 3 | 1 | 4000.00 | NULL |
| 2 | 4 | 1 | 46 | 4000.00 | NULL |
| 3 | 5 | 2 | 81 | 6000.00 | NULL |
| 4 | 6 | 3 | 152 | 6000.00 | NULL |
| 5 | 7 | 1 | 185 | 8000.00 | NULL |
| 6 | 1 | 2 | 255 | 8000.00 | NULL |
| 7 | 2 | 3 | 311 | 1000.00 | NULL |
| 8 | 3 | 1 | 347 | 1000.00 | NULL |
| 9 | 4 | 2 | 380 | 2000.00 | NULL |
| 10 | 5 | 3 | 416 | 2500.00 | NULL |
| 11 | 6 | 1 | 456 | 3000.00 | NULL |
| 12 | 7 | 2 | 458 | 3500.00 | NULL |
| 13 | 1 | 3 | 502 | 4000.00 | NULL |
| 14 | 2 | 1 | 548 | 4500.00 | NULL |
| 15 | 3 | 2 | 554 | 1000.00 | NULL |
| 16 | 4 | 3 | 584 | 1000.00 | NULL |
| 17 | 5 | 1 | 681 | 4000.00 | NULL |
| 18 | 6 | 2 | 722 | 4000.00 | NULL |
| 19 | 7 | 3 | 730 | 4000.00 | NULL |
| 20 | 1 | 1 | 731 | 1000.00 | NULL |
| 21 | 2 | 2 | 731 | 2000.00 | NULL |
| 22 | 3 | 4 | 731 | 3000.00 | NULL |
| 23 | 4 | 5 | 731 | 4000.00 | NULL |
| 24 | 5 | 2 | 731 | 1000.00 | NULL |
| 25 | 8 | 2 | 731 | 3000.00 | NULL |
| 26 | 7 | 4 | 731 | 5000.00 | NULL |
| 27 | 9 | 5 | 731 | 7000.00 | NULL |
| 28 | 1 | 1 | 731 | 1000.00 | NULL |
| 29 | 2 | 2 | 731 | 2000.00 | NULL |
| 30 | 3 | 4 | 731 | 4000.00 | NULL |
| 31 | 4 | 5 | 731 | 6000.00 | NULL |
| 32 | 5 | 1 | 731 | 2500.00 | NULL |
| 33 | 8 | 2 | 731 | 5000.00 | NULL |
| 34 | 7 | 4 | 731 | 7500.00 | NULL |
| 35 | 9 | 5 | 731 | 1000.00 | NULL |
+—–+—–+—–+——+———+——+
35 rows in set (0.00 sec)
修正定期装载脚本
当你修改了数据库模式后,你需要修正你一直用于定期装载的脚本。列表10-3表示修正后的定期装载脚本。
让我们假设源数据仅仅提供活跃客户的海运地址;客户表中非活跃客户的海运地址将是空的。因此你需要更新数据仓库中现有的用户信息,当他们的海运地址在源数据中有效的时候。如果用户还是希望你维护海运地址的历史信息,你将对海运地址字段应用SCD2。
我们还假设订单数量在销售订单源数据中是有效的,而且客户允许那些已经在数据仓库中的销售订单记录将不用更新。
列表10-3:修正dw的每日定期装载
/*****************************************************************/
/* */
/* dw_regular_10.sql */
/* */
/*****************************************************************/
USE dw;
/* CUSTOMER_DIM POPULATION */
TRUNCATE customer_stg;
LOAD DATA INFILE ‘customer.csv’
INTO TABLE customer_stg
FIELDS TERMINATED BY ‘ , ‘
OPTIONALLY ENCLOSED BY ‘ ” ‘
LINES TERMINATED BY ‘\r\n’
IGNORE 1 LINES
( customer_number
, customer_name
, customer_street_address
, customer_zip_code
, customer_city
, customer_state
, shipping_address
, shipping_zip_code
, shipping_city
, shipping_state )
;
/* SCD 2 ON ADDRESSES */
UPDATE
customer_dim a
, customer_stg b
SET
a.expiry_date = SUBDATE (CURRENT_DATE, 1)
WHERE
a.customer_number = b.customer_number
AND ( a.customer_street_address <> b.customer_street_address
OR a.customer_city <> b.customer_city
OR a.customer_zip_code <> b.customer_zip_code
OR a.customer_state <> b.customer_state
OR a.shipping_address <> b.shipping_address
OR a.shipping_city <> b.shipping_city
OR a.shipping_zip_code <> b.shipping_zip_code
OR a.shipping_state <> b.shipping_state
OR a.shipping_address IS NULL
OR a.shipping_city IS NULL
OR a.shipping_zip_code IS NULL
OR a.shipping_state IS NULL)
AND expiry_date = ‘9999-12-31’
;
INSERT INTO customer_dim
SELECT
NULL
, b.customer_number
, b.customer_name
, b.customer_street_address
, b.customer_zip_code
, b.customer_city
, b.customer_state
, b.shipping_address
, b.shipping_zip_code
, b.shipping_city
, b.shipping_state
, CURRENT_DATE
, ‘9999-12-31’
FROM
customer_dim a
, customer_stg b
WHERE
a.customer_number = b.customer_number
AND ( a.customer_street_address <> b.customer_street_address
OR a.customer_city <> b.customer_city
OR a.customer_zip_code <> b.customer_zip_code
OR a.customer_state <> b.customer_state
OR a.shipping_address <> b.shipping_address
OR a.shipping_city <> b.shipping_city
OR a.shipping_zip_code <> b.shipping_zip_code
OR a.shipping_state <> b.shipping_state
OR a.shipping_address IS NULL
OR a.shipping_city IS NULL
OR a.shipping_zip_code IS NULL
OR a.shipping_state IS NULL)
AND EXISTS (
SELECT *
FROM customer_dim x
WHERE b.customer_number = x.customer_number
AND a.expiry_date = SUBDATE (CURRENT_DATE, 1))
AND NOT EXISTS (
SELECT *
FROM customer_dim y
WHERE b.customer_number = y.customer_number
AND y.expiry_date = ‘9999–12–31’)
;
/* END OF SCD 2 */
/* SCD 1 ON NAME */
UPDATE customer_dim a, customer_stg b
SET a.customer_name = b.customer_name
WHERE a.customer_number = b.customer_number
AND a.expiry_date = ‘9999–12–31’
AND a.customer_name <> b.customer_name
;
/* ADD NEW CUSTOMER */
INSERT INTO customer dim
SELECT
NULL
, customer_number
, customer_name
, customer_street_address
, customer_zip_code
, customer_city
, customer_state
, shipping_address
, shipping_zip_code
, shipping_city
, shipping_state
, CURRENT_DATE
, ‘9999–12–31’
FROM customer_stg
WHERE customer_number NOT IN(
SELECT a.customer_number
FROM
customer_dim a
, customer_stg b
WHERE b.customer_number = a.customer_number )
;
/* END OF CUSTOMER_DIM POPULATION */
/* product dimension loading */
TRUNCATE product_stg
;
LOAD DATA INFILE ‘product.txt’
INTO TABLE product_stg
FIELDS TERMINATED BY ‘ ‘
OPTIONALLY ENCLOSED BY ‘ ‘
LINES TERMINATED BY ‘\r\n’
IGNORE 1 LINES
( product_code
, product_name
, product_category )
;
/* PRODUCT_DIM POPULATION */
/* SCD2 ON PRODUCT NAME AND GROUP */
UPDATE
product_dim a
, product_stg b
SET
expiry_date = SUBDATE (CURRENT_DATE, 1)
WHERE
a.product_code = b.product_code
AND ( a.product_name <> b.product_name
OR a.product_category <> b.product_category
AND expiry_date = ‘9999–12–31’
;
INSERT INTO product_dim
SELECT
NULL
, b.product_code
, b.product_name
, b.product_category
, CURRENT_DATE
, ‘9999–12–31’
FROM
product_dim a
, product_stg b
WHERE
a.product_code = b.product_code
AND ( a.product_name <> b.product_name
OR a.product_category <> b.product_category )
AND EXISTS (
SELECT *
FROM product_dim x
WHERE b.product_code = x.product_code
AND a.expiry_date = SUBDATE (CURRENT_DATE, 1)
AND NOT EXISTS (
SELECT *
FROM product_dim y
WHERE b.product_code = y.product_code
AND y.expiry_date = ‘9999–12–31’)
;
/* END OF SCD 2 */
/* ADD NEW PRODUCT */
INSERT INTO product_dim
SELECT
NULL
, product_code
, product_name
, product_category
, CURRENT_DATE
, ‘9999–12–31’
FROM product_stg
WHERE product_code NOT IN(
SELECT y.product_code
FROM product_dim x, product_stg y
WHERE x.product_code = y.product_code )
;
/* END OF PRODUCT_DIM POPULATION */
/* ORDER_DIM POPULATION */
INSERT INTO order_dim (
order_sk
, order_number
, effective_date
, expiry_date
)
SELECT
NULL
, order_number
, order_date
, ‘9999–12–31’
FROM source.sales_order
WHERE entry_date = CURRENT_DATE
;
/* END OF ORDER_DIM POPULATION */
/* SALES_ORDER_FACT POPULATION */
INSERT INTO sales_order_fact
SELECT
order_sk
, customer_sk
, product_sk
, date_sk
, order_amount
, order_quantity
FROM
source.sales order a
, order_dim b
, customer_dim c
, product_dim d
, date_dim e
WHERE
a.order_number = b.order_number
AND a.customer_number = c.customer_number
AND a.order_date >= c.effective_date
AND a.order_date <= c.expiry_date
AND a.product_code = d.product_code
AND a.order_date >= d.effective_date
AND a.order_date <= d.expiry_date
AND a.order_date = e.date
AND a.entry_date = CURRENT_DATE
;
/* end of script */
下一节将对如何准备必需的数据以使得列表10-3的脚本可以成功运行进行说明。
测试
在你可以运行列表10-3所示的脚本之前,还有几个事情需要处理。
第一,准备如下所示的客户数据,并保存为名为customer.csv的文件。相对于之前客户数据所发生的变化是:
n 现有全部8个客户的海运地址信息是有效的。
n 客户编码为9的记录是新的客户资料,也包含自己的海运地址。
CUSTOMER NO, CUSTOMER NAME, STREET ADDRESS, ZIP CODE, CITY, STATE,
SHIPPING ADDRESS, ZIP CODE, CITY, STATE
1, Really Large Customers, 7500 Louise Dr., 17050, Mechanicsburg, PA, 7500
Louise Dr., 17050, Mechanicsburg, PA
2, Small Stores, 2500 Woodland St., 17055, Pittsburgh, PA, 2500 Woodland
St., 17055, Pittsburgh, PA
3, Medium Retailers, 1111 Ritter Rd., 17055, Pittsburgh, PA, 1111 Ritter
Rd., 17055, Pittsburgh, PA
4, Good Companies, 9500 Scott St., 17050, Mechanicsburg, PA, 9500 Scott
St., 17050, Mechanicsburg, PA
5, Wonderful Shops, 3333 Rossmoyne Rd., 17050, Mechanicsburg, PA, 3333
Rossmoyne Rd., 17050, Mechanicsburg, PA
6, Extremely Loyal Clients, 7777 Ritter Rd., 17055, Pittsburgh, PA, 7777
Ritter Rd., 17055, Pittsburgh, PA
7, Distinguished Agencies, 9999 Scott St., 17050, Mechanicsburg, PA, 9999
Scott St., 17050, Mechanicsburg, PA
8, Subsidiaries, 10000 Wetline Blvd., 17055, Pittsburgh, PA, 10000 Wetline
Blvd., 17055, Pittsburgh, PA
9, Online Distributors, 2323 Louise Dr., 17055, Pittsburgh, PA, 2323
Louise Dr., 17055, Pittsburgh, PA
注:你仍然需要前面测试中用到的product.txt文件,尽管这里它没有什么变化。因为定期装载任务运行时,需要访问所有的源数据,这些源数据就包括product.txt文件。
第二件你必须作的事情是应用列表10-4中所示的sales_order_quantity_data.sql脚本向source数据库中的sales_order表新增order_quantity字段。这个新增的字段中的数据就是sales_order_fac事实表中新增的order_quantity字段的源数据。
列表10-4:向sales_order表添加order_quantity字段
/*****************************************************************/
/* */
/* add_sales_order_quantity.sql */
/* */
/*****************************************************************/
USE source;
ALTER TABLE sales_order
ADD order_quantity INT AFTER order_amount
;
/* end of script */
用如下命令运行该脚本:
mysql> \. c:\mysql\scripts\add_sales_order_quantity.sql
你将在你的控制台上看到:
Database changed
Query OK, 37 rows affected (0.39 sec)
Records: 37 Duplicates: 0 Warnings: 0
现在销售订单源有order_quantity字段,你可以添加销售订单测试数据了。列表10-5中的脚本添加9个销售订单记录到sales_order表中。注意到这些销售订单记录有订单数量,并且他们的订单日期是2007-03-02。
列表10-5:新增带有订单数量信息的9条销售订单记录
/*****************************************************************/
/* */
/* sales_order_quantity_data.sql */
/* */
/*****************************************************************/
USE source;
INSERT INTO sales_order VALUES
(38, 1, 1, ‘2007-03-02’, ‘2007-03-02’, 1000, 10)
, (39, 2, 2, ‘2007-03-02’, ‘2007-03-02’, 2000, 20)
, (40, 3, 3, ‘2007-03-02’, ‘2007-03-02’, 4000, 40)
, (41, 4, 4, ‘2007-03-02’, ‘2007-03-02’, 6000, 60)
, (42, 5, 1, ‘2007-03-02’, ‘2007-03-02’, 2500, 25)
, (43, 6, 2, ‘2007-03-02’, ‘2007-03-02’, 5000, 50)
, (44, 7, 3, ‘2007-03-02’, ‘2007-03-02’, 7500, 75)
, (45, 8, 4, ‘2007-03-02’, ‘2007-03-02’, 1000, 10)
, (46, 9, 1, ‘2007-03-02’, ‘2007-03-02’, 1000, 10)
;
/* end of script */
接下来,你需要设置你的mysql日期为测试数据的订单日期,也就是2007-03-02。
现在,你已经准备好可以运行列表10-3中的dw_regular_10.sql脚本。你可以调用一下命令:
mysql> \. c:\mysql\scripts\dw_regular_10.sql
你将看到:
Database changed
Query OK, 8 rows affected (0.05 sec)
Query OK, 9 rows affected (0.06 sec)
Records: 9 Deleted: 0 Skipped: 0 Warnings: 0
Query OK, 8 rows affected (0.06 sec)
Rows matched: 8 Changed: 8 Warnings: 0
Query OK, 8 rows affected (0.05 sec)
Records: 8 Duplicates: 0 Warnings: 0
Query OK, 0 rows affected (0.01 sec)
Rows matched: 0 Changed: 0 Warnings: 0
Query OK, 1 row affected (0.07 sec)
Records: 1 Duplicates: 0 Warnings: 0
Query OK, 4 rows affected (0.05 sec)
Query OK, 4 rows affected (0.06 sec)
Records: 4 Deleted: 0 Skipped: 0 Warnings: 0
Query OK, 0 rows affected (0.00 sec)
Rows matched: 0 Changed: 0 Warnings: 0
Query OK, 0 rows affected (0.00 sec)
Records: 0 Duplicates: 0 Warnings: 0
Query OK, 0 rows affected (0.00 sec)
Records: 0 Duplicates: 0 Warnings: 0
Query OK, 9 rows affected (0.07 sec)
Records: 9 Duplicates: 0 Warnings: 0
Query OK, 9 rows affected (0.10 sec)
Records: 9 Duplicates: 0 Warnings: 0
你可以用sql语句查询客户维表来确定这个修正后的定期装载脚本已经成功执行。
mysql> select customer_number no, customer_name name,
-> shipping_city, shipping_zip_code zip, shipping_state st,
-> effective_date eff, expiry_date exp
-> from customer_dim \G
修正后的定期装载脚本成功运行后将会给你如下的结果:
******************************1.row************************
no: 1
name: Really Large Customers
shipping_city: NULL
zip: NULL
st: NULL
eff: 2005-03-01
exp: 2007-03-01
******************************2. row *************************
no: 2
name: Small Stores
shipping_city: NULL
zip: NULL
st: NULL
eff: 2005-03-01
exp: 2007-03-01
******************************3. row *************************
no: 3
name: Medium Retailers
shipping_city: NULL
zip: NULL
st: NULL
eff: 2005-03-01
exp: 2007-03-01
******************************4. row **************************
no: 4
name: Good Companies
shipping_city: NULL
zip: NULL
st: NULL
eff: 2005-03-01
exp: 2007-03-01
******************************5. row ***************************
no: 5
name: Wonderful Shops
shipping_city: NULL
zip: NULL
st: NULL
eff: 2005-03-01
exp: 2007-03-01
******************************6. row ***************************
no: 6
name: Extremely Loyal Clients
shipping_city: NULL
zip: NULL
st: NULL
eff: 2005-03-01
exp: 2007-02-28
*************************** 7. row ***************************
no: 7
name: Distinguished Agencies
shipping_city: NULL
zip: NULL
st: NULL
eff: 2005-03-01
exp: 2007-03-01
*************************** 8. row ***************************
no: 6
name: Extremely Loyal Clients
shipping_city: NULL
zip: NULL
st: NULL
eff: 2007-03-01
exp: 2007-03-01
*************************** 9. row ***************************
no: 8
name: Subsidiaries
shipping_city: NULL
zip: NULL
st: NULL
eff: 2007-03-01
exp: 2007-03-01
*************************** 10. row ***************************
no: 1
name: Really Large Customers
shipping_city: Mechanicsburg
zip: 17050
st: PA
eff: 2007-03-02
exp: 9999-12-31
*************************** 11. row ***************************
no: 2
name: Small Stores
shipping_city: Pittsburgh
zip: 17055
st: PA
eff: 2007-03-02
exp: 9999-12-31
*************************** 12. row ***************************
no: 3
name: Medium Retailers
shipping_city: Pittsburgh
zip: 17055
st: PA
eff: 2007-03-02
exp: 9999-12-31
*************************** 13. row ***************************
no: 4
name: Good Companies
shipping_city: Mechanicsburg
zip: 17050
st: PA
eff: 2007-03-02
exp: 9999-12-31
*************************** 14. 4 row ***************************
no: 5
name: Wonderful Shops
shipping_city: Mechanicsburg
zip: 17050
st: PA
eff: 2007-03-02
exp: 9999-12-31
*************************** 15. 5 row ***************************
no: 6
name: Extremely Loyal Clients
shipping_city: Pittsburgh
zip: 17055
st: PA
eff: 2007-03-02
exp: 9999-12-31
*************************** 16. row ***************************
no: 7
name: Distinguished Agencies
shipping_city: Mechanicsburg
zip: 17050
st: PA
eff: 2007-03-02
exp: 9999-12-31
*************************** 17. row ***************************
no: 8
name: Subsidiaries
shipping_city: Pittsburgh
zip: 17055
st: PA
eff: 2007-03-02
exp: 9999-12-31
*************************** 18. row ***************************
no: 9
name: Online Distributors
shipping_city: Pittsburgh
zip: 17055
st: PA
eff: 2007-03-02
exp: 9999-12-31
18 rows in set (0.00 sec)
注 现有全部的客户记录都有海运地址,而那些老的(过期)的记录则没有。客户编号9的记录已经被添加,并且有一个海运地址信息。
为了确保销售数据被装载成功,查询用sql语句查询sales_order_fact表:
mysql> select order_sk o_sk, customer_sk c_sk, product_sk p_sk,
order_date_sk od_sk,
-> order_amount amt, order_quantity qty
-> from sales_order_fact;
这里是事实表的内容。
+——+——+——+——-+———+——+
| o_sk | c_sk | p_sk | od_sk | amt | qty |
+——+——+——+——-+———+——+
| 1 | 3 | 3 | 1 | 4000.00 | NULL |
| 2 | 4 | 1 | 46 | 4000.00 | NULL |
| 3 | 5 | 2 | 81 | 6000.00 | NULL |
| 4 | 6 | 3 | 152 | 6000.00| NULL |
| 5 | 7 | 1 | 185 | 8000.00 | NULL |
| 6 | 1 | 2 | 255 | 8000.00 | NULL |
| 7 | 2 | 3 | 311 | 1000.00 | NULL |
| 8 | 3 | 1 | 347 | 1000.00 | NULL |
| 9 | 4 | 2 | 380 | 2000.00 | NULL |
| 10 | 5 | 3 | 416 | 2500.00 | NULL |
| 11 | 6 | 1 | 456 | 3000.00 | NULL |
| 12 | 7 | 2 | 458 | 3500.00 | NULL |
| 13 | 1 | 3 | 502 | 4000.00 | NULL |
| 14 | 2 | 1 | 548 | 4500.00 | NULL |
| 15 | 3 | 2 | 554 | 1000.00 | NULL |
| 16 | 4 | 3 | 584 | 1000.00 | NULL |
| 17 | 5 | 1 | 681 | 4000.00 | NULL |
| 18 | 6 | 2 | 722 | 4000.00 | NULL |
| 19 | 7 | 3 | 730 | 4000.00 | NULL |
| 20 | 1 | 1 | 731 | 1000.00 | NULL |
| 21 | 2 | 2 | 731 | 2000.00 | NULL |
| 22 | 3 | 4 | 731 | 3000.00 | NULL |
| 23 | 4 | 5 | 731 | 4000.00 | NULL |
| 24 | 5 | 2 | 731 | 1000.00 | NULL |
| 25 | 8 | 2 | 731 | 3000.00 | NULL |
| 26 | 7 | 4 | 731 | 5000.00 | NULL |
| 27 | 9 | 5 | 731 | 7000.00 | NULL |
| 28 | 1 | 1 | 731 | 1000.00 | NULL |
| 29 | 2 | 2 | 731 | 2000.00 | NULL |
| 30 | 3 | 4 | 731 | 4000.00 | NULL |
| 31 | 4 | 5 | 731 | 6000.00 | NULL |
| 32 | 5 | 1 | 731 | 2500.00 | NULL |
| 33 | 8 | 2 | 731 | 5000.00 | NULL |
| 34 | 7 | 4 | 731 | 7500.00 | NULL |
| 35 | 9 | 5 | 731 | 1000.00 | NULL |
| 36 | 10 | 1 | 732 | 1000.00 | 10 |
| 37 | 11 | 2 | 732 | 2000.00 | 20 |
| 38 | 12 | 4 | 732 | 4000.00 | 40 |
| 39 | 13 | 5 | 732 | 6000.00 | 60 |
| 40 | 14 | 1 | 732 | 2500.00 | 25 |
| 41 | 15 | 2 | 732 | 5000.00 | 50 |
| 42 | 16 | 4 | 732 | 7500.00 | 75 |
| 43 | 17 | 5 | 732 | 1000.00 | 10 |
| 44 | 18 | 1 | 732 | 1000.00 | 10 |
+——+——+——+——-+———+——+
44 rows in set (0.00 sec)
注 只有9条新的记录有订单数量,老的记录则没有。
小结
本章你学习了通过向维度表和事实表增加新的字段来扩展数据仓库。下一章,你将考察另外一种类型的装载,按需装载,这是为了适应数据仓库发展需要你可能需要应用的。
第11章:按需装载
你已经在第7章学习了初始化装载以及在第8章学习了定期装载。这里还有另外一种装载类型,按需装载,是你必须熟悉的。当你需要在计划之外装载源数据,你将应用按需装载。例如,只有当有促销安排时,促销销售源数据才可用,而其他情况则是不可用的。
关于日期的预装载在第6章已经讨论过,“装载日期维”可以认为是按需装载的一种类型。你用日期数据预装载了你的数据仓库,当这些日期数据不够用时,你必须再次执行预装载。
增强模式
在本节,我将用一个促销销售情景来解释按需装载。定期导入在这里是不合适的,因为这些数据的装载计划是无法预计的。下面是需要被载入的一个csv平面文件的内容:
PROMOTION CODE,PROMOTION NAME,START DATE,LAST DATE
SO,Special Offer,2007-04-01,2007-04-10
DP,Disk Promotion,2007-05-05,2007-05-20
MS,Month Special,2007-06-01,2007-06-30
MS,Monitor Promotion,2007-07-10,2007-07-15
BS,Back to School,2007-08-10,2007-08-30
要注意的是源数据表示了几个促销时期,而不是单独的促销日期。这里假设你的客户需要载入的仅仅是将来新的促销数据,他们不需要在数据仓库中存储以往的促销历史记录。
首先你需要在date_dim表中增加一个新的字段,promo_ind(作为促销指示)。增加字段后的新的模式如图11.1所示。
图11-1:在date_dim表中新增promo_ind字段
你可以用类表11-1所示的的promotion_indicator.sql脚本,在date_dim表中新增promo_ind字段。
列表11-1:促销指示
/*****************************************************************/
/* */
/* promotion_indicator.sql */
/* */
/*****************************************************************/
USE dw;
ALTER TABLE date_dim
Performing On-Demand Population
ADD promo_ind CHAR (1) AFTER year
;
/* end of script */
你可以用以下命令运行该脚本:
mysql> \. c:\mysql\scripts\promotion_indicator.sql
在你运行该脚本后,你的控制台会出现:
Database changed
Query OK, 2132 rows affected (0.45 sec)
Records: 2132 Duplicates: 0 Warnings: 0
用如下sql语句查询日期维表以,确定在新增的promo_ind字段中无数据。
mysql> select * from date_dim where promo_ind IS NOT NULL;
该字段的所有数据值都是空,如这里所示:
Empty set (0.01 sec)
执行按需装载
在本节,我将用一个销售促销作为例子来向你展示,如何执行按需装载。实际上,该装载所作的就是, 如果该日期在计划预定的促销期间内,设置date_dim表中相应记录的promo_ind字段值为”Y”。
本例子的按需装载脚本由列表11-2所表示。你可以在日期被载入后运行该脚本。换句话说,所有促销计划,从开始到结束的所有日期在日期维中必须是可用的。
列表11-2:装载促销指示
/*****************************************************************/
/* */
/* on_demand.sql
/* */
/*****************************************************************/
USE dw;
TRUNCATE promo_schedule_stg;
LOAD DATA INFILE ‘promo_schedule.csv’
INTO TABLE promo_schedule_stg
FIELDS TERMINATED BY ‘,’
OPTIONALLY ENCLOSED BY ‘”‘
LINES TERMINATED BY ‘\r\n’
IGNORE 1 LINES
( promo_code
, promo_name
, promo_start_date
, promo_last_date )
;
UPDATE
date_dim a
, promo_schedule_stg b
SET a.promo_ind = ‘Y’
WHERE
a.date >= b.promo_start_date
AND a.date <= b.promo_last_date
;
/* end of script */
列表 11-2中的脚本将promo_schedule.csv的内容载入到promo_schedule_stg表中,因此在你可以运行列表 11-2中的脚本之前,你需要建立promo_schedule_stg表。列表11-3中的脚本可以用来建立这个表。
注:promo_schedule.csv文件将在下一节讨论。
列表11-3: 建立promo_schedule_stg表:
/*****************************************************************/
/* */
/* create_promo_schedule_stg.sql */
/* */
/*****************************************************************/
USE dw;
CREATE TABLE promo_schedule_stg (
promo_code CHAR (2)
, promo_name CHAR (30)
, promo_start_date DATE
, promo_last_date DATE
)
;
/* end of script */
用如下命令运行列表11-3中的脚本
mysql> \. c:\mysql\scripts\create_promo_schedule_stg.sql
你可以看到如下的响应:
Database changed
Query OK, 0 rows affected (0.20 sec)
测试
在你运行列表11-2之前,你需要准备如下的促销计划csv文件并存为promo_schedule.csv。
PROMOTION CODE,PROMOTION NAME,START DATE, LAST DATE
SO,Special Offer,2007-04-01,2007-04-10
DP,Disk Promotion,2007-05-05,2007-05-20
MS,Month Special,2007-06-01,2007-06-30
MS,Monitor Promotion,2007-07-10,2007-07-15
BS,Back to School,2007-08-10,2007-08-30
注:这里有83个促销日期在5个促销期间内。第一个期间有10个日期(2005-04-01, 2005-04-02,…2005-04-10), 第二个期间有16个日期(2005-05-05,2005-05-06…2005-05-20),等等。
你可以用如下命令运行列表11-2中的on_demand.sql脚本。
mysql> \. c:\mysql\scripts\on_demand.sql
这里是mysql控制台的回应。
Database changed
Query OK, 1 row affected (0.05 sec)
Query OK, 5 rows affected (0.06 sec)
Records: 5 Deleted: 0 Skipped: 0 Warnings: 0
Query OK, 83 rows affected (0.08 sec)
Rows matched: 83 Changed: 83 Warnings: 0
你可以通过查询date_dim表中的促销日期来确认执行的结果(促销指示被设置为”Y”)。例如,第一个促销期间的查询将显示10个日期,它们的promo_ind字段的值都是”Y”:
mysql> select * from date_dim where date >= ‘2007-04-01’ and
-> date <= ‘2007-04-10’ \G
结果如下:
*************************** 1. row ***************************
date_sk: 762
date: 2007-04-01
month_name: April
month: 4
quarter: 2
year: 2007
promo_ind: Y
effective_date: 0000-00-00
expiry_date: 9999-12-31
*************************** 2. row ***************************
date_sk: 763
date: 2007-04-02
month_name: April
month: 4
quarter: 2
year: 2007
promo_ind: Y
effective_date: 0000-00-00
expiry_date: 9999-12-31
*************************** 3. row ***************************
date_sk: 764
date: 2007-04-03
month_name: April
month: 4
quarter: 2
year: 2007
promo_ind: Y
effective_date: 0000-00-00
expiry_date: 9999-12-31
*************************** 4. row ***************************
date_sk: 765
date: 2007-04-04
month_name: April
month: 4
quarter: 2
year: 2007
promo_ind: Y
effective_date: 0000-00-00
expiry_date: 9999-12-31
*************************** 5. row ***************************
date_sk: 766
date: 2007-04-05
month_name: April
month: 4
quarter: 2
year: 2007
promo_ind: Y
effective_date: 0000-00-00
expiry_date: 9999-12-31
*************************** 6. row ***************************
date_sk: 767
date: 2007-04-06
month_name: April
month: 4
quarter: 2
year: 2007
promo_ind: Y
effective_date: 0000-00-00
expiry_date: 9999-12-31
*************************** 7. row ***************************
date_sk: 768
date: 2007-04-07
month_name: April
month: 4
quarter: 2
year: 2007
promo_ind: Y
effective_date: 0000-00-00
expiry_date: 9999-12-31
*************************** 8. row ***************************
date sk: 769
date: 2007-04-08
month_name : April
month: 4
quarter : 2
year : 2007
promo_ind: Y
effective_date : 0000-00-00
expiry_date: 9999-12-31
*************************** 9. row ***************************
date_sk: 770
date: 2007-04-09
month_name : April
month : 4
quarter : 2
year : 2007
promo_ind: Y
effective_date : 0000-00-00
expiry_date: 9999-12-31
*************************** 10. row ***************************
date_sk: 771
date: 2007-04-10
month_name : April
month: 4
quarter : 2
year : 2007
promo_ind: Y
effective_date : 0000-00-00
expiry_date: 9999-12-31
10 rows in set (0.01 sec)
小结
本章,你学习了按需装载技术,你可以用它来作为定期装载的补充。对于现有数据仓库的一些扩展,你的客户可能不希望增加任何新的东西,你必须只能对现有的实现重复利用。下一章,你将学习如何重复利用维。
第12章: 子集维度
一些用户从来就不需要大部分的详细数据。例如:他们通常想要某个月的记录而不是某些具体日期的。他们可能对某个地区的数据远比全国的销售数据更感兴趣。这些特定的维是由详细维数据中选取的几个记录组成的,因此称它们为维度的子集。由于维度子集远比详细维度更小,所以它们更加容易被使用而且能提供更快捷的响应。
在本章,你将预备两个特定的维度,这两个维产生自现有的维:月份向上钻取维(日期维的子集)和宾夕法尼亚州地区客户维(一个客户维的子集)。
月份向上钻取维
本节,我将解释月份向上钻取维的装载过程以及它的测试。
列表12-1所示的脚本中,产生了月份向上钻取维表,并通过日期维度初始化装载了月份数据。注意到 promo_ind 字段不包括在该维中。该字段不适用于月份这个级别,因为在一个月份中你可能有不止一个的促销活动。确切的,促销应该应用在日期级别上。
列表 12-1:实现月份向上钻取维
/*****************************************************************/
/* */
/* month_rollup_dim.sql */
/* */
/*****************************************************************/
USE dw;
CREATE TABLE month_dim
( month_sk INT NOT NULL AUTO_INCREMENT PRIMARY KEY
, month_name CHAR (9)
, month INT (2)
, quarter INT (1)
, year INT (4)
, effective_date DATE
, expiry_date DATE )
;
INSERT INTO month_dim
SELECT DISTINCT
NULL
, month_name
, month
, quarter
, year
, effective_date
, expiry_date
FROM date_dim
;
/* end of script */
列表12-1中的脚本产生了month_dim表并向month_dim表插入不重复的月份数据,这些数据包含了从2005-03-01到2010-12-31这段时期的月份数据。当你通过运行列表12-1中的脚本装载month_dim表后,该表将有70个月份(从2005-03-01到2010-12-31)。
用如下方式运行该脚本:
mysql> \. c:\mysql\scripts\month_rollup_dim.sql
你将在你的控制台上看到:
Database changed
Query OK, 0 rows affected (0.14 sec)
Query OK, 70 rows affected (0.07 sec)
Records: 70 Duplicates: 0 Warnings: 0
mysql>
现在查询month_dim表以确保装载正确:
mysql> select month_sk msk, month_name, month, quarter q, year,
-> effective_date efdate, expiry_date exdate
-> from month_dim;
查询结果是:
+——+————-+——–+—-+——–+————+————+ -+
| msk | month_name | month | q | year | efdate | exdate |
+——+————-+——–+—-+——–+————+————+
| 1 | March | 3 | 1 | 2005 | 0000-00-00 | 9999-12-31 |
| 2 | April | 4 | 2 | 2005 | 0000-00-00 | 9999-12-31 |
| 3 | May | 5 | 2 | 2005 | 0000-00-00 | 9999-12-31 |
| 4 | June | 6 | 2 | 2005 | 0000-00-00 | 9999-12-31 |
| 6 | August | 8 | 3 | 2005 | 0000-00-00 | 9999-12-31 |
| 5 | July | 7 | 3 | 2005 | 0000-00-00 | 9999-12-31 |
| 7 | September | 9 | 3 | 2005 | 0000-00-00 | 9999-12-31 |
| 9 | November | 11 | 4 | 2005 | 0000-00-00 | 9999-12-31 |
| 8 | October | 10 | 4 | 2005 | 0000-00-00 | 9999-12-31 |
| 10 | December | 12 | 4 | 2005 | 0000-00-00 | 9999-12-31 |
| 11 | January | 1 | 1 | 2006 | 0000-00-00 | 9999-12-31 |
| 12 | February | 2 | 1 | 2006 | 0000-00-00 | 9999-12-31 |
| 13 | March | 3 | 1 | 2006 | 0000-00-00 | 9999-12-31 |
| 14 | April | 4 | 2 | 2006 | 0000-00-00 | 9999-12-31 |
| 15 | May | 5 | 2 | 2006 | 0000-00-00 | 9999-12-31 |
| 16 | June | 6 | 2 | 2006 | 0000-00-00 | 999 -12-31 |
| 17 | July | 7 | 3 | 2006 | 0000-00-00 | 9999-12-31 |
| 18 | August | 8 | 3 | 2006 | 0000-00-00 | 9999-12-31 |
| 19 | September | 9 | 3 | 2006 | 0000-00-00 | 9999-12-31 |
| 20 | October | 10 | 4 | 2006 | 0000-00-00 | 9999-12-31 |
| 21 | November | 11 | 4 | 2006 | 0000-00-00 | 9999-12-31 |
| 22 | December | 12 | 4 | 2006 | 0000-00-00 | 9999-12-31 |
| 23 | January | 1 | 1 | 2007 | 0000-00-00 | 9999-12-31 |
| 24 | February | 2 | 1 | 2007 | 0000-00-00 | 9999-12-31 |
| 26 | April | 4 | 2 | 2007 | 0000-00-00 | 9999-12-31 |
| 25 | March | 3 | 1 | 2007 | 0000-00-00 | 9999-12-31 |
| 27 | May | 5 | 2 | 2007 | 0000-00-00 | 9999-12-31 |
| 28 | June | 6 | 2 | 2007 | 0000-00-00 | 9999-12-31 |
| 29 | July | 7 | 3 | 2007 | 0000-00-00 | 9999-12-31 |
| 30 | August | 8 | 3 | 2007 | 0000-00-00 | 9999-12-31 |
| 31 | September | 9 | 3 | 2007 | 0000-00-00 | 9999-12-31 |
| 32 | October | 10 | 4 | 2007 | 0000-00-00 | 9999-12-31 |
| 33 | November | 11 | 4 | 2007 | 0000-00-00 | 9999-12-31 |
| 34 | December | 12 | 4 | 2007 | 0000-00-00 | 9999-12-31 |
| 35 | January | 1 | 1 | 2008 | 0000-00-00 | 9999-12-31 |
| 36 | February | 2 | 1 | 2008 | 0000-00-00 | 9999-12-31 |
| 37 | March | 3 | 1 | 2008 | 0000-00-00 | 9999-12-31 |
| 38 | April | 4 | 2 | 2008 | 0000-00-00 | 9999-12-31 |
| 39 | May | 5 | 2 | 2008 | 0000-00-00 | 9999-12-31 |
| 40 | June | 6 | 2 | 2008 | 0000-00-00 | 9999-12-31 |
| 41 | July | 7 | 3 | 2008 | 0000-00-00 | 9999-12-31 |
| 42 | August | 8 | 3 | 2008 | 0000-00-00 | 9999-12-31 |
| 43 | September | 9 | 3 | 2008 | 0000-00-00 | 9999-12-31 |
| 44 | October | 10 | 4 | 2008 | 0000-00-00 | 9999-12-31 |
| 45 | November | 11 | 4 | 2008 | 0000-00-00 | 9999-12-31 |
| 46 | December | 12 | 4 | 2008 | 0000-00-00 | 9999-12-31 |
| 47 | January | 1 | 1 | 2009 | 0000-00-00 | 9999-12-31 |
| 48 | February | 2 | 1 | 2009 | 0000-00-00 | 9999-12-31 |
| 49 | March | 3 | 1 | 2009 | 0000-00-00 | 9999-12-31 |
| 50 | April | 4 | 2 | 2009 | 0000-00-00 | 9999-12-31 |
| 51 | May | 5 | 2 | 2009 | 0000-00-00 | 9999-12-31 |
| 52 | June | 6 | 2 | 2009 | 0000-00-00 | 9999-12-31 |
| 53 | July | 7 | 3 | 2009 | 0000-00-00 | 9999-12-31 |
| 54 | August | 8 | 3 | 2009 | 0000-00-00 | 9999-12-31 |
| 55 | September | 9 | 3 | 2009 | 0000-00-00 | 9999-12-31 |
| 56 | October | 10 | 4 | 2009 | 0000-00-00 | 9999-12-31 |
| 57 | November | 11 | 4 | 2009 | 0000-00-00 | 9999-12-31 |
| 58 | December | 12 | 4 | 2009 | 0000-00-00 | 9999-12-31 |
| 59 | January | 1 | 1 | 2010 | 0000-00-00 | 9999-12-31 |
| 60 | February | 2 | 1 | 2010 | 0000-00-00 | 9999-12-31 |
| 61 | March | 3 | 1 | 2010 | 0000-00-00 | 9999-12-31 |
| 62 | April | 4 2 | 2010 | 0000-00-00 | 9999-12-31 |
| 63 | May | 5 | 2 | 2010 | 0000-00-00 | 9999-12-31 |
| 64 | June | 6 | 2 | 2010 | 0000-00-00 | 9999-12-31 |
| 65 | July | 7 | 3 | 2010 | 0000-00-00 | 9999-12-31 |
| 66 | August | 8 | 3 | 2010 | 0000-00-00 | 9999-12-31 |
| 67 | September | 9 | 3 | 2010 | 0000-00-00 | 9999-12-31 |
| 68 | October | 10 | 4 | 2010 | 0000-00-00 | 9999-12-31 |
| 69 | November | 11 | 4 | 2010 | 0000-00-00 | 9999-12-31 |
| 70 | December | 12 | 4 | 2010 | 0000-00-00 | 9999-12-31 |
+——+————-+——–+—-+——–+————+———–70 rows in set (0.00 sec)
为了能够定期的从日期维载入数据到月份维,你可以插入它的装载脚本到日期维度的装载脚本中。你需要更新之前在第6章讨论过的日期维装载的存储过程。修改后的存储过程如列表12-2所示。修改的地方用粗体表示。现在,只要在装载一个新的日期条目并且该日期的月份没有在月份维表中的时候,该修改后的脚本将能够装载月份向上钻取维表。
列表 12-2:修正后的日期预装载脚本
/*****************************************************************/
/* */
/* pre_populate_date_12.sql */
/* */
/*****************************************************************/
USE dw;
DELIMITER // ;
DROP PROCEDURE IF EXISTS pre_populate_date //
CREATE PROCEDURE pre_populate_date (IN start_dt DATE, IN end_dt
DATE)
BEGIN
WHILE start_dt <= end_dt DO
INSERT INTO date_dim(
date_sk
, date
, month_name
, month
, quarter
, year
, effective_date
, expiry_date
)
VALUES(
NULL
, start_dt
, MONTHNAME (start_dt)
, MONTH (start_dt)
, QUARTER (start_dt)
, YEAR (start_dt)
, ‘0000-00-00’
, ‘9999-12-31’
)
;
SET start_dt = ADDDATE (start_dt, 1);
END WHILE;
INSERT INTO month_dim
SELECT DISTINCT
NULL
, month_name
, month
, quarter
, year
, effective_date
, expiry_date
FROM date_dim
WHERE CONCAT (month, year) NOT IN
(SELECT CONCAT (month, year) FROM month_dim)
;
END
//
DELIMITER ; //
/* end of script */
用如下命令调用列表12-2所示的脚本来重新编译该存储过程:
mysql> \. c:\mysql\scripts\pre_populate_date_12.sql
你将在你的控制台上看到如下的响应信息:
Database changed
Query OK, 0 rows affected (0.22 sec)
Query OK, 0 rows affected (0.04 sec)
为了测试该修正后的预装载日期机制,运行该存储过程来增加从2011-01-01 到 2011-12-31期间的日期,用如下的命令方式:
mysql> call pre_populate_date (‘2011-01-01’, ‘2011-12-31’);
Mysql将显示有12个记录生效。
Query OK, 12 rows affected (23.07 sec)
为了确认12个月份数据被正确载入,用该语句查询month_dim表:
mysql> select * from month_dim where year = 2011 \G
下面是查询的结果:
*************************** 1. row ***************************
month_sk: 71
month_name: January
month: 1
quarter: 1
year: 2011
effective_date: 0000-00-00
expiry_date: 9999-12-31
*************************** 2 row ***************************
month_sk: 72
month_name: February
month: 2
quarter: 1
year: 2011
effective_date: 0000-00-00
expiry_date: 9999-12-31
*************************** 3 row ***************************
month_sk: 73
month_name: March
month: 3
quarter: 1
year: 2011
effective_date: 0000-00-00
expiry_date: 9999-12-31
*************************** 4. row ***************************
month_sk: 74
month_name: April
month: 4
quarter: 2
year: 2011
effective_date: 0000-00-00
expiry_date: 9999-12-31
*************************** 5 row ***************************
month_sk: 75
month_name: May
month: 5
quarter: 2
year: 2011
effective_date: 0000-00-00
expiry_date: 9999-12-31
*************************** 6. row ***************************
month_sk: 76
month_name: June
month: 6
quarter: 2
year: 2011
effective date: 0000-00-00
expiry_date: 9999-12-31
*************************** 7. row ***************************
month_sk: 77
month_name: July
month: 7
quarter: 3
year: 2011
effective_date: 0000-00-00
expiry_date: 9999-12-31
*************************** 8. row ***************************
month_sk: 78
month_name: August
month: 8
quarter: 3
year: 2011
effective_date: 0000-00-00
expiry_date: 9999-12-31
*************************** 9 row ***************************
month_sk: 79
month_name: September
month: 9
quarter: 3
year: 2011
effective_date: 0000-00-00
expiry_date: 9999-12-31
*************************** 10. row ***************************
month_sk: 80
month_name: October
month: 10
quarter: 4
year: 2011
effective_date: 0000-00-00
expiry_date: 9999-12-31
*************************** 11. row ***************************
month_sk: 81
month_name: November
month: 11
quarter: 4
year: 2011
effective_date: 0000-00-00
expiry_date: 9999-12-31
*************************** 12. row ***************************
month_sk: 82
month_name: December
month: 12
quarter: 4
year: 2011
effective_date: 0000-00-00
expiry_date: 9999-12-31
12 rows in set (0.00 sec)
宾夕法尼亚州地区客户维
在本节我将用宾夕法尼亚州地区客户的子集维度来解释第二种维度子集的类型。我也将向你说明如何测试该子集维度。
相对的,一个向上钻取的维包含了它基础维的所有更高级别的数据。而一个特定子集维度则选择了它基础维的某个特定的数据集合。列表12-3所示的脚本产生并加载了宾夕法尼亚州(PA)地区客户子集维。
注意到,有两个事情是宾夕法尼亚州地区客户子集维区别于月份子集维的地方。
n pa_customer_dim表和customer_dim表的字段结构一样,而month_dim表没有 date_dim表中的日期字段。
n pa_customer_dim表的代理键是客户表的代理键。Month_dim表的代理键只属于month_dim表,并不是来自日期维表。
列表12-3 PA的客户:
/**********************************************************************/
/* */
/* pa_customer.sql */
/* */
/**********************************************************************/
USE dw;
CREATE TABLE pa_customer_dim
( customer_sk INT NOT NULL AUTO_INCREMENT PRIMARY KEY
, customer_number INT
, customer_name CHAR (50)
, customer_street_address CHAR (50)
, customer_zip_code INT (5)
, customer_city CHAR (30)
, customer_state CHAR (2)
, shipping_address CHAR (50)
, shipping_zip_code INT (5)
, shipping_city CHAR (30)
, shipping_state CHAR (2)
, effective_date DATE
, expiry_date DATE )
;
INSERT INTO pa_customer_dim
SELECT
customer_sk
, customer_number
, customer_name
, customer_street_address
, customer_zip_code
, customer_city
, customer_state
, shipping_address
, shipping_zip_code
, shipping_city
, shipping_state
, effective_date
, expiry_date
FROM customer_dim
WHERE customer_state = ‘PA’
;
/* end of script */
为了测试PA子维脚本,你需要先用列表12-4的脚本来增加三个居住于俄亥俄州的客户。
列表12-4:非PA 客户:
/**********************************************************************/
/* */
/* non_pa_customer.sql */
/* */
/**********************************************************************/
/* default to dw */
USE dw;
INSERT INTO customer_dim
( customer_sk
, customer_number
, customer_name
, customer_street_address
, customer_zip_code
, customer_city
, customer_state
, shipping_address
, shipping_zip_code
, shipping_city
, shipping_state
, effective_date
, expiry_date )
VALUES
(NULL, 10, ‘Bigger Customers’, ‘7777 Ridge Rd.’, ‘44102’,
‘Cleveland’, ‘OH’, ‘7777 Ridge Rd.’, ‘44102’, ‘Cleveland’,
‘OH’, CURRENT_DATE, ‘9999-12-31’)
, (NULL, 11, ‘Smaller Stores’, ‘8888 Jennings Fwy.’, ‘44102’,
‘Cleveland’, ‘OH’, ‘8888 Jennings Fwy.’, ‘44102’,
‘Cleveland’, ‘OH’, CURRENT_DATE, ‘9999-12-31’)
, (NULL, 12, ‘Small-Medium Retailers’, ‘9999 Memphis Ave.’, ‘44102’,
‘Cleveland’, ‘OH’, ‘9999 Memphis Ave.’, ‘44102’, ‘Cleveland’,
‘OH’, CURRENT_DATE, ‘9999-12-31’)
;
/* end of script */
用以下命令运行列表12-4中的脚本。
mysql> \. c:\mysql\scripts\non_pa_customer.sql
在你的控制台上将得到下面的响应信息:
Database changed
Query OK, 3 rows affected (0.86 sec)
Records: 3 Duplicates: 0 Warnings: 0
现在你已经准备好运行列表12-3中的pa_customer.sql脚本,在你做这些之前,确定你的Msql数据库的日期仍然是2007-03-02。
你可以用如下命令方式运行pa_customer.sql脚本。
mysql> \. c:\mysql\scripts\pa_customer.sql
你将在控制台上看到:
Database changed
Query OK, 0 rows affected (0.20 sec)
Query OK, 18 rows affected (0.08 sec)
Records: 18 Duplicates: 0 Warnings: 0
为了确保三个OH客户已经成功的载入,查询customer_dim 表:
mysql> select customer_name, customer_state, effective_date from customer_dim;
控制台上将看到:
+———————————+—————-+—————+
| customer_name | customer_state | effective_date|
+———————————+—————-+—————+
| Really Large Customers | PA | 2005-03-01 |
| Small Stores | PA | 2005-03-01 |
| Medium Retailers | PA | 2005-03-01 |
| Good Companies | PA | 2005-03-01 |
| Wonderful Shops | PA | 2005-03-01 |
| Extremely Loyal Clients | PA | 2005-03-01 |
| Distinguished Agencies | PA | 2005-03-01 |
| Extremely Loyal Clients | PA | 2007-03-01 |
| Subsidiaries | PA | 2007-03-01 |
| Really Large Customers | PA | 2007-03-02 |
| Small Stores | PA | 2007-03-02 |
| Medium Retailers | PA | 2007-03-02 |
| Good Companies | PA | 2007-03-02 |
| Wonderful Shops | PA | 2007-03-02 |
| Extremely Loyal Clients | PA | 2007-03-02 |
| Distinguished Agencies | PA | 2007-03-02 |
| Subsidiaries | PA | 2007-03-02 |
| Online Distributors | PA | 2007-03-02 |
| Bigger Customers | OH | 2007-03-02 |
| Smaller Stores | OH | 2007-03-02 |
| Small-Medium Retailers | OH | 2007-03-02 |
+———————————+—————-+—————+
21 rows in set (0.00 sec)
现在查询pa_customer_dim表来确定只有PA的客户在PA客户维表中。
mysql> select customer_name, customer_state, effective_date from pa_customer_dim;
结果如下:
+———————————+—————-+—————+
| customer_name | customer_state | effective_date|
+———————————+—————-+—————+
| Really Large Customers | PA | 2004-01-01 |
| Small Stores | PA | 2004-01-01 |
| Medium Retailers | PA | 2004-01-01 |
| Good Companies | PA | 2004-01-01 |
| Wonderful Shops | PA | 2004-01-01 |
| Extremely Loyal Clients | PA | 2004-01-01 |
| Distinguished Agencies | PA | 2004-01-01 |
| Extremely Loyal Clients | PA | 2005-11-01 |
| Subsidiaries | PA | 2005-11-01 |
| Really Large Customers | PA | 2005-11-03 |
| Small Stores | PA | 2005-11-03 |
| Medium Retailers | PA | 2005-11-03 |
| Good Companies | PA | 2005-11-03 |
| Wonderful Shops | PA | 2005-11-03 |
| Extremely Loyal Clients | PA | 2005-11-03 |
| Distinguished Agencies | PA | 2005-11-03 |
| Subsidiaries | PA | 2005-11-03 |
| Online Distributors | PA | 2005-11-03 |
+———————————+—————-+—————+
18 rows in set (0.00 sec)
正如你所看到的,只有PA客户进入到该表中,之前加入的OH客户并没有在其中。
修改定期装载
当一个新的PA客户资料加入到客户维中时,为了能够同时加入PA客户维表,你需要把PA客户子集维的装载合并到数据仓库的定期装载过程中。 修改后的定期装载脚本将在列表12-5中列出。这个改变(新增)用粗体显示。注意到,当每次你运行每日的定期装载脚本时,该脚本重建(截断,然后加入所有的PA客户)了PA客户子集维。
列表12-5:修正后的每日DW定期装载
/******************************************************************/
/* */
/* dw_regular_12.sql */
/* */
/******************************************************************/
USE dw;
/* CUSTOMER_DIM POPULATION */
TRUNCATE customer_stg;
LOAD DATA INFILE ‘customer.csv’
INTO TABLE customer_stg
FIELDS TERMINATED BY ‘ , ‘
OPTIONALLY ENCLOSED BY ‘”‘
LINES TERMINATED BY ‘\r\n’
IGNORE 1 LINES
( customer_number
, customer_name
, customer_street_address
, customer_zip_code
, customer_city
, customer_state
, shipping_address
, shipping_zip_code
, shipping_city
, shipping_state )
;
/* SCD 2 ON ADDRESSES */
UPDATE
customer_dim a
, customer_stg b
SET
a.expiry_date = SUBDATE (CURRENT_DATE, 1)
WHERE
a.customer_number = b.customer_number
AND ( a.customer_street_address <> b.customer_street_address
OR a.customer_city <> b.customer_city
OR a.customer_zip_code <> b.customer_zip_code
OR a.customer_state <> b.customer_state
OR a.shipping_address <> b.shipping_address
OR a.shipping_city <> b.shipping_city
OR a.shipping_zip_code <> b.shipping_zip_code
OR a.shipping_state <> b.shipping_state
OR a.shipping_address IS NULL
OR a.shipping_city IS NULL
OR a.shipping_zip_code IS NULL
OR a.shipping_state IS NULL)
AND expiry_date = ‘9999-12-31’
;
INSERT INTO customer_dim
SELECT
NULL
, b.customer_number
, b.customer_name
, b.customer_street_address
, b.customer_zip_code
, b.customer_city
, b.customer_state
, b.shipping_address
, b.shipping_zip_code
, b.shipping_city
, b.shipping_state
, CURRENT_DATE
, ‘9999-12-31’
FROM
customer_dim a
, customer_stg b
WHERE
a.customer_number = b.customer_number
AND ( a.customer_street_address <> b.customer_street_address
OR a.customer_city <> b.customer_city
OR a.customer_zip_code <> b.customer_zip_code
OR a.customer_state <> b.customer_state
OR a.shipping_address <> b.shipping_address
OR a.shipping_city <> b.shipping_city
OR a.shipping_zip_code <> b.shipping_zip_code
OR a.shipping_state <> b.shipping_state
OR a.shipping_address IS NULL
OR a.shipping_city IS NULL
OR a.shipping_zip_code IS NULL
OR a.shipping_state IS NULL)
AND EXISTS (
SELECT *
FROM customer_dim x
WHERE b.customer_number = x.customer_number
AND a.expiry_date – SUBDATE (CURRENT_DATE, 1))
AND NOT EXISTS (
SELECT *
FROM customer_dim y
WHERE b.customer_number = y.customer_number
AND y.expiry_date – ‘9999-12-31’)
;
/* END OF SCD 2 */
/* SCD 1 ON NAME */
UPDATE customer_dim a, customer_stg b
SET a.customer_name = b.customer_name
WHERE a.customer_number = b.customer_number
AND a.expiry_date – ‘9999-12-31’
AND a.customer_name <> b.customer_name
;
/* ADD NEW CUSTOMER */
INSERT INTO customer_dim
SELECT
NULL
, customer_number
, customer_name
, customer_street_address
, customer_zip_code
, customer_city
, customer_state
, shipping_address
, shipping_zip_code
, shipping_city
, shipping_state
, CURRENT_DATE
, ‘9999-12-31’
FROM customer_stg
WHERE customer_number NOT IN(
SELECT a.customer_number
FROM
customer_dim a
, customer_stg b
WHERE b.customer_number = a.customer_number )
/* RE-BUILD PA CUSTOMER DIMENSION */
TRUNCATE pa_customer_dim;
INSERT INTO pa_customer_dim
SELECT
customer_sk
, customer_number
, customer_name
, customer_street_address
, customer_zip_code
, customer_city
, customer_state
, shipping_address
, shipping_zip_code
, shipping_city
, shipping_state
, effective_date
, expiry_date
FROM customer_dim
WHERE customer_state = ‘PA’
;
/* END OF CUSTOMER_DIM POPULATION */
/* product dimension loading */
TRUNCATE product_stg
;
LOAD DATA INFILE ‘product.txt’
INTO TABLE product_stg
FIELDS TERMINATED BY ”
OPTIONALLY ENCLOSED BY ”
LINES TERMINATED BY ‘\r\n’
IGNORE 1 LINES
( product_code
, product_name
, product_category )
;
/* PRODUCT_DIM POPULATION */
/* SCD2 ON PRODUCT NAME AND GROUP */
UPDATE
product_dim a
, product_stg b
SET
expiry_date = SUBDATE (CURRENT_DATE, 1)
WHERE
a.product_code = b.product_code
AND ( a.product_name <> b.product_name
OR a.product_category <> b.product_category )
AND expiry_date = ‘9999-12-31’
;
INSERT INTO product_dim
SELECT
NULL
, b.product_code
, b.product_name
, b.product_category
, CURRENT_DATE
, ‘9999-12-31’
FROM
product_dim a
, product_stg b
WHERE
a.product_code = b.product_code
AND ( a.product_name <> b.product_name
OR a.product_category <> b.product_category )
AND EXISTS (
SELECT *
FROM product_dim x
WHERE b.product_code = x.product_code
AND a.expiry_date = SUBDATE (CURRENT_DATE, 1) )
AND NOT EXISTS (
SELECT *
FROM product_dim y
WHERE b.product_code = y.product_code
AND y.expiry_date = ‘9999-12-31’)
;
/* END OF SCD 2 */
/* ADD NEW PRODUCT */
INSERT INTO product_dim
SELECT
NULL
, product_code
, product_name
, product_category
, CURRENT_DATE
, ‘9999-12-31’
FROM product_stg
WHERE product_code NOT IN(
SELECT y.product_code
FROM product_dim x, product_stg y
WHERE x.product_code = y.product_code
;
/* END OF PRODUCT_DIM POPULATION */
/* ORDER_DIM POPULATION */
INSERT INTO order_dim (
order_sk
, order_number
, effective_date
, expiry_date
)
SELECT
NULL
, order_number
, order_date
, ‘9999-12-31’
FROM source.sales_order
WHERE entry_date = CURRENT_DATE
;
/* END OF ORDER_DIM POPULATION */
/* SALES_ORDER_FACT POPULATION */
INSERT INTO sales_order_fact
SELECT
order_sk
, customer_sk
, product_sk
, date_sk
, order_amount
, order_quantity
FROM
source.sales_order a
, order_dim b
, customer_dim c
, product_dim d
, date_dim e
WHERE
a.order_number = b.order_number
AND a.customer_number = c.customer_number
AND a.order_date >= c.effective_date
AND a.order_date <= c.expiry_date
AND a.product_code = d.product_code
AND a.order_date >= d.effective_date
AND a.order_date <= d.expiry_date
AND a.order_date = e.date
AND a.entry_date = CURRENT_DATE
;
/* end of script */
测试修正后的定期装载
现在你可以测试列表12-5的脚本。在你实施之前,先增加一些客户数据,通过运行列表12-6的脚本增加一个PA客户和一个OH客户到客户维中。
列表12-6:增加两个客户
/******************************************************************/
/* */
/* two_more_customers.sql */
/* */
/******************************************************************/
/* default to dw */
USE dw;
INSERT INTO customer_dim
( customer_sk
, customer_number
, customer_name
, customer_street_address
, customer_zip_code
, customer_city
, customer_state
, shipping_address
, shipping_zip_code
, shipping_city
, shipping_state
, effective_date
, expiry_date )
VALUES
(NULL, 13, ‘PA Customer’, ‘1111 Louise Dr.’, ‘17050’,
‘Mechanicsburg’, ‘PA’, ‘1111 Louise Dr.’, ‘17050’,
‘Mechanicsburg’, ‘PA’, CURRENT_DATE, ‘9999-12-31’)
, (NULL, 14, ‘OH Customer’, ‘6666 Ridge Rd.’, ‘44102’,
‘Cleveland’, ‘OH’, ‘6666 Ridge Rd.’, ‘44102’,
‘Cleveland’, ‘OH’, CURRENT_DATE, ‘9999-12-31’)
;
/* end of script */
现在运行列表12-6中的脚本:
mysql> \. c:\mysql\scripts\two_more_customers.sql
Mysql将显示有两个记录生效。
Database changed
Query OK, 2 rows affected (0.06 sec)
Records: 2 Duplicates: 0 Warnings: 0
现在更改你的Msql数据库的日期为2007-03-03以保证老的数据不会重新载入,然后运行dw_regular_12.sql脚本。
mysql> \. c:\mysql\scripts\dw_regular_12.sql
你将看到你的控制台有如下显示:
Database changed
Query OK, 9 rows affected (0.15 sec)
Query OK, 9 rows affected (0.14 sec)
Records: 9 Deleted: 0 Skipped: 0 Warnings: 0
Query OK, 0 rows affected (0.05 sec)
Rows matched: 0 Changed: 0 Warnings: 0
Query OK, 0 rows affected (0.01 sec)
Records: 0 Duplicates: 0 Warnings: 0
Query OK, 0 rows affected (0.00 sec)
Rows matched: 0 Changed: 0 Warnings: 0
Query OK, 0 rows affected (0.00 sec)
Records: 0 Duplicates: 0 Warnings: 0
Query OK, 18 rows affected (0.04 sec)
Query OK, 19 rows affected (0.06 sec)
Records: 19 Duplicates: 0 Warnings: 0
Query OK, 4 rows affected (0.09 sec)
Query OK, 4 rows affected (0.07 sec)
Records: 4 Deleted: 0 Skipped: 0 Warnings: 0
Query OK, 0 rows affected (0.06 sec)
Rows matched: 0 Changed: 0 Warnings: 0
Query OK, 0 rows affected (0.00 sec)
Records: 0 Duplicates: 0 Warnings: 0
Query OK, 0 rows affected (0.00 sec)
Records: 0 Duplicates: 0 Warnings: 0
Query OK, 0 rows affected (0.15 sec)
Records: 0 Duplicates: 0 Warnings: 0
Query OK, 0 rows affected (0.17 sec)
Records: 0 Duplicates: 0 Warnings: 0
现在用该语句查询pa_customer_dim表。你将看到新的PA客户被插入到表中。
mysql> select customer_name, customer_state, effective_date from pa_customer_dim;
这是结果:
+————————-+—————-+—————+
| customer_name | customer_state | effective_date|
+————————-+—————-+—————+
| Really Large Customers | PA | 2005-03-01 |
| Small Stores | PA | 2005-03-01 |
| Medium Retailers | PA | 2005-03-01 |
| Good Companies | PA | 2005-03-01 |
| Wonderful Shops | PA | 2005-03-01 |
| Extremely Loyal Clients | PA | 2005-03-01 |
| Distinguished Agencies | PA | 2005-03-01 |
| Extremely Loyal Clients | PA | 2007-03-01 |
| Subsidiaries | PA | 2007-03-01 |
| Really Large Customers | PA | 2007-03-02 |
| Small Stores | PA | 2007-03-02 |
| Medium Retailers | PA | 2007-03-02 |
| Good Companies | PA | 2007-03-02 |
| Wonderful Shops | PA | 2007-03-02 |
| Extremely Loyal Clients | PA | 2007-03-02 |
| Distinguished Agencies | PA | 2007-03-02 |
| Subsidiaries | PA | 2007-03-02 |
| Online Distributors | PA | 2007-03-02 |
| PA Customer | PA | 2007-03-03 |
+————————-+—————-+—————+
19 rows in set (0.00 sec)
小结
在这章,你学习了两种类型的子集维。月份子集维是向上钻取维的一个例子,一个装载自比它更为详细的基础维的具有更高级的维。PA客户维是一个特定的子集维;只从它的基础维选择PA客户数据进行装载。
下一章,你将学习另外一个复用现有维的技术,叫角色扮演维度。