DQL 超维分析 - 2 DQL 概念和语法
2 DQL 概念和语法
维度
维度是我们观察数据的角度,用 SQL 理解就是 group by 后面的分组字段。比如常见的产品、客户、地区、日期、品类、员工等等都是维度。
DQL(Dimensional Query Languange)是以维度为核心的查询语言。在 DQL 中,将不是外键的主键字段定义成维度。回忆前面的三种关联,都是与主键相关的。有了维度,表间关系就可以描述成表和维度的关系(毕竟表都有主键 / 逻辑主键)
DQL 中的表间关系描述(中间是维度)
维的层次
维的不同粒度称为维的层次,可以方便从不同粒度观察数据。比如城市的层次包括地区、省、市、县等不同层次;日期包括年、季、月、周等不同层次。有了维和层,我们就可以从不同角度、不同粒度查询数据。
启动 DQL IDE,打开安装包附带的 demo.lmd 元数据文件。我们接下来使用这个已经建好的元数据模型进行实验。
可以看到,基于“市“这个维度,构建出省和大区两个层次,我们再查询时就可以基于不同的地区维度 / 层次统计数据。
比如要从发货记录表中查询每个发货单的大区、省、市信息,DQL 就可以这样写:
SELECT 发货单号,发货城市#大区 大区,发货城市#省,发货城市
FROM 发货记录
其中 发货地区#大区(F#L)
代表查询发货城市的大区层次,通过菜单栏 - 系统 - 翻译 DQL,执行这个语句:
可以看到翻译后的 SQL,大区和省份都按照 DQL 定义的层函数进行了计算:
SELECT T_1."发货单号" "发货单号",CAST(T_1."发货城市"/10000 AS INT) "大区",CAST(T_1."发货城市"/100 AS INT) "发货城市",T_1."发货城市" "发货城市" FROM "发货记录" T_1
维过滤
DQL 可以在维上进行过滤,也就是在 ON 后面写过滤条件,由于 ON 的典型写法是维表名. 字段名,所以 ON 后面的条件也就是对维表进行过滤。比如要查询大区编号是 2,3,4 的发货数量。
DQL:
SELECT 发货记录.COUNT(发货单号) 发货数量
ON 大区 WHERE 大区 in(2,3,4)
FROM 发货记录
BY 发货城市#大区
直接在 ON 后面写 WHERE 过滤条件,在多表关联中基于同一个维对齐时可以只写一个维过滤条件。
翻译后的 SQL:
SELECT CAST(T_1."发货城市"/10000 AS INT) "大区",COUNT(T_1."发货单号") "发货数量" FROM "发货记录" T_1 WHERE CAST(T_1."发货城市"/10000 AS INT) IN(2,3,4) GROUP BY CAST(T_1."发货城市"/10000 AS INT)
时间维与假表
日期时间作为一种特殊的维度在查询时经常使用,比如年、年月、季度、周等等。DQL 为了方便使用日期时间维引入了“假表”的概念。假表是指没有物理数据对应的单字段逻辑数据表。事实上,假表不仅限用于日期时间维,其他任何想要方便使用的维度都可以设为假表。
在工具栏 - 增加假表,增加一个“日”假表:
这个表有且只有一个主键字段“日”,其格式为 YYYYMMDD,下面通过层函数分别计算出了月和年两个层次(分别对应月和年假表)。
比如要按年统计订单数量,DQL 这样写:
SELECT 订单.count(*) 订单数
ON 年 AS 发货年
FROM 订单
BY 发货日期#年
翻译后的 SQL:
SELECT YEAR(T_1."发货日期") "发货年",count(*) "订单数"
FROM "订单" T_1
GROUP BY YEAR(T_1."发货日期")
有了假表,对于所有日期维度,就都可以按照假表定义的各种维度进行分析了,页面上看到的每个日期字段都有这些维度,很容易构建出下面的界面,使用非常清晰。
外键
前面说过,DQL 定义的 JOIN 都与主键有关,外键表、同维表、主子表都可以通过主外键关系来描述,这样我们就可以通过外键建立起各类表间关系,一次性把表间关系表述清楚以后就不用再动了,这就是 DQL 倡导的一次建模,也称为非按需建模(与宽表等按需建模相对)。
比如,订单表和客户维表进行外键关联,在 IDE 里这样设置:
这时我们再查询订单和客户信息,就可以只基于订单一个表进行,DQL 语句:
SELECT 订单,客户.客户名称,客户.联系人姓名,签单日期
FROM 订单
其翻译后的 SQL 是多表关联的:
SELECT T_1_1."订单ID" "订单",T_1_2."客户名称" "客户名称",T_1_2."联系人姓名" "联系人姓名",T_1_1."签单日期" "签单日期"
FROM "订单" T_1_1
LEFT JOIN "客户" T_1_2
ON T_1_1."客户ID"=T_1_2."客户ID"
前面的日期假表,也是通过外键与日期时间字段进行关联,每个字段都关联日期假表就能获得上面页面查询的效果。
同维表
同维表之间的关系描述与外键表相同,在使用时可以基于同维表中任意表查询,所有同维表的字段都当成本表字段使用,不需要再写 外键. 维表字段 形式。
比如客户和 VIP 客户的信息,这两个表互为同维表,DQL 直接写字段就可以:
SELECT 客户,客户名称,VIP级别
FROM 客户
翻译后的 SQL:
SELECT T_1."客户ID" "客户",T_1."客户名称" "客户名称",T_1_1."VIP级别" "VIP级别"
FROM "客户" T_1
LEFT JOIN "VIP客户" T_1_1
ON T_1."客户ID"=T_1_1."客户ID"
查询时,还可以限定从同维表中读取(找不到继续在同维表的同维表中查找),DQL:
SELECT 客户,客户名称,VIP级别@VIP客户
FROM 客户
用 @同维表
来指定要查询的字段来源。
SQL:
SELECT T_1."客户ID" "客户",T_1."客户名称" "客户名称",T_1_1."VIP级别" "VIP级别"
FROM "客户" T_1
LEFT JOIN "VIP客户" T_1_1
ON T_1."客户ID"=T_1_1."客户ID"
子表
主子表的关系是子表的部分主键作为外键指向主表的主键。比如,订单和订单明细表,通过设置外键后构建出的主子关系。
从订单表查询订单以及订单金额(来源订单明细)等信息,DQL 语句:
SELECT 订单, 客户, 订单@订单明细.SUM(单价*数量*(1-折扣)) 订单金额
FROM 订单
此处的语法[email protected](F)
表 T 的子表 S 的广义字段 F 的聚合式(count/avg/sum/max/min),通过主表直接针对子表聚合。
翻译后的 SQL:
SELECT T_1."订单ID" "订单",T_1."客户ID" "客户",T_3.F_2 "订单金额"
FROM "订单" T_1
JOIN (SELECT T_3."订单ID" F_1,SUM(T_3."单价"*T_3."数量"*(1-T_3."折扣")) F_2
FROM "订单明细" T_3
GROUP BY T_3."订单ID") T_3
ON T_1."订单ID"=T_3.F_1
使用非 DQL 标准函数
DQL 定义了很多标准函数,在 DQL 语句里可以直接使用。但有时还想使用数据库的特别函数,DQL 也提供了使用非标准函数的方式,DQL 语句:
SELECT 年月,供应商,总金额
FROM 入库单汇总
WHERE 年月 = YEAR($SYSDATE()) * 100 + MONTH(NOW())
使用 $ 标注的函数提交数据库时会保留原样(不被翻译)。
通过上面的概念和建模介绍,我们已经基本掌握的 DQL 的语法,相对 SQL 也更简单。总结一下 DQL 语法:
select … |
广义字段、子表字段或其聚合式的表达式 |
T…F as A |
T的广义字段及聚合式、子表字段聚合式 |
on … |
设定对齐维,仅单表可省略 |
D as A |
维度D |
where … |
基于所有D的条件表达式 |
from … |
|
T as A |
表T |
where … |
T的过滤条件 |
by … |
T的分组,与on依次对应 |
having … |
最后选出条件 |
order by … |
排序子句 |