元数据文件描述数据库中,各个表中的数据以及各类表间关系;元数据文件使用DQL元数据层设计器编辑,存为json格式的文件,文件的默认扩展名为.lmd。它可以和润乾报表V2018引擎一起部署在web服务器上,客户端通过浏览器进行查询访问;也可以和DQL Server的驱动包一起部署在客户端,客户端以标准JDBC的连接方式连接访问。
打开DQL元数据层设计器,在菜单栏中点击文件>新建,创建元数据文件:
元数据中的表有四种,普通表、SQL型表、假表和分表,在这里先介绍普通表。
普通表的物理表名必须和数据库中的表名或视图名完全一致。如果是带模式的表,则必须把表名定义成schema.table,如:dba.公司信息。表名称的初始值和物理表名相同,也可以自行修改。
在常规页面中,可以设定表或视图的字段,数据来源需要和表或视图在数据库中的字段名相同。字段名称的初始值和数据来源相同,可以自行修改。
在菜单栏中点击系统>数据源,可以设定数据源连接:
在数据源连接窗口中,可以配置数据源的连接属性,或连接指定的数据源。在DQL元数据层设计器中,不允许同时连接多个数据源。在数据源连接列表中,demo为默认的HSQL数据库。
连接数据源之后,可以直接导入数据库中的物理表或视图为元数据的表,然后再添加修改主键、外键等属性。导入时,在工具栏中点击导入数据库表:
然后在弹出窗口中选择需要导入的数据库表即可。
如果数据库中未设定主键及外键信息,则需要在元数据的表字段信息中修改。
如果选择了选项使用数据库中描述作为表和字段名称,则导入表时,表名和字段名来自数据库中表和字段的描述。
SQL表与数据库中的视图类似,它是执行SQL语句后返回的结果集所构成的表。
在快捷工具栏中,点击增加SQL型表,或者在菜单栏中选择相应选项,可以在元数据中新建SQL型表:
SQL型表需要设定表名称及SQL语句,设定完毕后,连接数据源后,可以执行读取结构,自动分析取结果集中的字段信息:
执行时,在从数据库获取结果集后,数据来源为空时,会缺省使用字段名称进行查询。此规则适用于除假表外的所有表类型。
用来唯一确定记录的字段,一般情况和数据库物理表的主键定义一致。导入数据库物理表的时候,能把数据库的主键一起读进来。也可以在元数据中修改主键字段,使之和数据库不一致。如果是SQL型表,读取结构时不会包含主键信息,主键需要设定。
设定主键,只需要勾选字段后面主键的复选框即可。
与数据库中的情况相似,一个表的主键可以是一个,也可以是多个;单一主键的值,或者多个主键值的组合,在一个表中应该具有唯一性。
元数据中表的主键可以人工设定,在设定时一定要注意主键的合理性,在定义表的主键时,DQL Server并不会去验证主键设定是否合理。如果设定的主键值在表中不唯一,在查询或者汇总计算时就可能产生错误。
与数据库中的概念相似,DQL中,表的外键字段指向另一张表的主键,从而产生关联。导入数据库物理表的时候,会把数据库的外键定义一起读进来。在元数据中可以修改外键的定义,不需要和数据库的定义完全一致。
外键名称:外键的名称可以直接编辑,设定时不允许与本表的字段名相同,也不允许与本表的其它外键名称相同,否则引用外键时会产生错误;如果当前外键是由单外键字段组成的,那么外键名称通常可以不使用,而是用外键字段来代替;如果当前外键是由多个外键字段组合成的,那么在引用外键指向表的字段时,只能使用外键名称,如上图中fk1、fk2和fk3均为外键名称,选中外键的外键名称是fk2
对应表名:外键所指向的表名,当指向表的主键唯一且非外键时,那么这个表称为维表,如上图所示的维表是:产品。
引用字段:外键指向表的主键,当这个主键非外键时,它被称为维,如上图所示的维是:产品.产品ID。当引用字段并非维时,即引用字段是外键,此时,引用字段会指向新的引用字段……这样的引用最终会指向一个维,这个维就是最初外键指向的维。
外键字段:当前表的字段,和引用字段一一对应,如上图中所示入库单表的外键字段是:产品ID
当日期型字段特别多时,使用菜单日期字段自动映射日期维,可使日期型字段自动对应日期假表。
在DQL中,外键字段和维字段的地位是完全一样的,可以把外键字段看成维字段。
任何表的主键都可以视为维字段。如果某个表的主键是外键,那么这个主键的维即为外键指向的维;如果主键并非外键,那么这个主键的维就是它本身。对于非主键的字段,只有当它是外键时才是维字段,字段的维即为外键指向的维。
Ø 举例:
根据ER图所示,“回款单”表中的客户ID字段指向“客户”表的客户ID字段,“回款单”表与“客户”表之间的数据关系为多对一的关系,其中的相关概念描述如下:
外键字段:“回款单”表的客户ID字段
维表:“客户”表
维:“客户”表的主键客户ID字段
写DQL的时候,可以像引用属性一样引用外键表的字段,比如:SELECT 客户ID.客户名称,回款日期,金额 FROM 回款单,这里客户ID.客户名称引用的是客户ID指向的“客户”表的客户名称字段。
知识点:
1) 对于单一字段形成的外键,在使用DQL时,可以用外键字段.外键表的字段名的方式来直接使用。
2) 对于多字段组成的外键,在使用DQL时,可以用外键名称.外键表的字段名的方式来直接使用。
3) 使用外键名称.外键表的字段名方式引用,既可用于多字段组成的外键,也可用于单一字段形成的外键情况。
有些表间存在着逻辑上的外键关系,但是程序员设计数据库的时候并没有实际定义,因此,从数据库读入物理表结构的时候,会缺少这些外键定义。
智能识别外键的功能,就是根据选中的主表(维表),智能地查找其它表中是否有字段数据类型、长度、值和选中主表的主键完全一致,且数据符合外键规范,没有定义外键的情况,如果有则列出来,用户根据实际情况进行选择确认,一旦被确认的,会自动加到外键定义中。
在连接数据源后,在菜单中可以选择执行智能识别外键:
在窗口中选择主表,点击确定后,系统会智能匹配所选表的结构及数据,从系统中选择出和所选表相匹配,且未设定外键的对象出来
对系统自动识别出来的关系,可以进行人工审查,并进行干预和调整,以便最大限度的保证模型的正确性。点击确认可以确认使用一条映射,点击删除则不使用,调整后如下:
点击确认后,模型中会自动生成外键关系
知识点:
1) 对于设计规范的数据库,可直接使用导出的模型结构,建模工作量可以忽略不计。
2) 外键字段与引用表的主键字段,应该具有相同的数据类型以及数据宽度、数据精度等,这样才可以智能识别,否则只能手工指定。
在表中,参数为维的函数,且返回维与原维不同时,称为层函数。
所谓假表,就是数据库中实际不存在的单字段表。在DQL中引入假表的概念,主要是为了解决两类问题:
第一类通过定义维的各个层次结构,使用户能够在不同的粒度上观察数据
通过假表与层函数的配合使用,我们能够定义类似于,年、年月、日期,这样的日期层次,从而使用户获得能够在不同时间粒度上观察数据的能力,同样的,我们也能够定义国家、省、市这样的结构,这取决于具体的业务规范,对于这种结构,在DQL元数据层设计器中都可以一次定义,到处使用。
对于层次结构的定义,通常有几个维度就定义几张表(或假表)。比如上述的日期,一般就定义年、年月、日这样三张表。如果业务表中的日期字段存储的是日信息,就让它指向“日”表,如果是年月信息,则指向“年月”表,依次类推。
在元数据定义中,当表间的外键关系设定之后,会根据外键设定中,产生的维信息,自动生成维的相关定义。然后可以在元数据的维和层页面里,定义这几个维度间的计算关系。
Ø 举例:
说明:
1) 在层间计算式中用?表示源字段表,表示“年”维年字段的值可以由“年月”维的年月字段通过表达式int(?/100)换算而来。
2) 层间计算式也可以说成从细向粗计算。比如“日期”维,只能从日算出年,不能从年算出日。
3) 目标维可分别通过多个源维进行换算。比如“年”维,可能是从日聚集到年,也可能从年月聚集到年,因此,对于“年”维,就要定义两个层间计算式,分别从“日期”表的日期和“年月”表的年月计算而来。
4) 定义好这种结构之后,我们就可以在DQL中直接使用定义中的任何结构,如下例:
在回款单表中回款日期是个日期类型,在回款单表的外键定义中,我们将“回款单”表的字段回款日期与“日期”表的字段日期建立外键关系,如下图:
这样,我们就可以在DQL中获得回款日期的年,月等信息了,如回款日期#年,得到回款日期的年份,回款日期#年月就得到了回款日期的年月
Ø 举例:
第二类可以充当数据库视图,这种情况下,可以把假表视为表来使用,可以为其定义主键、外键等属性,在用户眼中和表是相同的
知识点:
有了假表和层函数等概念,用户就可以按照希望的层次来观察数据,并且用户也可以更灵活的使用数据。
在讲外键的时候,我们说到,表的外键必将指向一个维。因此,只要为某些字段定义了外键,DQL就会自动把外键最终指向的表.关键字段作为维存储在维定义里,一般情况不需要单独去定义。
但是,表.关键字段这么长的名称作为维名很不方便,写DQL的时候显得太长,所以,可以在维定义里修改维名,使之更短更容易记忆。
比如DQL:
- SELECT客户ID.客户名称,SUM(金额)
- ON客户.客户ID
- FROM回款单BY 客户ID
如上图定义了维名后,就可以写成
- SELECT客户ID.客户名称,SUM(金额)
- ON客户
- FROM回款单BY 客户ID
很多业务系统的数据量巨大,直接从基础业务表进行查询汇总会非常慢,因此数据库里往往存在一些已经造好的历史汇总表。
对于用户来说,当他进行查询时需要知道有哪些历史汇总表存在的难度比较大,尤其是当存在多级汇总表的时候,要能够恰当选择其中一个汇总表难度更大了。
因此如果某张表是历史汇总表,那么可以在这张表上定义基础表属性,定义其从哪一张基础表汇总而来。当用户的DQL基于基础表进行分组汇总时,DQL元数据层会智能寻找汇总表,看哪个汇总表和DQL的汇总级别最接近,然后智能地把DQL解析成从这个汇总表查出。汇总表和基础表是相对的概念,表A是表B的基础表,则表B是表A的汇总表;在定义了基础表属性后,这个历史汇总表会自动加入其基础表的汇总表中。
举例来说,有一个基于日期的基础表,数据库中存在按月汇总、按年汇总的两张汇总表。当用户的查询DQL是聚合到季这一层时,系统会自动从月汇总表里查出;当聚合到年时,会自动从年汇总表里查出。
汇总表和基础表之间是多对多的关系。
Ø 举例:
现有支付单,支付月汇总两张表。支付月汇总表数据是从“支付单”表汇总而来,那么可以定义如下:
上图中的字段名是“支付月汇总”表的字段名,广义外键或表达式是指基于基础表的计算表达式,可以把上述定义理解为这样的DQL:
- SELECT
- 支付单.SUM(支付金额) 支付总额
- ON 年月
- FROM
- 支付单
- BY 支付日期#年月
此时,如果需要基于支付单,进行年汇总,即下面的DQL:
- SELECT
- 支付单.SUM(支付金额) 支付总额
- ON 年
- FROM
- 支付单
- BY 支付日期#年
DQL元数据层会自动从已有的支付月汇总的数据中进一步汇总,即执行以下SQL:SELECT INT(T1_1_1.年月/100) 年,SUM(T1_1_1.支付总额) 支付总额 FROM 支付供应商月汇总 T1_1_1 GROUP BY INT(T1_1_1.年月/100),通过对基础表的设定,可以充分利用事先汇总完成的数据,以提高效率。
知识点:
1) 定义基础表功能,可以简化业务视图,降低用户对业务系统的学习难度,用户只需要记住基础表即可,不需要记忆汇总表。
2) 在定义基础表功能时,其中的字段名,必须包括主表的所有关键字段,如上图中的年月为支付月汇总主键,必须定义广义外键或表达式。计算字段可以全写,或只写一个。
3) 在上述定义中,我们定义年月的广义外键或表达式为支付日期#年月,如果层函数或者假表中出现错误或异常,也会导致系统不能自动找到汇总表。
4) 当基础表中有多个汇总表可用时,会通过对表中记录数的设置选择记录数少的使用,记录数相同或都为空时取第一个符合条件的汇总表。
通过设定主键和外键,使得表与表相互关联。如订单,根据外键关联,可以获得销售以及与销售表同维的雇员等表的信息,也可以获得客户以及与客户表同维的VIP客户等表的信息,再通过雇员的外键关联,可以获得城市表的信息,等等。
表的外键,同维的表的外键,通称为广义外键,广义外键的同维表的外键,同样属于广义外键,另外,广义外键的层函数也是一种广义外键。
表的字段,同维表的字段,广义外键的同维表的字段,通称为广义字段,广义字段的层函数也是一种广义字段。
用广义字段定义的表达式,通称为伪字段,按普通字段处理,但不作为外键。
外键指向表的同维表集合,通称为广义外键表,但不包含主键外键时指向的本表同维表。
通过在元数据中设定伪字段,可以命名一些常用的广义字段,可以使得对它们的查询语句更为精简方便。例如:
如果在回款单中定义广义字段客户VIP结束如下:
伪字段可以相当于普通字段使用,如查询的DQL可以写成:SELECT 客户VIP结束年月 FROM 回款单,而不必写为SELECT 客户ID.结束时间#月 FROM 回款单。伪字段也可以作为汇总维度使用,如SELECT COUNT(*) 本年到期VIP客户回款笔数 ON 年 FROM 回款单 BY 客户VIP结束年月#年。
知识点:
1) 在元数据中定义的伪字段在DQL元数据文件中使用时与普通字段相同。
通过设置子表,使得在主表中可以直接对子表字段进行汇总查询。如客户,根据添加子表回款单,可在查询客户表字段的同时对回款单表中的字段做汇总查询。子表外键必须关联主表主键字段。
部分主键字段构成外键指向的表是主表;单字段主键的层函数的维表是主表;主表的主表是主表;同维表的主表是主表;主表的同维表是主表。
在元数据中,当A表中有多个主键时,对部分主键字段添加外键后,在外键对应的主表中会自动将A表做为子表列出。
通过在元数据中添加子表,可以使得对它们的查询语句更为精简方便。例如:
客户ID为客户表中的主键字段,回款单中客户ID和回款单ID为主键字段,回款单的客户ID外键指向客户表的客户ID。
回款单中外键设置如下:
在客户表中会自动添加子表回款单,子表外键为对应主表主键的外键字段,如下:
在主表中对子表字段的汇总查询与主表中字段的查询语法不同,如:
- SELECT
- 客户.名称 客户名称
- 客户.市#省 省份
- 客户@回款单.sum(金额) 回款金额
- FROM
- 客户
Ø 知识点:
1) 在主表中可对子表字段做汇总,子表外键必须指向主表主键字段。
2) 在主表中对子表字段的汇总查询语法:
T@S.f(F) 表T的子表S的广义字段F的聚合表达式count/avg/sum/max/min
在数据库中,有时,数据会分开存储在多个数据结构完全相同的表中,如分年度存储的销售数据、分部门存储的员工资料、分地区存储的客户信息等等。在数据查询与分析时,往往需要将这些表中的数据联合起来,比如查询历年销售数据、查询所有部门的员工资料或者查询全部客户信息等,此时,可以通过在DQL元数据文件中,设定分表。
建立分表,可以直接点击增加分表按钮执行,也可以在菜单栏中选择编辑>增加分表,或者使用快捷键Ctrl+B:
选择后,可以选择组成分表的各个物理表来源:
物理表可以在当前数据源的数据库表中选择,也可以在元数据表中选择,选择的物理表必须具有相同的数据结构:
选定分表的数据表来源后,可以为分表命名,字段定义会自动生成:
与元数据中的普通表定义不同,分表中还需定义分区属性,根据指定的分段字段,设定分表中的分段列表。分段字段需是维字段,而且,分表中的各个分区表的分段字段的值需要是严格单调的,这样才可以根据分段字段的设定,在查询时,合理将分表拆分。设定分段列表时,每个物理表作为一个分区,由小到大设定段界,设定段界的值将包含在分区内,如下面的设置:
图中的设定,相当于设定分区订单2011 中,分段字段订单编号<=10554;最后的一个分区订单2012 的段界可以为空白,此时最后一个分区无上限。
在查询时,分表的使用和元数据中其它表的DQL语法相同,比如:
SELECT * FROM订单总表
DQL元数据层会自动将分表中的各个物理表联合访问,相当于执行:
SELECT T1_1.订单编号 订单ID FROM (SELECT * from 订单2011 UNION ALL SELECT * from 订单2012) T1_1
那么,分表中,分区的设定有什么意义呢?在执行有条件的查询时,有时候查询的数据并非来源于分表中所有的物理表,此时如果筛选条件是分段字段,在DQL元数据文件中就可以根据分段列表中的段界设定,寻找到数据来源于哪个或者哪些物理表,从数据查询时只访问相关的数据库。
如:SELECT * FROM 订单总表 WHERE 订单编号<10250
DQL会解析为:SELECT T1_1.订单ID 订单ID FROM (SELECT * from 订单2011) T1_1 WHERE T1_1.订单ID<10250
如:SELECT * FROM 订单总表 WHERE 订单ID in (10620,10630,10640)
DQL会解析为:SELECT T1_1.订单编号 订单ID FROM (SELECT * from 订单2012) T1_1 WHERE T1_1.订单ID IN (10620,10630,10640)