实现不同数据库的表间的 JOIN 运算的极简方法

简介: 跨库计算是数据分析中的常见难题,尤其涉及多数据库系统时,表间 JOIN 操作复杂度显著提升。esProc 提供了一种高效解决方案,能够简化跨库 JOIN 的实现。例如,在车辆管理、交管和公民信息系统中,通过 esProc 可轻松完成如下任务:按城市统计有车公民事件数量、找出近一年获表彰的车主信息,以及按年份和品牌统计车辆违章次数。esProc 支持不同关联场景(如维表关联与主子表关联)的优化算法,如内存索引、游标处理和有序归并,从而大幅提升编码和运算效率。无论是同构还是异构数据源,esProc 均能灵活应对,为复杂数据分析提供强大支持。

数据分析涉及不同业务系统时就要做跨库计算,而表间 JOIN 是最麻烦的,很多数据库都不具备这样的能力,用 Java 取数再计算又太复杂。用 esProc 完成跨库 JOIN 会简单很多。

数据与用例
车辆管理系统(DB_Vehicle)保存了车辆与车主等相关信息,其中车主信息表 owner_info 表结构简化如下:
b4e18827a5f2f58b38fbf296ba64c7f4_1746691824772100.png

主键 owner_id 是车主身份证号。

车辆表 vehicle_master 简化结构如下:

1ae94ca76e0b9c92320b4ff035ede58b_1746691824824100.png

vin 设为主键,plate_no 也是唯一的,两个字段在逻辑上都可以视为主键。

交管系统(DB_Traffic)存储了车辆交通信息,其中违章记录表 traffic_violation 结构如下:

f4e2764c319457b7981ed2cf153752ee_1746691824892100.png

公民信息系统(DB_CitizenEvent)存储了公民相关信息,其中公民事件表 citizen_event 结构如下:

bf2841ffcec10b922e5e62a2142c9f6d_1746691824942100.png

4 个表间逻辑关系简单描述是这样的:

2801b8305f418e8e53123ccedca42d38_1746691825010100.png

从逻辑上看,citizen_event 是 owner_info 是多对一的关系,作为维表的 owner_info 的规模要远小于 citizen_event。

traffic_violation 和 vehicle_master 这两个表也是一对多的关系,规模都可能很大,从后者的角度来看,traffic 更像一个子表,这两个表构成主子关系(plato_no 是 vehicle 表的逻辑主键)。

为什么要区分表间关系呢?

日常的等值 JOIN 基本都会涉及主键(多对多的关联基本没有业务意义),大体可以分为两种:维表关联是一种,是普通字段和维表的主键关联(如 citizen_event 和 owner_info);另一种是某个表的主键与另一个表的主键或部分主键的关联(如 vehicle_master 和 traffic_violation,traffic_violation 表中,可以把 plate_no 和 violation_id 视为共同主键,即 violation_id 是从属于 plate_no 的)。

esProc 在做 JOIN 运算时会根据不同的关联情况选择不同的方法,简化编码的同时还能提升计算效率。这里先有个印象就可以,来看具体例子。

要做这样几个计算:

  1. 按城市统计最近一年有车公民的事件数量,用于分析各城市有车人的“行为活跃度”
  2. 找出近一年获得表彰(Commendation)的车主姓名和事件描述,用以识别“优秀市民”
  3. 按年份和品牌统计车辆违章次数,用于分析某些品牌的车是否更容易违章,用于驾驶行为和车辆品牌的关联研究

如果这些表在同一个数据库,用 SQL 写这些运算并不困难,大概是这样:

1. SELECT   o.city, COUNT(e.event_id) AS event_count
FROM     citizen_event e
JOIN     owner_info o ON e.citizen_id = o.citizen_id
WHERE    e.event_time >= DATE_SUB(CURDATE(), INTERVAL 1 YEAR)
GROUP BY o.city
ORDER BY event_count DESC;

2. SELECT   o.name AS citizen_name, e.description
FROM     citizen_event e
JOIN     owner_info o ON e.citizen_id = o.citizen_id
WHERE    e.event_type = 'Commendation'
  AND    e.event_time >= DATE_SUB(CURDATE(), INTERVAL 1 YEAR);

3. SELECT   YEAR(v.violate_time) AS year, vi.brand, COUNT(v.violation_id) AS violation_count
FROM     traffic_violation v
JOIN     vehicle_info vi ON v.plate_no = vi.plate_no
GROUP BY YEAR(v.violate_time), vi.brand
ORDER BY year, violation_count DESC;

但如果跨库时就麻烦很多了。

安装 esProc
先前往乾学院下载免费的 esProc 标准版。

安装后,配数据库连接,这里三个数据库都是 MySQL。

先把 MySQL JDBC 驱动包放到 [esProc 安装目录]\common\jdbc 目录下(其他数据库类似)。
56f661e30f3af2e76c44fd4b1fa3ff45_1746691822857100.png
然后启动 esProc IDE,菜单栏选择 Tool-Connect to Data Source,配置 MySQL 标准 JDBC 连接。
81324e0588f1bf28dc8837d0f60d16f6_1746691823186100.png
三个数据库都采用如上方式配置,配置完成后,测试一下连接,点击 Connect,发现刚刚配置的两个数据源变成粉红色证明连接成功。
53666cb3be5fc57ff600742f5200902f_1746691823289100.png
测试一下,按 ctrl+F9 执行脚本,可以正常查询数据说明配置没问题

9113aa04c838a48a5e30ac4bc9316a2b_1746691823377100.png
用例实现
下面来实现前面第一个计算需求:按城市统计最近一年有车公民的事件数量。要关联 owner_info 和 citizen_event 两个表,也就是维表的关联计算。

维表的关联
esProc 实现:
image.png
A2 从 vehicle 库查询车主信息,query@x 表示数据全部加载内存后关闭数据库连接,使用 keys@i 设置主键并建立索引,通常事实表会远大于维表,这个索引会被复用很多次,能加快计算速度。

A4 查询事件表,筛选最近一年的数据,都读入内存。

A5 使用 switch 进行外键关联。由于外键指向的维表记录是唯一的,switch 直接将关联字段 citizen_id 转换成 A2 中的记录(实际在内存中存储的是维表记录所在地址)。
589d38ea244f63a0b1a874cb41f17af5_1746691823521100.png
这种转换是一次性的,后续可以重复使用,而且可以同时处理多个维表的外键关联。关联完成后通过“关联字段. 维表字段”方式就能引用任意维表字段。A6 就通过 citizen_id.reg_city 获得注册地进行分组汇总。

整体运行如下:
6212f0fc672c55e1ffff71f437b615e2_1746691823654100.png
接下来继续:找出近一年获得表彰的车主姓名和事件描述。

在前面代码的基础上增加:
image.png
还是基于 A5 的关联结果进行计算,实现了复用。

6411e0aff8627dda627437a392a17599_1746691823759100.png
这里我们再解释一下,跨库关联很多数据库本身就做不了,尤其是异构的情况。esProc 的这种关联能力是与数据源无关的,什么库都可以,甚至其他五花八门的数据源也都没问题,这是其一。其二是,即使与单库 JOIN 相比,esProc 显著区分外键关系也有很大的好处。

书写和理解上,通过点(.)操作符(类似对象. 属性)就能引用外键表的所有字段,有多少层都可以(维表还可能有维表),也很容易表达自关联 / 循环关联的情况。

当 citizen_event 表的数据量很大时,用 esProc 仍然可以处理。不过,当数据量大到无法全部放进内存时,内存地址化方法就不再有效了,因为在外存无法保存事先算好的地址,这时就只能边读入边地址化。

按城市统计所有车公民的事件数量:
image.png
与全内存的写法大部分一样,区别在 A4 使用 cursor 创建游标分批读取数据。esProc 的游标是延迟游标,附加在游标上的计算等到最后取数时才会真正计算。
c01da5b20dc698e41f40c5100892e1f8_1746691823864100.png
但游标是一次性的,如果想再进行其他计算,比如还要获得表彰的车主。再基于 A5 计算是得不到结果的(注意 A7 的计算结果):

9db4d03c4c91b35cdad9da9e33393e5d_1746691823985100.png
这时可以使用 esProc 提供的管道机制:
image.png
A6 和 A7 基于 A5 创建管道(A7 是简化写法),B6 基于管道进行分组汇总,结果返回给 A6:
370a35ce7178e78faa25a3cf2a70882c_1746691824115100.png
B7 则根据另一个管道筛选获得表彰的数据,A7 的结果:
b0b391c60bf4315f2602e34103be3171_1746691824233100.png
主子表的关联
按年份和品牌统计车辆违章次数。

image.png
A5 使用 join 函数根据 plate_no 关联了两个表,其关联结果是这样的:
e12b3fac74f65e15d8cccd53229d491f_1746691824353100.png
保留了两边完整记录的多层集合,点开可以看到
e9d9d2b8bf2194b02580571e9b6b2450_1746691824457100.png
关联完成后,A6 就能通过多层引用进行分组汇总。
84132f0c9369307fef71b869779a83b0_1746691824613100.png
处理主子表关联时,我们使用了与外键关联 switch 不同的 join 函数,join 函数提供了一些选项,@1 表示左连接,@f 表示全连接,@d 做差集等,用来满足不同的连接需求。事实上,外键关联也可以使用 join 函数来完成。

那为什么不统一用 join 呢?

这里我们看到的都是两个表关联,如果存在多个维表(大部分情况),使用 switch 可以将维表(维表可能还有维表)都附加到事实表上,但用 join 就很难表达这种层次关系,书写也不方便。

主子表关联时的两个表可能都很大,利用表的关联字段都是主键(或部分主键)的特性,可以采用有序归并的算法一次遍历就完成关联。

按年份和品牌统计车辆违章次数:
image.png
A2 和 A4 使用 cursor 创建游标,里面的 SQL 都对 plate_no 排序。

A5 使用 joinx 做有序归并,返回的仍是游标。剩下的代码就跟全内存时一样了。

有序遍历利用了关联键有序的特性,只适用于主子表的关联(可对主键有序),但不适用于前面那种维表的外键关联。因为同一个表上可能有多个要参与关联的外键字段,不可能让同一个表同时针对多个字段都有序。这也是区分 JOIN 后采用了不同函数(算法)的原因。

总体来看,esProc 不仅能轻松实现跨库关联,还提供了不同关联场景的实现算法,简单区分后就能获得明显的编码效率和运算效率的提升。

相关文章
|
2月前
|
数据库
【YashanDB知识库】数据库一主一备部署及一主两备部署时,主备手动切换方法及自动切换配置
【YashanDB知识库】数据库一主一备部署及一主两备部署时,主备手动切换方法及自动切换配置
【YashanDB知识库】数据库一主一备部署及一主两备部署时,主备手动切换方法及自动切换配置
|
1月前
|
Oracle 安全 关系型数据库
【Oracle】使用Navicat Premium连接Oracle数据库两种方法
以上就是两种使用Navicat Premium连接Oracle数据库的方法介绍,希望对你有所帮助!
321 28
|
1月前
|
SQL 关系型数据库 MySQL
大数据新视界--大数据大厂之MySQL数据库课程设计:MySQL 数据库 SQL 语句调优方法详解(2-1)
本文深入介绍 MySQL 数据库 SQL 语句调优方法。涵盖分析查询执行计划,如使用 EXPLAIN 命令及理解关键指标;优化查询语句结构,包括避免子查询、减少函数使用、合理用索引列及避免 “OR”。还介绍了索引类型知识,如 B 树索引、哈希索引等。结合与 MySQL 数据库课程设计相关文章,强调 SQL 语句调优重要性。为提升数据库性能提供实用方法,适合数据库管理员和开发人员。
|
1月前
|
监控 数据库
【YashanDB 知识库】ycm 托管数据库时报错 OM host ip:127.0.0.1 is not support join to YCM
在托管数据库时,若 OM 的 IP 被设置为 127.0.0.1,将导致无法托管至 YCM,并使数据库失去监控。此问题源于安装时修改了 OM 的监听 IP。解决方法包括:将 OM 的 IP 修改为本机实际 IP 或 0.0.0.0,同时更新 env 文件及 yasom 后台数据库中的相关配置。经验总结指出,应避免非必要的后台 IP 修改,且数据库安装需遵循规范,不使用仅限本机访问的 IP(如 127.0.0.1)。
|
2月前
|
SQL 数据库连接 Linux
数据库编程:在PHP环境下使用SQL Server的方法。
看看你吧,就像一个调皮的小丑鱼在一片广阔的数据库海洋中游弋,一路上吞下大小数据如同海中的珍珠。不管有多少难关,只要记住这个流程,剩下的就只是探索未知的乐趣,沉浸在这个充满挑战的数据库海洋中。
66 16
|
4月前
|
数据采集 数据库 Python
有哪些方法可以验证用户输入数据的格式是否符合数据库的要求?
有哪些方法可以验证用户输入数据的格式是否符合数据库的要求?
241 75
|
2月前
|
监控 数据库
【YashanDB知识库】ycm托管数据库时报错OM host ip:127.0.0.1 is not support join to YCM
在托管数据库时,若OM的IP被设置为127.0.0.1,则不支持托管到YCM,导致数据库无法正常监控。此问题源于安装时修改了OM监听IP为127.0.0.1。解决方法为将OM的IP修改为本机实际IP或0.0.0.0,并更新yasom后台数据库中的相关配置。建议遵循规范安装,避免使用仅限本机访问的IP(如127.0.0.1),以减少潜在风险。
|
3月前
|
存储 缓存 Java
java语言后台管理ruoyi后台管理框架-登录提示“无效的会话,或者会话已过期,请重新登录。”-扩展知识数据库中密码加密的方法-问题如何解决-以及如何重置若依后台管理框架admin密码-优雅草卓伊凡
java语言后台管理ruoyi后台管理框架-登录提示“无效的会话,或者会话已过期,请重新登录。”-扩展知识数据库中密码加密的方法-问题如何解决-以及如何重置若依后台管理框架admin密码-优雅草卓伊凡
300 3
java语言后台管理ruoyi后台管理框架-登录提示“无效的会话,或者会话已过期,请重新登录。”-扩展知识数据库中密码加密的方法-问题如何解决-以及如何重置若依后台管理框架admin密码-优雅草卓伊凡
|
3月前
|
数据库
【YashanDB 知识库】数据库一主一备部署及一主两备部署时,主备手动切换方法及自动切换配置
**数据库主备切换简介** 在数据库正常或异常情况下,实现主备切换至关重要。若配置不当,主节点故障将影响业务使用,尤其在23.2版本中。原因包括资源紧张或主节点异常。解决方法涵盖手动和自动切换: 1. **一主一备部署**: - **手动切换**:支持Switchover(同步正常时)和Failover(主库损坏时)。 - **自动切换**:启用yasom仲裁选主开关。 2. **一主两备部署**: - 默认最大保护模式,自动切换开启。 需检查并配置自动切换以确保高可用性。经验总结:一主一备默认关闭自动切换,需手动开启;一主两备默认开启。
|
2月前
|
监控 数据库
ycm托管数据库时报错OM host ip:127.0.0.1 is not support join to YCM-YashanDB
ycm托管数据库时报错OM host ip:127.0.0.1 is not support join to YCM-YashanDB

热门文章

最新文章

OSZAR »