• 使用jOOQ将Oracle风格的隐式连接自动转换为ANSI JOIN


    虽然jOOQ主要被用作Java中嵌入动态SQL的内部SQL DSL,它提供了市场上最好的解决方案,但jOOQ也越来越多地被用于其次要功能之一。在jOOQ 3.9中引入的解析器主要是为了能够解析DDL语句,例如为代码生成目的对你的模式进行逆向工程,我们已经增加了越来越多的功能和SQL转换能力,使解析器能够作为一个独立的产品,通过命令行界面我们的网站或通过普通的jOOQ API来使用。 在jOOQ 3.14中增加的一个功能,主要是对那些使用jOOQ作为分析器的人有用,就是将旧的Oracle风格的隐式连接转换为ANSI JOIN的能力。

    为什么要避免 "隐式连接"?

    大多数RDBMS供应商都支持旧的Oracle风格的隐式连接语法,并进行了适当的优化。在过去,在SQL-92之前,这就是我们用于内部连接表的方式,例如,在查询Sakila数据库时。

    1. SELECT *
    2. FROM actor a, film_actor fa, film f
    3. WHERE a.actor_id = fa.actor_id
    4. AND fa.film_id = f.film_id
    5. 复制代码

    诚然,这个语法是很直观的。只要声明所有你想获取数据的表,然后确保只通过过滤匹配的主键/外键值来保留适当的数据。 当然,这可能会出大错。由于许多明显的原因,例如,当你在添加一个表之后忘记了一个连接谓词。如果查询是复杂的,这可能是很难调试的。解决方案是ANSI JOIN。从SQL-92开始(差不多30年了!),这就是我们在大多数RDBMS中的连接方式。

    1. SELECT *
    2. FROM actor a
    3. JOIN film_actor fa ON a.actor_id = fa.actor_id
    4. JOIN film f ON fa.film_id = f.film_id
    5. 复制代码

    虽然仍然有可能定义错误的连接谓词,但至少不再有可能忘记一个谓词,因为这在语法上是不正确的(除了MySQL,遗憾的是,在那里,ON子句是可选的)。

    1. SELECT *
    2. FROM actor a
    3. JOIN film_actor fa -- Syntax error
    4. JOIN film f -- Syntax error
    5. 复制代码

    jOOQ的隐式JOIN

    注意,人们通常把上述语法称为 "隐式连接",而JPQL和jOOQ回收了另一种"隐式连接 "的术语,它是基于外键路径的,甚至比ANSI SQL语法更不容易出错。使用jOOQ,上述查询可以写成如下。

    1. ctx.select(
    2. FILM_ACTOR.actor().asterisk(),
    3. FILM_ACTOR.asterisk(),
    4. FILM_ACTOR.film().asterisk())
    5. .from(FILM_ACTOR)
    6. .fetch();
    7. 复制代码

    仅仅在查询中存在这些to-one的关系路径,就会隐含地将适当的LEFT JOIN 或INNER JOIN 添加到FROM 子句中。这只是在普通ANSI JOIN之上的便利,而不是替代。

    改造Oracle的隐式连接

    当你有一个旧的代码库,你希望升级并将所有的查询转换为使用ANSI JOIN时,请使用jOOQ来实现。你可以使用jOOQ的编程功能(如前所述),或免费网站www.jooq.org/translate。在网站上,只需选择 "Oracle风格到ANSI JOIN "选项,在左边放置以下SQL:输入

    1. SELECT
    2. a.first_name,
    3. a.last_name,
    4. count(c.category_id)
    5. FROM
    6. actor a,
    7. film_actor fa,
    8. film f,
    9. film_category fc,
    10. category c
    11. WHERE a.actor_id = fa.actor_id
    12. AND fa.film_id = f.film_id
    13. AND fc.category_id = c.category_id
    14. GROUP BY
    15. a.actor_id,
    16. a.first_name,
    17. a.last_name
    18. 复制代码

    输出

    1. SELECT
    2. a.first_name,
    3. a.last_name,
    4. count(c.category_id)
    5. FROM actor a
    6. JOIN film_actor fa
    7. ON a.actor_id = fa.actor_id
    8. JOIN film f
    9. ON fa.film_id = f.film_id
    10. CROSS JOIN (
    11. film_category fc
    12. JOIN category c
    13. ON fc.category_id = c.category_id
    14. )
    15. GROUP BY
    16. a.actor_id,
    17. a.first_name,
    18. a.last_name
    19. 复制代码

    然后......呼啦啦。输出正确地显示了结果,不需要的CROSS JOIN,因为其中一个连接谓词丢失了:是的,这个工具已经帮了大忙!让我们来修正输入的查询。让我们来修正输入的查询:修正输入

    1. SELECT
    2. a.first_name,
    3. a.last_name,
    4. count(c.category_id)
    5. FROM
    6. actor a,
    7. film_actor fa,
    8. film f,
    9. film_category fc,
    10. category c
    11. WHERE a.actor_id = fa.actor_id
    12. AND fa.film_id = f.film_id
    13. AND f.film_id = fc.film_id -- This was missing
    14. AND fc.category_id = c.category_id
    15. GROUP BY
    16. a.actor_id,
    17. a.first_name,
    18. a.last_name
    19. 复制代码

    固定输出

    1. SELECT
    2. a.first_name,
    3. a.last_name,
    4. count(c.category_id)
    5. FROM actor a
    6. JOIN film_actor fa
    7. ON a.actor_id = fa.actor_id
    8. JOIN film f
    9. ON fa.film_id = f.film_id
    10. JOIN film_category fc
    11. ON f.film_id = fc.film_id
    12. JOIN category c
    13. ON fc.category_id = c.category_id
    14. GROUP BY
    15. a.actor_id,
    16. a.first_name,
    17. a.last_name
    18. 复制代码

    如果你使用Oracle的神秘的外层连接语法(+) (或者SQL Server的*= ,它已经有一段时间不被支持了),这也是可行的。你可能有这样的输入:输入

    1. SELECT
    2. a.first_name,
    3. a.last_name,
    4. count(c.category_id)
    5. FROM
    6. actor a,
    7. film_actor fa,
    8. film f,
    9. film_category fc,
    10. category c
    11. WHERE a.actor_id = fa.actor_id(+)
    12. AND fa.film_id = f.film_id(+)
    13. AND f.film_id = fc.film_id(+)
    14. AND fc.category_id(+) = c.category_id
    15. GROUP BY
    16. a.actor_id,
    17. a.first_name,
    18. a.last_name
    19. 复制代码

    产生这样的输出

    1. SELECT
    2. a.first_name,
    3. a.last_name,
    4. count(c.category_id)
    5. FROM actor a
    6. LEFT OUTER JOIN film_actor fa
    7. ON a.actor_id = fa.actor_id
    8. LEFT OUTER JOIN film f
    9. ON fa.film_id = f.film_id
    10. LEFT OUTER JOIN (
    11. film_category fc
    12. RIGHT OUTER JOIN category c
    13. ON fc.category_id = c.category_id
    14. )
    15. ON f.film_id = fc.film_id
    16. GROUP BY
    17. a.actor_id,
    18. a.first_name,
    19. a.last_name
    20. 复制代码

    呃,哇。又来了!其中一个(+) 符号在错误的一边,这就是为什么我们得到这个RIGHT OUTER JOIN 。这个工具再次表明,旧的语法是很容易出错的。让我们来修正它。修正输入

    1. SELECT
    2. a.first_name,
    3. a.last_name,
    4. count(c.category_id)
    5. FROM
    6. actor a,
    7. film_actor fa,
    8. film f,
    9. film_category fc,
    10. category c
    11. WHERE a.actor_id = fa.actor_id(+)
    12. AND fa.film_id = f.film_id(+)
    13. AND f.film_id = fc.film_id(+)
    14. AND fc.category_id = c.category_id(+)
    15. GROUP BY
    16. a.actor_id,
    17. a.first_name,
    18. a.last_name
    19. 复制代码

    固定输出

    1. SELECT
    2. a.first_name,
    3. a.last_name,
    4. count(c.category_id)
    5. FROM actor a
    6. LEFT OUTER JOIN film_actor fa
    7. ON a.actor_id = fa.actor_id
    8. LEFT OUTER JOIN film f
    9. ON fa.film_id = f.film_id
    10. LEFT OUTER JOIN film_category fc
    11. ON f.film_id = fc.film_id
    12. LEFT OUTER JOIN category c
    13. ON fc.category_id = c.category_id
    14. GROUP BY
    15. a.actor_id,
    16. a.first_name,
    17. a.last_name
    18. 复制代码

    总结

    玩一玩,告诉我们你的想法!

     

     学习的节奏慢一点效果才能更好!希望这篇文章对各位有所帮助!共勉。

  • 相关阅读:
    百度最强中文AI作画大模型
    ChatGPT是否可以用于虚拟心理治疗和心理健康支持?
    CentOS手把手教你搭建Zabbix Server,Zabbix Proxy,Zabbix Agent企业级监控平台
    VSCode中ESLint插件修复+配置教程
    想要精通算法和SQL的成长之路 - 找到最终的安全状态
    史上最全JVM性能调优:线程+子系统+类加载+内存分配+垃圾回收
    微服务实战 06 分布式事务框架 seata AT 模式 和 Saga 模式
    localForage.js
    【Spring Cloud】新闻头条微服务项目:文章内容安全审核(需求分析及前期准备)
    LeetCode(力扣)39. 组合总和Python
  • 原文地址:https://blog.csdn.net/YYniannian/article/details/126087895