当前位置: 首页 > news >正文

DQL 超维分析 - 1 DQL 原理

1 DQL 原理

关联分类

业务中几乎所有关联运算都可以分成下面三种等值 JOIN。

1. 外键关联
表 A 的某个字段和表 B 的主键字段关联(所谓字段关联,就是前一节说过的在等值 JOIN 的过滤条件中要对应相等的字段)。A 表称为事实表,B 表称为维表。A 表中与 B 表主键关联的字段称为 A 指向 B 的外键,B 也称为 A 的外键表。
这里说的主键是指逻辑上的主键,也就是在表中取值唯一、可以用于唯一确定某条记录的字段,不一定是在数据库表上建立过的主键。
外键表是多对一的关系,且只有 JOIN 和 LEFT JOIN,而 FULL JOIN 非常罕见。
典型例子:商品交易表和商品信息表。
显然,外键关联是不对称的。事实表和维表的位置不能互换。

2. 同维表
表 A 的主键与表 B 的主键关联,A 和 B 互称为同维表。同维表是一对一的关系,JOIN、LEFT JOIN 和 FULL JOIN 的情况都会有,不过在大多数数据结构设计方案中,FULL JOIN 也相对少见。
典型例子:员工表和经理表。
同维表之间是对称的,两个表的地位相同。同维表还构成是等价关系,A 和 B 是同维表,B 和 C 是同维表,则 A 和 C 也是同维表。

3. 主子表
表 A 的主键与表 B 的部分主键关联,A 称为主表,B 称为子表。主子表是一对多的关系,只有 JOIN 和 LEFT JOIN,不会有 FULL JOIN。
典型例子:订单和订单明细。
主子表也是不对称的,有明确的方向。
在 SQL 的概念体系中并不区分外键表和主子表,多对一和一对多从 SQL 的观点看来只是关联方向不同,本质上是一回事。确实,订单也可以理解成订单明细的外键表。但是,在这里要把它们区分开,将来在简化语法和性能优化时将使用不同的手段。

这三种 JOIN 已经涵盖了绝大多数等值 JOIN 的情况,甚至可以说几乎全部有业务意义的等值 JOIN 都属于这三类,把等值 JOIN 限定在这三种情况之中,几乎不会减少其适应范围。而且所有关联都涉及主键,就可以利用关联都涉及主键这个特征来简化 JOIN 的代码书写。

DQL 实现原理

1. 外键属性化
先看个例子,设有如下两个表:

employee 员工表id 员工编号name 姓名nationality 国籍department 所属部门department 部门表id 部门编号name 部门名称manager 部门经理

employee 表和 department 表的主键都是其中的 id 字段,employee 表的 department 字段是指向 department 表的外键,department 表的 manager 字段又是指向 employee 表的外键(因为经理也是个员工)。这是很常规的表结构设计。

现在想问一下:哪些美国籍员工有一个中国籍经理?
用 SQL 写出来是个三表 JOIN 的语句:

SELECT A.* 
FROM employee A
JOIN department B ON A.department=B.id
JOIN employee C ON B.manager=C.id
WHERE A.nationality='USA' AND C.nationality='CHN'

首先要 FROM employee 用于获取员工信息,然后这个 employee 表要和 department 做 JOIN 获取员工的部门信息,接着这个 department 表还要再和 employee 表 JOIN 要获取经理的信息,这样 employee 表需要两次参与 JOIN,在 SQL 语句中要为它起个别名加以区分,整个句子就显得比较复杂难懂。

如果把外键字段直接理解成它关联的维表记录,用 DQL 写出来是这样:

SELECT * FROM employee
WHERE nationality='USA' AND department.manager.nationality='CHN'

department.manager.nationality 部分表示当前员工的“所属部门的经理的国籍”。把外键字段理解成维表的记录后,维表的字段就相当于外键的属性了,department.manager 即是“所属部门的经理”,而这个字段在 department 表中仍然是个外键,那么它对应的维表记录字段可以继续理解为它的属性,也就会有 department.manager.nationality,即“所属部门的经理的国籍”。
这种对象式的理解方式称为外键属性化,显然比笛卡尔积过滤的理解方式要自然直观得多。外键表 JOIN 时并不会涉及到两个表的乘法,外键字段只是用于找到外键表中对应的那条记录,完全不会涉及到笛卡尔积这种有乘法特性的运算。
前面约定,外键关联时维表中关联键必须是主键,这样,事实表中每一条记录的外键字段关联的维表记录就是唯一的,也就是说 employee 表中每一条记录的 department 字段唯一关联一条 department 表中的记录,而 department 表中每一条记录的 manager 字段也唯一关联一条 employee 表中的记录。这就保证了对于 employee 表中的每一条记录,department.manager.nationality 都有唯一的取值,可以被明确定义。
但是,SQL 对 JOIN 的定义中并没有主键的约定,如果基于 SQL 的规则,就不能认定与事实表中外键关联的维表记录有唯一性,有可能发生与多条记录关联,对于 employee 表的记录来讲,department.manager.nationality 没有明确定义,就不能使用了。

事实上,这种对象式写法在高级语言(如 C,Java)中很常见,在这类语言中,数据就是按对象方式存储的。employee 表中的 department 字段取值根本就是一个对象,而不是编号。其实许多表的主键取值本身并没有业务意义,仅仅是为了区分记录,而外键字段也仅仅是为了找到维表中的相应记录,如果外键字段直接是对象,就不需要再通过编号来标识了。不过,SQL 不能支持这种存储机制,还要借助编号。
还要注意的是,说过外键关联是不对称的,即事实表和维表是不对等的,只能基于事实表去找维表字段,而不会有倒过来的情况。

2. 同维表等同化
同维表的情况相对简单,还是从例子开始,设有两个表:

employee 员工表id 员工编号name 姓名salary 工资...
manager 经理表id 员工编号allowance 岗位津贴....

两个表的主键都是 id,经理也是员工,两表共用同样的员工编号,经理会比普通员工多一些属性,另用一个经理表来保存。
现在要统计所有员工(包括经理)的总收入(加上津贴)。
用 SQL 写出来还是会用到 JOIN:

SELECT employee.id, employee.name, employy.salary+manager.allowance
FROM employee
LEFT JOIN manager ON employee.id=manager.id

而对于两个一对一的表,其实可以简单地把它们看成一个表,DQL 写出来:

SELECT id,name,salary+allowance
FROM employee

类似地,根据的约定,同维表 JOIN 时两个表都是按主键关联的,相应记录是唯一对应的,salary+allowance 对 employee 表中每条记录都是唯一可计算的,不会出现歧义。这种简化方式称为同维表等同化
同维表之间的关系是对等的,从任何一个表都可以引用到其它同维表的字段。

3. 子表集合化
订单及订单明细是典型的主子表:

Orders 订单表id 订单编号customer 客户date 日期...
OrderDetail 订单明细id 订单编号no 序号product 订购产品price 价格...

Orders 表的主键是 id,OrderDetail 表中的主键是 (id,no),前者的主键是后者的一部分。
现在想计算每张订单的总金额。
用 SQL 写出来会是这样:

SELECT Orders.id, Orders.customer, SUM(OrderDetail.price)
FROM Orders
JOIN OrderDetail ON Orders.id=OrderDetail.id
GROUP BY Orders.id, Orders.customer

要完成这个运算,不仅要用到 JOIN,还需要做一次 GROUP BY,否则选出来的记录数太多。
如果把子表中与主表相关的记录看成主表的一个字段,那么这个问题也可以不再使用 JOIN 以及 GROUP BY,DQL 写法:

SELECT id, customer, OrderDetail.SUM(price)
FROM Orders

与普通字段不同,OrderDetail 被看成 Orders 表的字段时,其取值将是一个集合,因为两个表是一对多的关系。所以要在这里使用聚合运算把集合值计算成单值。DQL 这种简化方式称为子表集合化

DQL 运行原理

有了这三种手段,DQL 就把将多表关联转换成了单表查询,消除了关联。再将 DQL 工程化成产品就可以实现灵活自助查询了。

..

工程化后的 DQL 产品主要包括这样几部分:

DQL Server:

DQL 服务器,负责存储元数据,将应用发送的 DQL 语句翻译成 SQL 到数据库执行,并接收查询结果返回给前端应用。

DQL 元数据 IDE:

建模工具,用于描述表间关系,生成语义层模型。DQL Server 会解析模型(元数据)文件实现 DQL 查询。

DQL JDBC:

DQL 封装的标准 JDBC 接口,可以集成到应用中使用。从应用的角度,DQL Server 可以被看待成逻辑数据库。

http://www.dtcms.com/a/314570.html

相关文章:

  • 无公网环境下在centos7.9上使用kk工具部署k8s平台(amd64架构)
  • redis可视化工具汇总
  • Ubuntu系统VScode实现opencv(c++)图像二维直方图
  • 免费MCP: JSON 转 Excel MCP
  • N4语法书
  • 数据结构——图及其C++实现(1)概念、存储结构、遍历
  • 【09】C++实战篇——C++ 生成静态库.lib 及 C++调用lib,及实际项目中的使用技巧
  • 10.苹果ios逆向-FridaHook-ios中的算法-CCMD5
  • curl发送文件bodyParser无法获取请求体的问题分析
  • RAG From Scratch 系列教程-3: Routing
  • 将AAL图谱对应到Yeo7大网络中【原理,代码分析】
  • 断点续传Demo实现
  • 16.8 华为昇腾CANN架构深度实战:3大核心引擎解析与性能优化216%秘籍
  • C++高频知识点(十四)
  • 如果发送的数据和接受的数据不一致时,怎么办?
  • 从 Hive 数仓出发,全面剖析 StarRocks、MySQL、HBase 的使用场景与区别
  • Linux-Day02.Linux指令
  • Vue 3 + AntV X6 实现流程编辑功能
  • C语言-指针[指针数组和数组指针]
  • 【web应用】Maven:Java 生态的构建与依赖管理利器
  • LeetCode算法日记 - Day 1: 移动零、复写零
  • 排序算法——归并排序(图文演示)
  • 最小二乘法MSE
  • 【Linux】重生之从零开始学习运维之GTID复制
  • 【动态规划 | 回文字串问题】动态规划解回文问题的核心套路
  • docker镜像源配置教程,以及解决安装好docker配置镜像源后,出现报错。Job for docker.service failed
  • 在 C++ 中实现类似 Vue 3 的 Pinia 状态管理库
  • C++模板知识点3『std::initializer_list初始化时逗号表达式的执行顺序』
  • 2025-08月特辑---私有化部署gitea仓库
  • Android UI 组件系列(九):ListView 性能优化与 ViewHolder 模式实战