SQL聚合统计(count/max/min等)并使用springboot jpa原生查询

  1、  SQL聚合统计

 我们经常遇到按照分组聚合取出数据的情况,比如我们有一个人员表的数据如下:

我们希望按照地址(addr)分组统计出beijing/shanghai/shenzhen三个城市每个城市的总人数、年龄小于18岁的未成年人、大于18岁的成年人这些信息,该怎么办呢?

此时可以使用SQL中的聚合查询group by 然后结合统计函数即可实现上述需求,我们直观的想法是使用如下的SQL语句进行统计:

select  addr, count(*) total, count(age < 18) childNum, count(age >= 18) adultNum  
from tb_person
where 1 = 1
group by addr;

result:
addr    |total|childnum|adultnum|
--------|-----|--------|--------|
beijing |    3|       3|       3|
shanghai|    5|       5|       5|
shenzhen|    2|       2|       2|

注: 这里的where 1 =1 仅仅是占个位,不加也行,在实际中可能需要其他额外的条件,直接附在where 后面就行了;

但是发现结果并不是我们想要的,北京市一共就3人,怎么可能未成年人数和成年人人数都有3人,那加起来不就是6人了吗?

实际上:

条件count(age < 18 ) 应改为 count(age < 18 or null)。 这里必须要加 "or null" , why?如果不加,当age不满足小于18 ,里面是count(false),count(false)与count(*)是等价的也会统计出所有的数目。

这是因为count('任意内容')都会统计出所有记录数,只有count(null)才不计数,当age 大于18时(结果为false) or null 的结果是null, count(null)= 0 , 才能筛选过滤得到我们想要的结果。

我们更改count()统计条件重新查询:结果如下

select  addr, count(*) total, count(age < 18 or null ) childNum, count(age >= 18 or null) adultNum  
from tb_person
where 1 = 1
group by addr;

result:
addr    |total|childnum|adultnum|
--------|-----|--------|--------|
beijing |    3|       0|       3|
shanghai|    5|       3|       2|
shenzhen|    2|       1|       1|

各个城市都统计出了总人数、未成年人数、成年人数,参照原始数据,发现统计结果正确。

2、 其他聚合函数

除了使用count() 聚合函数,一般常用的聚合函数还有sum()求和函数、max()最大值函数、min()最小值函数、avg()平均值函数等,也可以配合group by 聚合使用。比如这里求每个城市最大年龄的值:

后面还可以加上升序排列(asc)或者倒序(desc)

-- 按照城市地址分别统计各个城市的最大年龄值,并按倒序排列
select addr, max(age) maxAge
from tb_person
where 1=1
group by addr
order by maxAge desc;

result:
addr    |maxage|
--------|------|
shenzhen|    44|
shanghai|    38|
beijing |    28|

发现得到了我们想要的结果。

3、springboot jpa原生SQL聚合统计

springboot jpa 提供和封装了众多的操作数据库的方法,但是对于我们上文提到的复杂查询,则需创建原生SQL查询才行,这里使用EntityManager来实现。

@Service
@Slf4j
public class PersonService {   
 @Autowired
    private EntityManager entityManager;
  /**
     * 原生SQL聚合查询统计
     * @return
     */
    public  List<PersonStatisVO> statisAddrPersonNum() {
        String sql = "select  addr, count(*) total, count(age < 18 or null) childNum, count(age >= 18 or null) adultNum  \n" +
                " from tb_person \n" +
                " where 1 = 1 \n" +
                " group by addr;";
        Query nativeQuery = entityManager.createNativeQuery(sql);
        nativeQuery.unwrap(NativeQueryImpl.class).setResultTransformer(new DefineResultTransformer<>(PersonStatisVO.class));
        List<PersonStatisVO> resultList = nativeQuery.getResultList();
        return resultList;
    }
}

result:
[
  {
    "addr": "beijing",
    "total": 3,
    "childnum": 0,
    "adultnum": 3
  },
  {
    "addr": "shanghai",
    "total": 5,
    "childnum": 3,
    "adultnum": 2
  },
  {
    "addr": "shenzhen",
    "total": 2,
    "childnum": 1,
    "adultnum": 1
  }
]

对于查询到的结果需要自定义一个转换类,把SQL查询结果映射成Java 实体类

/**
 * 自定义hinernate结果转换类
 */
public class DefineResultTransformer<T> implements ResultTransformer, Serializable {
    private static final long serialVersionUID = 775781526148172513L;

    private Class<T> resultClazz;
    private BeanUtilsBean beanUtilsBean;

    public DefineResultTransformer(Class<T>  resultClass){
        this.resultClazz = resultClass;
        this.beanUtilsBean = BeanUtilsBean.getInstance();
    }

    @Override
    public Object transformTuple(Object[] tuple, String[] aliases) {
        Object result = null;

        Field[] fields = resultClazz.getDeclaredFields();
        try {
            result = resultClazz.newInstance();
            for(int i =0 ;i<aliases.length; i++){
                for(Field field : fields){
//   1、忽略大小写映射,如SQL查询字段为 "username" 可直接映射为Java实体类中userName属性
//   2、去掉"_" 映射,如SQL查询出的字段为 "user_name" 也可直接映射为Java实体类中userName属性
                    if(field.getName().equalsIgnoreCase(aliases[i].replaceAll("_",""))){
                        beanUtilsBean.setProperty(result,field.getName(),tuple[i]);
                        break;
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    @Override
    public List transformList(List collection) {
        return collection;
    }

}

欢迎关注博主公众号,后台回复  207  可领取2021最新Java全栈视频教程

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
使用Spring Boot和JPA的情况下,可以按照以下步骤进行配置和使用: 1. 首先,在你的项目中添加Spring Boot和JPA的依赖。可以在项目的pom.xml文件中添加以下依赖项: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> ``` 2. 创建一个实体类,用于映射数据库表。在你的代码中,可以看到一个名为User的实体类\[1\]。这个实体类使用JPA的注解来定义表名、字段和关联关系等。 3. 创建一个DAO接口,用于操作数据库。在你的代码中,可以看到一个名为UserDao的接口\[2\],它继承了JpaRepository接口,并指定了实体类和主键类型。 4. 创建一个Service接口和实现类,用于处理业务逻辑。在你的代码中,可以看到一个名为IUserService的接口和一个名为UserService的实现类。在实现类中,可以注入UserDao,并在方法中调用UserDao的方法来操作数据库。 5. 创建一个Controller类,用于处理HTTP请求。在你的代码中,可以看到一个名为UserController的类\[3\],它使用了@RestController注解来标识这是一个控制器类,并使用@RequestMapping注解来指定请求路径。在方法中,可以注入IUserService,并调用其方法来处理请求。 6. 最后,你可以使用IDEA来运行你的应用程序,并通过访问http://localhost:8080/user/queryAll来测试你的接口。 总结起来,使用Spring Boot和JPA可以方便地进行数据库操作。你需要创建实体类、DAO接口、Service接口和实现类以及Controller类来完成整个流程。通过注解和依赖注入,你可以简化代码并提高开发效率。 #### 引用[.reference_title] - *1* *2* *3* [idea 搭建 springboot+jpa](https://blog.csdn.net/Baldprogrammer/article/details/103275398)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值