一晃一年多过去,openGauss都推出7.0的版本了,于是打开whats new学习下7.0版本的新特性,发现7.0版本在7.0.0 RC-1版本新增了对cross apply join的支持,并且在官方文档中,介绍cross join等效于inner join,于是想着整理下哥儿几个的用法,以及三者的区别。

一、哥仨的相同点

  1. 均用于多表数据关联查询;

  2. 均通过组合不同表数据生成新结果集;

  3. 执行过程都涉及表之间的行匹配逻辑。

二、核心区别

1. INNER JOIN

  • 必须指定ON条件进行行匹配;

  • 仅返回满足ON后面所跟的关联条件的记录;

  • 典型场景:关联部门表和员工表。

2. CROSS JOIN

  • 无需指定关联条件;

  • 返回笛卡尔积(所有行组合),两两交叉;

  • 典型场景:生成组合测试数据。

  • 右侧表达式不可引用左表及其字段

3. CROSS APPLY

  • 右侧表达式可引用左表字段;

  • 支持表值函数动态计算;

  • 典型场景:逐行计算关联子查询。

三、示例说明

示例表结构

1.部门表

    openGauss=# select * from dept; dept_id | dept_name | dept_manager---------+-----------+--------------       1 | marketing |       2 | sales     |       3 | develop   |       4 | finacial  |           41(4 rows)

    2.员工表

      openGauss=# select * from employ;emp_id |  emp_name  | dept_id--------+------------+---------11 | zhaofeng   |       112 | zhangfeng2 |       121 | lifang     |       222 | lifang2    |       231 | nezha      |       332 | nezha2     |       3(6 rows)

      1. INNER JOIN示例

        SELECT e.emp_name, d.dept_name FROM employ eINNER JOIN dept d ON e.dept_id = d.dept_id;

        执行结果:

        返回部门表中dept_id和员工表中dept_id相同的数据。

          openGauss=# SELECT e.emp_name,d.dept_name FROM employ e INNER JOIN dept d ON e.dept_id = d.dept_id;emp_name  | dept_name------------+-----------zhaofeng   | marketingzhangfeng2 | marketinglifang     | saleslifang2    | salesnezha      | developnezha2     | develop(6 rows)

          2. CROSS JOIN示例

            SELECT e.name, d.dept_name FROM employ eCROSS JOIN departments d;

            执行结果是返回所有员工表中的数据和部门表中的数据的交叉组合结果

              openGauss=# SELECT e.emp_name,d.dept_name FROM employ e CROSS JOIN dept d;emp_name  | dept_name------------+-----------zhaofeng   | marketingzhaofeng   | saleszhaofeng   | developzhaofeng   | finacialzhangfeng2 | marketingzhangfeng2 | saleszhangfeng2 | developzhangfeng2 | finaciallifang     | marketinglifang     | saleslifang     | developlifang     | finaciallifang2    | marketinglifang2    | saleslifang2    | developlifang2    | finacialnezha      | marketingnezha      | salesnezha      | developnezha      | finacialnezha2     | marketingnezha2     | salesnezha2     | developnezha2     | finacial(24 rows)

              返回结果记录条数为员工表记录条数(6)*部门表记录条数(4)=24条。由于该操作返回结果为笛卡尔积,在实际业务系统中要谨慎使用。

              如官网所说,我们尝试执行下inner join on (true), 以1=1构造true条件,执行结果如下所示:

                openGauss=# SELECT e.emp_name,d.dept_name FROM employ e INNER JOIN dept d ON 1=1;emp_name  | dept_name------------+-----------zhaofeng   | marketingzhaofeng   | saleszhaofeng   | developzhaofeng   | finacialzhangfeng2 | marketingzhangfeng2 | saleszhangfeng2 | developzhangfeng2 | finaciallifang     | marketinglifang     | saleslifang     | developlifang     | finaciallifang2    | marketinglifang2    | saleslifang2    | developlifang2    | finacialnezha      | marketingnezha      | salesnezha      | developnezha      | finacialnezha2     | marketingnezha2     | salesnezha2     | developnezha2     | finacial(24 rows)

                不过在cross join中,右侧不能引用左侧的表或者列,否则会报错,如下所示:

                  openGauss=# SELECT d.dept_name,v.emp_id FROM dept d CROSS join (SELECT * FROM employ e WHERE e.dept_id = d.dept_id) v WHERE d.dept_name IN ('marketing', 'sales') ORDER BY d.dept_name, v.emp_id;ERROR: invalid reference to FROM-clause entry for table "d"LINE 1: ...SS join (SELECT * FROM employ e WHERE e.dept_id = d.dept_id)...^HINT: There is an entry for table "d", but it cannot be referenced from this part of the query.

                  但是cross apply就可以引用左侧的表了。

                  3. CROSS APPLY示例

                    openGauss=# SELECT d.dept_name,v.emp_id,v.emp_name FROM dept d CROSS apply (SELECT * FROM employ e WHERE e.dept_id = d.dept_id) v WHERE d.dept_name IN ('marketing', 'sales') ORDER BY d.dept_name, v.emp_id;dept_name | emp_id |  emp_name-----------+--------+------------marketing |     11 | zhaofengmarketing |     12 | zhangfeng2sales     |     21 | lifangsales     |     22 | lifang2(4 rows)

                    执行结果返回的是符合部门过滤条件的,以及e.dept_id = d.dept_id的记录。

                    根因溯源

                    那么为什么cross apply会支持右边的子查询可以引用左表的内容呢?

                    这个和cross apply语法的执行机制有关。CROSS APPLY 的核心逻辑是 对左表的每一行动态计算右表表达式(通常是表值函数或子查询),并将结果与左表当前行组合。这种“行间关联”机制允许右表表达式直接引用左表的字段,因为它的计算是 基于左表当前行的上下文CROSS JOIN 在逻辑上会先展开两表的笛卡尔积,此时右表子查询无法感知左表的单行上下文。

                    Cross join语法在实际业务中也有很多适用的场景,体现出他特有的灵活,高效以及易读易理解的优势。

                    灵活性:解决需要基于左表每行动态关联右表的场景(如分页、TOP-N、函数调用)。

                    性能优化:避免先生成笛卡尔积再过滤的巨大开销。

                    语义清晰:直观表达“逐行处理”的业务逻辑。

                    后记

                    相应的,和out join对应的,在7.0版本也支持了out apply语法,应用out apply的时候,在右侧也可以直接引用左侧的表,如果大家感兴趣,将在后面的文章中继续说明。

                    本文作者

                    本文内容来自于数据库领域资深技术专家赵锋老师,希望我们的文章正好能解决你的问题。

                    Logo

                    华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

                    更多推荐