自助关联查询难在哪里
事物是普遍联系的,很多有业务意义的查询也会涉及多个数据表的关联。
BI 类软件通常会提供自助查询功能,有些软件还能支持关联查询,但实际使用的大多数还是单表的,关联查询功能很少被业务人员使用。涉及到关联表的查询常常需要由技术人员事先准备好,也就是我们常说的宽表。业务人员通常只会基于单一的宽表来做查询。关联查询是几乎所有 BI 类软件的软肋,无论大牌还是新秀,几乎一试一个准,全军覆没。
为什么会这样呢?
因为很多人不会用这些软件提供的多表关联查询功能。
理解这个问题,要从数据库是如何看待关联说起。
数据常常是存储在关系数据库中(或类似逻辑结构的存储),一般也就是使用 SQL 来做查询。而 SQL 对关联运算(也就是 JOIN)的定义很简单,两个表关联时,给出对应的关联字段就可以了,除此之外没有更多的信息和约定。
这种简单的规则,在关联表多的时候可能让很多人晕掉。
如果参与关联的两个表之间只有唯一的关联关系,那么只要设置好对应的关联字段就可以了,一般人都不会晕掉。这些情况,我们称为是简单关联。
但有的时候可能出现不是简单关联的情况了,特别是在关联表较多的时候,用户就有可能晕掉了。
比较常见的情况是两个表之间有多个关联关系,我们称为重复关联。
比如,通话记录表中会有主叫号码和被叫号码,我们要用这些号码与电话帐户表关联才能取到号码的相关信息(比如注册地)来作为查询条件。举例来讲,我们想查询所有北京号码打给上海号码的通话记录,这就需要把通话记录表和电话帐户表做关联,而电话帐户表要被关联两次,分别使用通话记录表的主叫号码和被叫号码关联,才能分别取出主叫号码注册地和被叫号码注册地。首先要把同一个电话帐户表关联两次,这就有相当一部分软件根本不支持了;其次,还要分别取出两次注册地字段,要分清楚是用主叫号码关联出来的还是用被叫号码关联出来的,这就要给电话帐户表起不同的别名来区分(SQL 就是这么干的),这个概念又会让非专业人员感觉很糊涂。
重复关联还可能出现被跟随复制的情况。我们这个例子实际上已经简化了,通常电话帐户表中并不会直接存储北京、上海这种级别的地点,而是会用一个地点编号去和地区表关联,从地区表中才能再取出这个地点编号对应的地点所在的省级区域(甚至可能再分几级)。那么上述关联过程中,这个地区表也会跟随着被关联两次,也要起别名才能区分。如果地区再分级了(这其实是常有的事),被跟随关联两次的表就更多,关联表稍多时,连技术人员都要小心仔细才能搞得清,业务人员基本上就没可能理清楚了。
就用这个地区表,随手可以再举出让人崩溃的查询例子:北京号码漫游到广东后打给上海号码的电话,这个查询在数据库中完全可以做出来的,通话记录表中可以有基站信息,基站表再和地区表关联可以获得打电话所在地点,但是这么复杂的关联关系,已经彻底无法在 BI 界面上让业务用户理解了。
更麻烦一点的情况是互相关联。
人事系统里员工表,还有部门表。员工表中有所属部门的字段与部门表关联,部门会有经理,而经理也是个员工,部门表中的经理字段会再和员工表关联。这就发生互相关联的情况,转圈了。
现在我们要查出女经理手下的男员工,自己想想 SQL 会写成啥样吧。员工表关联到部门表获取部门经理,然后再转回来和员工表关联获取经理的性别,员工表出现两次,又要起别名,这样才能区分出从员工表中取出来的性别字段是待查员工的还是其经理的。
用这个简单例子,可以让很多号称无所不能查的 BI 软件现出原形。有相当一部分根本不支持重复关联的直接就歇菜了,即使有些勉强能搞定的,也会非常绕,业务人员根本理解不了,还是相当于搞不定。
互相关联到极端情况,还会变成自我关联了。
比如前面说过地区可能分级,而分级的地区表很可能并不会做成多个表,而是只有一个表,用一个字段表示其上级地区(编号),这是很常见的数据结构设计,但这也意味着地区表会和自己关联。从最低层的营业所(或基站)走到省级区域,可能会有三五级之多,这个表也就要被重复关联三五次,起上三五个别名才分得清,你说业务人员晕不晕?
发生这些现象的原因是前面说的,SQL 对于 JOIN 的定义过于简单了,过于简单的定义无法抓过关联的根本特征。定义简单,是容易理解了,但用来描述现实却会很复杂。就像只要学加法是很简单,但试图把乘法也用加法来描述却会很麻烦。我们这里还只是举了只有外键表的例子,如果再混合上同维表和主子表的情况(尽管这两种情况本身不算更复杂),事情就会复杂到不可收拾的地步(写出 SQL 就会带着子查询,之前还只是 JOIN 多)。这不能指责业务人员的脑子笨,在这种关联模型下,当关联到七八个表的时候(这对于大型业务就是家常便饭),技术人员都常常出错,何况不熟悉数据库的业务人员。结果这些软件提供了关联查询能力也没法用,还是要麻烦技术人员造宽表。但这样一来,事事都要求助 IT 部门,在线查询就很难在线了。
那么该怎么办呢?
嗯,关注一下润乾报表和 esProc SPL 中的 DQL,就能找到答案了。
英文版