【数据蒋堂】第 36 期:JOIN 延伸:维度概念

sjjt-36

谈到数据分析时常常会用到维度这个词,针对数据立方体的钻取、旋转、切片等操作都是围绕维度进行的,几乎所有的数据分析人员都知道并会运用这个术语,但要问及它的定义,却几乎没有人能给出来。

通俗来讲,我们把用来分类的属性(字段)称为维度,比如地区、年度、产品类型等;而另外一些用于聚合运算的属性则称为测度,比如销售额、产量、考试成绩等。维度不能做聚合运算,比如计算地区合计是没有意义的;测度则不能用于分类,比如按销售额分类也没什么业务意义。我们通常就是用是否”可用于分类“来判定一个属性是不是维度,但这其实只是对于维度性质的描述,并不能作为定义。

我们基于关系数据库来讨论这个问题,先简单回顾一下基本概念。

设有一个关系数据库,其中有若干数据表,表的数据结构由字段构成,表的数据由记录构成。

数据表的某些字段被指定为主键,需要满足这样的条件:表中所有记录在这些字段上的取值是互不相同的。也就是说,可以用主键值来唯一确定相应的记录。数据表可以没有主键,但有只能有一套。构成主键的字段称为主键字段。

每个数据表可以有多套外键,外键也是该数据表的某些字段,其取值总是在另一个表(可能是本表)的主键取值范围内。这里的另一个表被称为外键指向表,简称外键表,构成外键的字段被称为外键字段。

现在,我们定义:在关系数据库中,不是外键字段的主键字段被称为维度,维度所在的表称为维表,维度可以用维表的主键字段来标识。顺便地,我们定义即不是主键字段也不是外键字段的字段为测度。从这个定义上看,显然不可能某个字段即是维度又是测度。

需要说明的是,我们这里所说的主键外键是指逻辑意义上的概念,也就是在数据的 E-R 结构设计中的主外键。有时为了性能而在物理数据结构中并不真地建立主键和外键,这种情况不在我们的考虑范围内。

先从一些例子来理解维度的定义。

看这两个同维表:

employee   员工表

id 员工编号
name 姓名
salary 工资

manager   经理表

id 员工编号
allowance 岗位津贴

我们会在 manager 表建立外键,字段为 id,指向 employee 表的主键 id。这时,manage.id 就即是主键字段也是外键字段,那么它不是维度。而 employee.id 是主键字段而不是外键字段,那么它就是维度。

再看主子表的情况:

Orders   订单表

id 订单编号
customer 客户
date 日期

OrderDetail 订单明细

id 订单编号
no 序号
product 订购产品
price 价格

OrderDetail.id 是主键字段,但也是指向 Orders 表的外键字段,所以它不是维度。而 Orders.id 是主键字段但不是外键字段,那么它是维度,OrderDetail.no 是主键字段且不是外键字段,它也是维度。

OrderDetail.no 这个维度有些特殊,一般来说,不会有另外一个外键字段和它关联了,我们把这种未被指向的维度称为孤维。在查询界面中做维度对齐运算时一般不用列出来孤维。

再来检验这个定义是否和常规的维度观念相符,并且对于不相符的情况要给出合理的解决方案。

对于地区、产品这些常规维度,数据库中都会有对应的地区表、产品表,那么这些维度就对应了这些表的主键字段,符合我们定义。而销售额、产量等属性则不可能对应到某个表的主键,所以确实也不是维度。

但日期(或年度)呢?它显然是个维度,但数据库中并没有一个表以它为主键,似乎不符合我们的维度定义。

事实上,所有用到日期数据类型的数据库在逻辑上都应当有一个日期表,其它数据表的日期型字段均可以视为指向这个日期表的外键。但由于日期的相关信息都可以由日期本身计算出来(年度、月份等),而没有需要单独存储的属性,因此我们通常不会在物理数据库中建立这个日期表。那么,只要在逻辑上恢复这个日期表,日期就符合前面的维度定义了。我们把这种逻辑上应该有但物理上并未建立的表称为假表,假表可以看成是一个单字段无记录的表,这个单字段也就是该表的主键,这样就可以承载没有物理表的维度了。类似地,年度、月份也都可以用假表定义。

再观察年龄这种属性,它有可能用于分类(每个年龄的人数),又可能用于聚合(某部门人员的平均年龄),从性质上看,它似乎即是维度又是测度?这就与我们的定义相悖了。

其实,我们在用年龄分类统计时并不是用年龄值本身,而是年龄段,年龄本身应该是个实数值,这是不能用于分类的。也就是说,年龄是测度,而通过年龄计算出来的年龄段才是个维度。这时候,我们要引入维函数概念,维函数以某个字段值为参数,返回某个维度的取值。通过维函数可以把测度转换成维度,在它的帮助下,我们就可以保持维度的严格定义,同时又不和常规观念矛盾。

这里的维度定义是基于外键概念的,而我们知道,外键实际上定义了表之间的 JOIN 关系。从这个意义上讲,维度是被 JOIN 定义的!