easy-query隐式Group革命性OLAP优化JAVA下最强查询ORM没有之一子查询归并 ...

农民  论坛元老 | 2025-4-21 07:33:27 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 1585|帖子 1585|积分 4755

easy-query JAVA下最强查询ORM没有之一的恣意子查询归并革命性OLAP优化

前言

对于大部分OLTP而言的查询市面上常见的orm已经能很好的处置惩罚,只要建立好对象关系那么就可以非常简单的实现隐式join或者隐式子查询
easy-query一款将ORM的Relational在查询中表现得淋漓尽致,支持手动join、手动子查询也支持隐式join、隐式子查询并且支持手动和隐式混用,经过2年半时间的打磨努力从追赶.net orm到逾越我敢说内里有着许多人的努力和众多框架作者的头脑鉴戒才气让easy-query在短短2年变得越来越完善功能也是越来越强盛

  • 2023年如何实现更多的sql让orm在项目开发中变得可用做到零sql
  • 2024实现如何不以翻译sql为目的实现用户心中无sql编写表达式
  • 2025至今优化复杂sql在olap下的优化处置惩罚
当你在想怎么连表的时候、当你在想这个sql怎么翻译成表达式的时候其实你已经陷入了“自证”的环节,我们应该要思量的是你盼望实现的功能比如查询用户条件是银行卡有2张的,而不是写好一堆sql然后问orm作者这个怎么写,从面向sql结果向面向需求编写表达式的变化。
介绍

easy-query

文档地址 https://www.easy-query.com/easy-query-doc/
GITHUB地址 https://github.com/dromara/easy-query
GITEE地址 https://gitee.com/dromara/easy-query
对象关系


隐式join

常见的隐式join如下
查询银行卡要求是筛选条件是银行卡所属用户手机号包含1234并且所属银行是工商银行的
  1.         List<SysBankCard> bankCards = easyEntityQuery.queryable(SysBankCard.class)
  2.                 .where(bank_card -> {
  3.                     bank_card.user().phone().like("1234");
  4.                     bank_card.bank().name().eq("工商银行");
  5.                 }).toList();
复制代码
天生的sql为
  1.     SELECT
  2.         t.`id`,
  3.         t.`uid`,
  4.         t.`code`,
  5.         t.`type`,
  6.         t.`bank_id`,
  7.         t.`open_time`
  8.     FROM
  9.         `t_bank_card` t
  10.     LEFT JOIN
  11.         `t_sys_user` t1
  12.             ON t1.`id` = t.`uid`
  13.     INNER JOIN
  14.         `t_bank` t2
  15.             ON t2.`id` = t.`bank_id`
  16.     WHERE
  17.         t1.`phone` LIKE '%1234%'
  18.         AND t2.`name` = '工商银行'
复制代码
有些小伙伴就很奇怪为什么对于user而言是left join对于bank而言缺是inner join缘故原由就是在创建对象关系的时候我们指定了SysBankCard和SysBank的关系是外键关系所以不大概存在有银行卡没有银行的情况
具体对象如下
  1. @Table("t_bank")
  2. @EntityProxy
  3. @Data
  4. @FieldNameConstants
  5. @EasyAlias("bank")
  6. public class SysBank implements ProxyEntityAvailable<SysBank, SysBankProxy> {
  7.     @Column(primaryKey = true)
  8.     private String id;
  9.     /**
  10.      * 银行名称
  11.      */
  12.     private String name;
  13.     /**
  14.      * 成立时间
  15.      */
  16.     private LocalDateTime createTime;
  17.     /**
  18.      * 拥有的银行卡
  19.      */
  20.     @Navigate(value = RelationTypeEnum.OneToMany,
  21.             selfProperty = {"id"},
  22.             targetProperty = {"bankId"})
  23.     private List<SysBankCard> bankCards;
  24. }
  25. @Table("t_bank_card")
  26. @EntityProxy
  27. @Data
  28. @FieldNameConstants
  29. @EasyAlias("bank_card")
  30. public class SysBankCard implements ProxyEntityAvailable<SysBankCard , SysBankCardProxy> {
  31.     @Column(primaryKey = true)
  32.     private String id;
  33.     private String uid;
  34.     /**
  35.      * 银行卡号
  36.      */
  37.     private String code;
  38.     /**
  39.      * 银行卡类型借记卡 储蓄卡
  40.      */
  41.     private String type;
  42.     /**
  43.      * 所属银行
  44.      */
  45.     private String bankId;
  46.     /**
  47.      * 用户开户时间
  48.      */
  49.     private LocalDateTime openTime;
  50.     /**
  51.      * 所属银行
  52.      */
  53.     @Navigate(value = RelationTypeEnum.ManyToOne, selfProperty = {"bankId"}, targetProperty = {"id"})
  54.     @ForeignKey//可以不加 加了就是InnerJoin处理更多细节查看注解篇章
  55.     private SysBank bank;
  56.     /**
  57.      * 所属用户
  58.      */
  59.     @Navigate(value = RelationTypeEnum.ManyToOne, selfProperty = {"uid"}, targetProperty = {"id"})
  60.     private SysUser user;
  61. }
  62. @Table("t_sys_user")
  63. @EntityProxy
  64. @Data
  65. @FieldNameConstants
  66. @EasyAlias("user")
  67. public class SysUser implements ProxyEntityAvailable<SysUser , SysUserProxy> {
  68.     @Column(primaryKey = true)
  69.     private String id;
  70.     private String name;
  71.     private String phone;
  72.     private Integer age;
  73.     private LocalDateTime createTime;
  74.     /**
  75.      * 用户拥有的银行卡数
  76.      */
  77.     @Navigate(value = RelationTypeEnum.OneToMany, selfProperty = {"id"}, targetProperty = {"uid"})
  78.     private List<SysBankCard> bankCards;
  79.     /**
  80.      * 用户拥有的书本
  81.      */
  82.     @Navigate(value = RelationTypeEnum.OneToMany, selfProperty = {"id"}, targetProperty = {"uid"})
  83.     private List<SysUserBook> userBooks;
  84. }
  85. @Table("t_sys_user_book")
  86. @EntityProxy
  87. @Data
  88. @FieldNameConstants
  89. @EasyAlias("user_book")
  90. public class SysUserBook implements ProxyEntityAvailable<SysUserBook , SysUserBookProxy> {
  91.     private String id;
  92.     private String name;
  93.     private String uid;
  94.     private BigDecimal price;
  95. }
复制代码
演示了隐式join后我在演示对应的隐式子查询
隐式子查询

筛选出用户拥有至少2张工商银行卡且还未在建设银行开户的用户
  1. List<SysUser> list = easyEntityQuery.queryable(SysUser.class)
  2.         .where(user -> {
  3.             user.bankCards().where(card -> {
  4.                 card.bank().name().eq("工商银行");
  5.             }).count().ge(2L);
  6.             user.bankCards().none(card -> {
  7.                 card.bank().name().eq("建设银行");
  8.             });
  9.         }).toList();
复制代码
天生的sql为
  1. SELECT
  2.     t.`id`,
  3.     t.`name`,
  4.     t.`phone`,
  5.     t.`age`,
  6.     t.`create_time`
  7. FROM
  8.     `t_sys_user` t
  9. WHERE
  10.     (
  11.         SELECT
  12.             COUNT(*)
  13.         FROM
  14.             `t_bank_card` t1
  15.         INNER JOIN
  16.             `t_bank` t2
  17.                 ON t2.`id` = t1.`bank_id`
  18.         WHERE
  19.             t1.`uid` = t.`id`
  20.             AND t2.`name` = '工商银行'
  21.     ) >= 2
  22.     AND NOT ( EXISTS (SELECT
  23.         1
  24.     FROM
  25.         `t_bank_card` t3
  26.     INNER JOIN
  27.         `t_bank` t4
  28.             ON t4.`id` = t3.`bank_id`
  29.     WHERE
  30.         t3.`uid` = t.`id`
  31.         AND t4.`name` = '建设银行' LIMIT 1))
复制代码
这里出现了两个子查询分别是[至少2张工商银行卡]和[还未在建设银行开户的用户]两个条件
那么到这里就是大部分ORM能做的事情了,乃至大部分ORM连这点都做不到,所以说如果你的ORM支持子查询那么你的ORM在面对复杂查询如果只能做到这么一点那么只能算是一个刚到及格线的ORM,因为如果出现多个子查询或者在多对多下这种处置惩罚是非常致命的,尤其是查询用户条件是菜单为/admin这种
正片开始

所谓鱼(可维护性)和熊掌(性能)不可兼得,那么easy-query是如那边置惩罚像这种情况下的呢,又是如何让你兼得鱼和熊掌的有请我们的本期主角【隐式Group】
隐式Group

筛选出用户拥有至少2张工商银行卡且还未在建设银行开户的用户
对于相同的条件easy-query只必要一个设置就可以让你兼得鱼(可维护性)和熊掌(性能)
  1. List<SysUser> list = easyEntityQuery.queryable(SysUser.class)
  2.         //启用隐式group,无论实在where还是select里面用到的bankCards子查询都会复用同一个隐式Group
  3.         .subQueryToGroupJoin(u->u.bankCards())
  4.         .where(user -> {
  5.             //至少2张工商银行
  6.             user.bankCards().where(card -> {
  7.                 card.bank().name().eq("工商银行");
  8.             }).count().ge(2L);
  9.             //没有建行卡
  10.             user.bankCards().none(card -> {
  11.                 card.bank().name().eq("建设银行");
  12.             });
  13.         }).toList();
复制代码
我们再来看天生的sql
  1. SELECT
  2.     t.`id`,
  3.     t.`name`,
  4.     t.`phone`,
  5.     t.`age`,
  6.     t.`create_time`  
  7. FROM
  8.     `t_sys_user` t
  9. LEFT JOIN
  10.     (
  11.         SELECT
  12.             t1.`uid` AS `uid`,
  13.             COUNT((CASE WHEN t3.`name` = '工商银行' THEN 1 ELSE NULL END)) AS `__count2__`,
  14.             (CASE WHEN COUNT((CASE WHEN t3.`name` = '建设银行' THEN 1 ELSE NULL END)) > 0 THEN false ELSE true END) AS `__none3__`
  15.         FROM
  16.             `t_bank_card` t1
  17.         INNER JOIN
  18.             `t_bank` t3
  19.                 ON t3.`id` = t1.`bank_id`
  20.         GROUP BY
  21.             t1.`uid`
  22.     ) t2
  23.         ON t2.`uid` = t.`id`
  24. WHERE
  25.     IFNULL(t2.`__count2__`,0) >= 2
  26.     AND IFNULL(t2.`__none3__`,true) = true
复制代码
有没有一种眼前一亮的感觉,我敢说目前来讲并没有多少ORM实现了隐式Group,当然对于OLAP而言easy-query的提供的功能远不止此
partition by

筛选用户条件为喜欢工商银行的(第一张开户的银行卡是工商银行的)
  1. List<SysUser> list = easyEntityQuery.queryable(SysUser.class)
  2.         .where(user -> {
  3.             //用户的银行卡中第一个开户银行卡是工商银行的
  4.             user.bankCards().orderBy(x->x.openTime().asc()).firstElement().bank().name().eq("工商银行");
  5.         }).toList();
复制代码
天生的sql
  1. SELECT
  2.     t.`id`,
  3.     t.`name`,
  4.     t.`phone`,
  5.     t.`age`,
  6.     t.`create_time`
  7. FROM
  8.     `t_sys_user` t
  9. LEFT JOIN
  10.     (
  11.         SELECT
  12.             t2.`id` AS `id`,
  13.             t2.`uid` AS `uid`,
  14.             t2.`code` AS `code`,
  15.             t2.`type` AS `type`,
  16.             t2.`bank_id` AS `bank_id`,
  17.             t2.`open_time` AS `open_time`
  18.         FROM
  19.             (SELECT
  20.                 t1.`id`,
  21.                 t1.`uid`,
  22.                 t1.`code`,
  23.                 t1.`type`,
  24.                 t1.`bank_id`,
  25.                 t1.`open_time`,
  26.                 (ROW_NUMBER() OVER (PARTITION BY t1.`uid` ORDER BY t1.`open_time` ASC)) AS `__row__`
  27.             FROM
  28.                 `t_bank_card` t1) t2
  29.         WHERE
  30.             t2.`__row__` = 1
  31.         ) t4
  32.             ON t4.`uid` = t.`id`
  33.     INNER JOIN
  34.         `t_bank` t5
  35.             ON t5.`id` = t4.`bank_id`
  36.     WHERE
  37.         t5.`name` = '工商银行'
复制代码
是的你没看错就是这么简单如果你必要动态那么只必要用where(true/false,条件)或者更加直白的方式即可
筛选用户条件为喜欢工商银行的(第一张开户的银行卡是工商银行的)和用户姓名叫小明的,其中喜欢工商银行这个条件可以是动态的
  1.         boolean likeICBC = false;
  2.         String name = "小明";
  3.         List<SysUser> list = easyEntityQuery.queryable(SysUser.class)
  4.                 .where(user -> {
  5.                     if(EasyStringUtil.isNotBlank(name)){
  6.                         user.name().like(name);
  7.                     }
  8.                     if(likeICBC){
  9.                         //用户的银行卡中第一个开户银行卡是工商银行的
  10.                         user.bankCards().orderBy(x -> x.openTime().asc()).firstElement().bank().name().eq("工商银行");
  11.                     }
  12.                 }).toList();
复制代码
天生的sql
  1.     SELECT
  2.         `id`,
  3.         `name`,
  4.         `phone`,
  5.         `age`,
  6.         `create_time`
  7.     FROM
  8.         `t_sys_user`
  9.     WHERE
  10.         `name` LIKE '%小明%'
复制代码
是的你没看错动态partition就是这么简单就是这么容易阅读和维护
select子查询

写了这么多where子查询的隐式Group那么再来写点select子查询相关的吧
查询用户返回用户姓名和用户开户的前两张银行卡类型逗号分割
  1. List<Draft2<String, String>> list = easyEntityQuery.queryable(SysUser.class)
  2.                 .where(user -> {
  3.                     user.name().like("小明");
  4.                 }).select(user -> Select.DRAFT.of(
  5.                         user.name(),
  6.                         //用户的银行卡中前两个开户银行卡类型
  7.                         user.bankCards().orderBy(x -> x.openTime().asc()).elements(0, 1).joining(x -> x.type(),",")
  8.                 )).toList();
复制代码
天生的sql
  1. SELECT
  2.     t.`name` AS `value1`,
  3.     (SELECT
  4.         GROUP_CONCAT(t1.`type` SEPARATOR ',')
  5.     FROM
  6.         `t_bank_card` t1
  7.     WHERE
  8.         t1.`uid` = t.`id`
  9.     ORDER BY
  10.         t1.`open_time` ASC LIMIT 2) AS `value2`
  11. FROM
  12.     `t_sys_user` t
  13. WHERE
  14.     t.`name` LIKE '%小明%'
复制代码
超级无敌究极子查询转group
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

农民

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表