开源 BI 实践:简化多表关联查询

DQL 重要的作用是解决表间关联,有了上一期配置好的元数据,我们就可以来操练一些 DQL 语句,下面看几种不同关联方式的例子,看看它是如何简化关联的。

多级关联

先跑出界面,仔细观察其中的元数据树,可以看到所有表、以及表的关联关系都在树上体现出来了,只要是能关联上的数据,无论涉及几个表,都可以逐级找到。

下面想对比各个国家客户的订单数据,这涉及到订单、客户、国家、区域表。把订单表展开后,这四个表的信息都能找到:

..

生成的 DQL 语句:

SELECT

 T1. 客户编号. 客户名 客户名

 ,T1. 客户编号. 国家编号. 国家名 国家名

 ,T1.客户编号. 国家编号. 区域编号. 区域名 区域名

 ,T1. 总价 总价

 ,T1. 订单优先级 订单优先级

FROM

 订单表 T1

其中的区域名,可以理解为订单的客户的国家的区域名称

实现相同功能的 SQL 语句如下:

SELECT

 T_1_2.C_NAME ` 客户名 `

 ,T_1_3.N_NAME ` 国家名 `

 ,T_1_4.R_NAME ` 区域名 `

 ,T_1_1.O_TOTALPRICE ` 总价 `

 ,T_1_1.O_ORDERPRIORITY ` 订单优先级 `

FROM

 orders T_1_1

 LEFT JOIN

 customer T_1_2 ON T_1_1.O_CUSTKEY=T_1_2.C_CUSTKEY

 LEFT JOIN

 nation T_1_3 ON T_1_2.C_NATIONKEY=T_1_3.N_NATIONKEY

 LEFT JOIN

 region T_1_4 ON T_1_3.N_REGIONKEY=T_1_4.R_REGIONKEY

SQL 中用 JOIN 把四个表关联到一起后获取各表需要的信息。

自关联

想了解雇员的多级上级信息,各级雇员都在雇员表里,只是用上级字段指向上级的那条记录(外键到雇员表自身):

..

DQL 语句:

SELECT

 T1. 雇员姓名 雇员姓名

 ,T1. 职务 职务

 ,T1. 上级. 雇员姓名 上级姓名

 ,T1. 上级. 职务 上级职务

 ,T1. 上级. 性别 上级性别

 ,T1. 上级. 上级. 雇员姓名 上级的上级姓名

FROM

 雇员 T1

SQL 语句:

SELECT

 T_1_1."姓名" "雇员姓名"

 ,T_1_1."职务" "职务"

 ,T_1_2."姓名" "上级姓名"

 ,T_1_2."职务" "上级职务"

 ,T_1_2."性别" "上级性别"

 ,T_1_3."姓名" "上级的上级姓名"

FROM

 "雇员" T_1_1

 LEFT JOIN

 "雇员" T_1_2 ON T_1_1."上级"=T_1_2."雇员 ID"

 LEFT JOIN

 "雇员" T_1_3 ON T_1_2."上级"=T_1_3."雇员 ID"

这个查询虽然只涉及雇员一个表,但要用它三次,T_1_1 代表的是当前雇员,T_1_2 代表的是上级, T_1_3 代表的是上级的上级,把它们 JOIN 后,就把当前雇员、上级、上级的上级拼到一条记录里了。

在 DQL 中是单表查询,上级,上级的上级的信息清晰自然。

互关联

想要查询员工和经理的信息,经理信息虽然也在员工表,但谁是哪个部门的经理存储在部门表里,员工表的部门字段外键到部门表,部门的经理字段又外键回员工表,两表互相关联:

DQL 语句:

SELECT

 T1. 员工姓名 员工姓名

 ,T1. 国籍 员工国籍

 ,T1. 部门. 部门名称 部门名称

 ,T1. 部门. 经理. 员工姓名 经理姓名

 ,T1. 部门. 经理. 国籍 经理国籍

FROM

 员工 T1

SQL 语句:

SELECT

 T_1_1.NAME "员工姓名"

 ,T_1_1.COUNTRY "员工国籍"

 ,T_1_2.NAME "部门名称"

 ,T_1_3.NAME "经理姓名"

 ,T_1_3.COUNTRY "经理国籍"

FROM

 EMPLOYEE T_1_1

 LEFT JOIN

 DEPARTMENT T_1_2 ON T_1_1.DEPTID=T_1_2.ID

 LEFT JOIN

 EMPLOYEE T_1_3 ON T_1_2.MANAGER=T_1_3.ID

DQL 里理解为员工的部门的经理

SQL 里理解为代表员工的员工表 T_1_1、部门表、代表经理的员工表 T_1_3 关联到一起

显然 DQL 的理解更为容易。

重复关联

要查询发货记录里的发货城市、收货城市,发货记录表里会用两个城市字段都外键到同一个维表(城市表):

DQL 语句:

SELECT

 T1. 发货单号 发货单号

 ,T1. 发货日期 发货日期

 ,T1. 发货城市. 名称 发货城市

 ,T1. 收货城市. 名称 收货城市

FROM

 发货记录 T1

SQL 语句:

SELECT

 T_1_1."发货单号" "发货单号"

 ,T_1_1."发货日期" "发货日期"

 ,T_1_2."城市" "发货城市"

 ,T_1_3."城市" "收货城市"

FROM

 "发货记录" T_1_1

 LEFT JOIN

 "城市" T_1_2 ON T_1_1."发货城市"=T_1_2."城市编码"

 LEFT JOIN

 "城市" T_1_3 ON T_1_1."收货城市"=T_1_3."城市编码"

DQL 里理解为发货记录的发货城市、收货城市

SQL 里理解为发货记录表、代表发货城市的城市表 T_1_2、代表收货城市的城市表 T_1_3 做关联

同维表

要查客户信息,主要信息在客户表,对客户的补充信息在 VIP 客户表,两表是一对一的关系 (元数据中设置客户表主键外键到 VIP 客户主键),DQL 中会把它们自然合并在一起,称作同维表,DQL 从客户表就能直接查询两表信息:

DQL 语句:

SELECT

 T1. 客户名称 客户名称

 ,T1. 联系人姓名 联系人姓名

 ,T1.VIP 级别 VIP 级别

 ,T1.VIP 折扣率 VIP 折扣率

FROM

 客户 T1

SQL 语句:

SELECT

 T_1."客户名称" "客户名称"

 ,T_1."联系人姓名" "联系人姓名"

 ,T_1_1."VIP 级别" "VIP 级别"

 ,T_1_1."折扣率" "VIP 折扣率"

FROM

 "客户" T_1

LEFT JOIN

 "VIP 客户" T_1_1 ON T_1."客户 ID"=T_1_1."客户 ID"


SQL 中需要对两表做关联查询。

多字段关联

要查订单的发货情况,发货记录表外键到订单明细表有两个关联字段(订单和商品):

..

这种多字段关联在 DQL 中也可以像单表一样查询:

DQL 语句:

SELECT

 T1. 发货单号 发货单号

 ,T1. 收货日期 收货日期

 ,T1. 订单信息. 数量 数量

 ,T1. 订单信息. 折扣 折扣

FROM

 发货记录 T1

SQL 语句:

SELECT

 T_1_1."发货单号" "发货单号"

 ,T_1_1."收货日期" "收货日期"

 ,T_1_2."数量" "数量"

 ,T_1_2."折扣" "折扣"

FROM

 "发货记录" T_1_1

 LEFT JOIN

 "订单明细" T_1_2 ON T_1_1."订单 ID"=T_1_2."订单 ID" AND T_1_1."产品 ID"=T_1_2."产品 ID"

SQL 里书写多字段关联条件时,注意要写全,不要漏写关联字段的等值条件。

按维对齐关联

想按照日期统计入库数量、支付金额,订单金额。入库数量按照入库单表的入库日期统计;支付金额按照支付单表的支付日期统计;订单金额按照订单表里的发货日期 (也可以如下图切换成按照签单日期) 统计:

..

这个 DQL 语句里也有了 JOIN,但注意这和 SQL 的笛卡尔积 JOIN 不一样,DQL 的 JOIN 是每个表先分别按日期维汇总,汇总之后按照日期再对齐成最后的结果集,而 SQL 的 JOIN 是笛卡尔积成一个大结果集后,再过滤去掉不符合条件的记录,然后再按照维度字段分组汇总:

SELECT

 T1.sum(入库数量) 入库数量求和

 ,T2.sum(支付金额) 支付金额求和

 ,T3.sum(明细金额) 订单明细金额求和

ON 日 日期

FROM

 入库单 T1 BY T1. 入库日期

 JOIN

 支付单 T2 BY T2. 支付日期

 JOIN

 订单明细 T3 BY T3. 订单. 发货日期

SQL 语句:

SELECT

 T_1.F_1 "日期"

 ,T_1.F_2 "入库数量求和"

 ,T_2.F_2 "支付金额求和"

 ,T_3.F_2 "订单明细金额求和"

FROM

 (SELECT T_1."入库日期" F_1,sum(T_1."入库数量") F_2 FROM "入库单" T_1 GROUP BY T_1."入库日期") T_1

 JOIN

 (SELECT T_2."支付日期" F_1,sum(T_2."支付金额") F_2 FROM "支付单" T_2 GROUP BY T_2."支付日期") T_2 ON T_1.F_1=T_2.F_1

 JOIN

 (SELECT T_3_3."发货日期" F_1,sum((T_3_2."单价"*(1-T_3_1."折扣")*T_3_1."数量"))F_2 FROM "订单明细" T_3_1 LEFT JOIN "产品" T_3_2 ON T_3_1."产品 ID"=T_3_2."产品 ID" LEFT JOIN "订单" T_3_3 ON T_3_1."订单 ID"=T_3_3."订单 ID" GROUP BY T_3_3."发货日期") T_3 ON T_1.F_1=T_3.F_1

上面这个查询还没涉及各个表额外的条件,SQL 里写关联字段的等值条件 (ON) 就有好几个,因为疏忽随便漏掉一个都会是错误结果,而 DQL 中各个表先独自算的方式就不容易出错。

后记

探讨上面这些不同方式的关联查询,有助于加深对 DQL 的理解。但在元数据中设置表关系时、界面用户用元数据树做多维分析时没必要刻意区分是哪种关联方式,都是自然而然的,多表关联的需求大多变成了单表查询,既不会理解错,也不会用错,业务人员自然就能灵活地自己搞定更多多维分析需求。