怎样简单实现不同数据库的表间的 JOIN 运算

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

数据与用例

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

..

主键 owner_id 是车主身份证号。

车辆表 vehicle_master 简化结构如下:

..

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

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

..

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

..

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

..

从逻辑上看,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 目录下(其他数据库类似)。

..

然后启动 esProc IDE,菜单栏选择 Tool-Connect to Data Source,配置 MySQL 标准 JDBC 连接。

..

三个数据库都采用如上方式配置,配置完成后,测试一下连接,点击 Connect,发现刚刚配置的两个数据源变成粉红色证明连接成功。

..

测试一下,按 ctrl+F9 执行脚本,可以正常查询数据说明配置没问题

..

用例实现

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

维表的关联

esProc 实现:

A
1=connect("vehicle")
2=A1.query@x("select * from owner_info").keys@i(owner_id)
3=connect("citizen")
4=A3.query@x("select * from citizen_event where event_time >= DATE_SUB(CURDATE(), INTERVAL 1 YEAR)")
5=A4.switch(citizen_id,A2)
6=A5.groups(citizen_id.reg_city;count(event_id):ent)

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

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

A5 使用 switch 进行外键关联。由于外键指向的维表记录是唯一的,switch 直接将关联字段 citizen_id 转换成 A2 中的记录(实际在内存中存储的是维表记录所在地址)。

..

这种转换是一次性的,后续可以重复使用,而且可以同时处理多个维表的外键关联。关联完成后通过“关联字段. 维表字段”方式就能引用任意维表字段。A6 就通过 citizen_id.reg_city 获得注册地进行分组汇总。

整体运行如下:

..

接下来继续:找出近一年获得表彰的车主姓名和事件描述

在前面代码的基础上增加:

A
7=A5.select(event_type=="Commendation").new(citizen_id.name,description)

还是基于 A5 的关联结果进行计算,实现了复用。

..

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

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

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

按城市统计所有车公民的事件数量:

A
1=connect("vehicle")
2=A1.query@x("select * from owner_info").keys@i(owner_id)
3=connect("citizen")
4=A3.cursor@x("select * from citizen_event")
5=A4.switch(citizen_id,A2)
6=A5.groups(citizen_id.reg_city;count(event_id):ent)

与全内存的写法大部分一样,区别在 A4 使用 cursor 创建游标分批读取数据。esProc 的游标是延迟游标,附加在游标上的计算等到最后取数时才会真正计算。

..

但游标是一次性的,如果想再进行其他计算,比如还要获得表彰的车主。再基于 A5 计算是得不到结果的(注意 A7 的计算结果):

..

这时可以使用 esProc 提供的管道机制:

AB
1=connect("dba")
2=A1.cursor@x("SELECT * FROM orders WHERE EXTRACT(YEAR FROM order_date) = EXTRACT(YEAR FROM CURRENT_DATE) ORDER BY customer_id")
3=connect("dbc")
4=A3.query@x("SELECT * FROM customer").keys@i(customer_id)
5=A2.switch(customer_id,A4)
6cursor A5=A6.groups(customer_id.customer_level;count(1):order_count)
7cursor=A7.group(customer_id).select(~.len()>1).conj().id(customer_id.customer_name)

A6 和 A7 基于 A5 创建管道(A7 是简化写法),B6 基于管道进行分组汇总,结果返回给 A6:

..

B7 则根据另一个管道筛选获得表彰的数据,A7 的结果:

..

主子表的关联

按年份和品牌统计车辆违章次数

A
1=connect("vehicle")
2=A1.query@x("select * from vehicle_master")
3=connect("traffic")
4=A3.query@x("select * from traffic_violation")
5=join(A2:v,plate_no;A4:t,plate_no)
6=A5.groups(year(t.violate_time),v.brand;count(1):cnt)

A5 使用 join 函数根据 plate_no 关联了两个表,其关联结果是这样的:

..

保留了两边完整记录的多层集合,点开可以看到

..

关联完成后,A6 就能通过多层引用进行分组汇总。

..

处理主子表关联时,我们使用了与外键关联 switch 不同的 join 函数,join 函数提供了一些选项,@1 表示左连接,@f 表示全连接,@d 做差集等,用来满足不同的连接需求。事实上,外键关联也可以使用 join 函数来完成。

那为什么不统一用 join 呢?

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

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

按年份和品牌统计车辆违章次数:

A
1=connect("vehicle")
2=A1.cursor@x("select * from vehicle_master order by plate_no")
3=connect("traffic")
4=A3.cursor@x("select * from traffic_violation order by plate_no")
5=joinx(A2:v,plate_no;A4:t,plate_no)
6=A5.groups(year(t.violate_time),v.brand;count(1):cnt)

A2 和 A4 使用 cursor 创建游标,里面的 SQL 都对 plate_no 排序。

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

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/80176.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Nacos源码—4.Nacos集群高可用分析三

大纲 6.CAP原则与Raft协议 7.Nacos实现的Raft协议是如何写入数据的 8.Nacos实现的Raft协议是如何选举Leader节点的 9.Nacos实现的Raft协议是如何同步数据的 10.Nacos如何实现Raft协议的简版总结 6.CAP原则与Raft协议 (1)CAP分别指的是什么 (2)什么是分区以及容错 (3)为…

普通IT的股票交易成长史--20250509晚复盘

声明: 本文章的内容只是自己学习的总结,不构成投资建议。价格行为理论学习可参考简介中的几位,感谢他们的无私奉献。 送给自己的话: 仓位就是生命,绝对不能满仓!!!!&…

python实现点餐系统

使用python实现点餐系统的增加菜品及价格,删除菜品,查询菜单,点菜以及会员折扣价等功能。 代码: 下面展示一些 内联代码片。 # coding utf-8menu {拍黄瓜: 6, 小炒肉: 28, 西红柿炒蛋: 18, 烤鱼: 30, 红烧肉: 38, 手撕鸡: 45,…

从ellisys空口分析蓝牙耳机回连手机失败案例

问题背景: 前两天同事发现我们现在做的项目,耳机在跟某些特定类型安卓手机(尤其是比较新的手机)回连会失败,然后我帮他分析了一些log,记录如下: 回连失败所做步骤如下: 手机和耳机…

教育+AI:个性化学习能否颠覆传统课堂?

近年来,人工智能(AI)技术迅猛发展,逐渐渗透到各行各业,教育领域也不例外。从智能辅导系统到自适应学习平台,AI正在改变传统的教学模式,使个性化学习成为可能。然而,这种变革能否真正…

【C++设计模式之Strategy策略模式】

C设计模式之Strategy策略模式 模式定义核心思想动机(Motivation)结构(Structure)实现步骤1. 定义策略接口(基于继承)2.实现具体策略3.上下文类(Context)4. 在main中调用 应用场景(基于继承)1.定义策略接口2.实现具体策略3.上下文类…

Python企业级MySQL数据库开发实战指南

简介 Python与MySQL的完美结合是现代Web应用和数据分析系统的基石,能够创建高效稳定的企业级数据库解决方案。本文将从零开始,全面介绍如何使用Python连接MySQL数据库,设计健壮的表结构,实现CRUD操作,并掌握连接池管理、事务处理、批量操作和防止SQL注入等企业级开发核心…

matlab转python

1 matlab2python开源程序 https://blog.csdn.net/qq_43426078/article/details/123384265 2 网址 转换网址:https://app.codeconvert.ai/code-converter?inputLangMatlab&outputLangPython 文件比较网址:https://www.diffchecker.com/text-comp…

Vue 3 中编译时和运行时的概念区别

文章目录 前言Vue 3 中的编译时 vs 运行时区别模板在编译时转化为渲染函数编译时的优化处理运行时的工作:创建组件实例与渲染流程前言 详细整理 Vue 3 中编译时和运行时的概念区别,并重点解释为什么组件实例是在运行时创建的。 我会结合官方文档、源码分析和社区解释,确保内…

Spring 框架实战:如何实现高效的依赖注入,优化项目结构?

Spring 框架实战:如何实现高效的依赖注入,优化项目结构? 在当今的 Java 开发领域,Spring 框架占据着举足轻重的地位。而依赖注入作为 Spring 的核心概念之一,对于构建高效、灵活且易于维护的项目结构有着关键作用。本…

创建虚拟服务时实现持久连接。

在调度器中配置虚拟服务,实现持久性连接,解决会话保持问题。 -p 【timeout】 -p 300 这5分钟之内调度器会把来自同一个客户端的请求转发到同一个后端服务器。【不管使用的调度算法是什么。】【称为持久性连接。】 作用:将客户端一段时间…

说下RabbitMQ的整体架构

RabbitMQ 是一个基于 AMQP(Advanced Message Queuing Protocol) 协议的开源消息中间件,RabbitMQ的整体架构围绕消息的生产、路由、存储和消费设计,旨在实现高效、可靠的消息传递,它由多个核心组件协同工作。 核心组件 …

STM32--GPIO

教程 视频 博主教程 STM32系统结构图 GPIO GPIO(General Purpose Input/Output)是STM32内部的一种外设。 一个STM32芯片内存在多个GPIO外设,每个GPIO外设有16个引脚; 比如GPIOA:PA0~PA15; GPIOB:PB0~…

QUIC协议优化:HTTP_3环境下的超高速异步抓取方案

摘要 随着 QUIC 和 HTTP/3 的普及,基于 UDP 的连接复用与内置加密带来了远超 HTTP/2 的性能提升,可显著降低连接握手与拥塞恢复的开销。本文以爬取知乎热榜数据为目标,提出一种基于 HTTPX aioquic 的异步抓取方案,并结合代理 IP设…

[论文阅读]MCP Guardian: A Security-First Layer for Safeguarding MCP-Based AI System

MCP Guardian: A Security-First Layer for Safeguarding MCP-Based AI System http://arxiv.org/abs/2504.12757 推出了 MCP Guardian,这是一个框架,通过身份验证、速率限制、日志记录、跟踪和 Web 应用程序防火墙 (WAF) 扫描来…

Redis客户端缓存的4种实现方式

Redis作为当今最流行的内存数据库和缓存系统,被广泛应用于各类应用场景。然而,即使Redis本身性能卓越,在高并发场景下,应用与Redis服务器之间的网络通信仍可能成为性能瓶颈。 这时,客户端缓存技术便显得尤为重要。 客…

eNSP中路由器OSPF协议配置完整实验和命令解释

本实验使用三台华为路由器(R1、R2和R3)相连,配置OSPF协议实现网络互通。拓扑结构如下: 实验IP规划 R1: GE0/0/0: 192.168.12.1/24 (Area 0)Loopback0: 1.1.1.1/32 (Area 0) R2: GE0/0/0: 192.168.12.2/24 (Area 0)GE0/0/1: 192.…

内网渗透——红日靶场三

目录 一、前期准备 二、外网探测 1.使用nmap进行扫描 2.网站信息收集 3.漏洞复现(CVE-2021-23132) 4.disable_function绕过 5.反弹shell(也,并不是) 6.SSH登录 7.权限提升(脏牛漏洞) 8.信息收集 9.上线msf 三…

解决Win11下MySQL服务无法开机自启动问题

问题描述 在win11系统中,明明将MySQL服务设置成了自动启动,但在重启电脑后MySQL服务还是无法自动启动,每次都要重新到计算机管理的服务中找到服务再手动启动。 解决方式 首先确保mysql服务的启动类型为自动。 设置方法:找到此电…

后端项目进度汇报

项目概述 本项目致力于构建一个先进的智能任务自动化平台。其核心技术是一套由大型语言模型(LLM)驱动的后端系统。该系统能够模拟一个多角色协作的团队,通过一系列精心设计或动态生成的处理阶段,来高效完成各种复杂任务&#xff…