【mybatis】mybatis动态order by 的问题, 注意 只需要把#{} 改成 ${} 即可

没有检索到摘要

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

先说解决方案:

注意  只需要把#{} 改成 ${}  即可

 

 

再看 使用过程:

Mapper.java

List<IntegralGoods> findInUid(@Param("uidList") List<String> uidList,@Param("order") String order,@Param("orderType") String orderType);

首先,是这样的mybatis拼接的sql语句

<select id="findInUid" parameterType="com.pisen.cloud.luna.ms.jifen.base.domain.IntegralGoods" resultMap="baseResBean">

        select
        a.id as 'id',
        a.uid as 'uid',
        a.create_date as 'createDate',
        a.update_date as 'updateDate',
        a.update_id as 'updateId',
        a.create_id as 'createId',
        a.brand_uid as 'brandUid',
        a.tid as 'tid',
        a.stock as 'stock',
        a.name as 'name',
        a.goods_code as 'goodsCode',
        a.market_value as 'marketValue',
        a.specification as 'specification',
        a.remark as 'remark',
        a.type as 'type',
        a.integral as 'integral',
        a.description as 'description',
        a.sale_num as 'saleNum',
        a.limit_num as 'limitNum',
        a.shelf_flag as 'shelfFlag',
        a.home_show_flag as 'homeShowFlag',
        sl.shelf_date as 'shelfDate',
        sl.obtained_date as 'obtainedDate',
        b.id b_id,
        b.src b_src,
        b.type b_type,
        b.sort  b_sort
        from
            integral_goods a
        left join
            integral_goods_img b
        on
            a.uid = b.integral_goods_id
        left join
            shelf_log sl
        on
            a.uid = sl.integral_goods_uid
        <where>
                    a.uid
            IN
            <foreach collection="uidList" item="item" index="index" open="(" separator="," close=")">
                #{item}
            </foreach>
            <if test="order != null and orderType">
                order by #{order} #{orderType}
            </if>
        </where>

    </select>

 

执行的sql语句是这样:

SELECT
    a.id AS 'id',
    a.uid AS 'uid',
    a.create_date AS 'createDate',
    a.update_date AS 'updateDate',
    a.update_id AS 'updateId',
    a.create_id AS 'createId',
    a.brand_uid AS 'brandUid',
    a.tid AS 'tid',
    a.stock AS 'stock',
    a. NAME AS 'name',
    a.goods_code AS 'goodsCode',
    a.market_value AS 'marketValue',
    a.specification AS 'specification',
    a.remark AS 'remark',
    a.type AS 'type',
    a.integral AS 'integral',
    a.description AS 'description',
    a.sale_num AS 'saleNum',
    a.limit_num AS 'limitNum',
    a.shelf_flag AS 'shelfFlag',
    a.home_show_flag AS 'homeShowFlag',
    sl.shelf_date AS 'shelfDate',
    sl.obtained_date AS 'obtainedDate',
    b.id b_id,
    b.src b_src,
    b.type b_type,
    b.sort b_sort
FROM
    integral_goods a
LEFT JOIN integral_goods_img b ON a.uid = b.integral_goods_id
LEFT JOIN shelf_log sl ON a.uid = sl.integral_goods_uid
WHERE
    a.uid IN (
        '09f163c30c504d7fb571186db9c10ff3',
        'd8a9184443524e369be59417e9edd409',
        '778b5c0fbc4141b4bd8de3b7756a1d9d',
        '58d53fe0126d47c8bf382647244e2b1d'
    )
ORDER BY
    'integral' 'asc'

 

这样执行sql  是没有效果的 !!!

正确的

<select id="findInUid" parameterType="com.pisen.cloud.luna.ms.jifen.base.domain.IntegralGoods" resultMap="baseResBean">

        select
        a.id as 'id',
        a.uid as 'uid',
        a.create_date as 'createDate',
        a.update_date as 'updateDate',
        a.update_id as 'updateId',
        a.create_id as 'createId',
        a.brand_uid as 'brandUid',
        a.tid as 'tid',
        a.stock as 'stock',
        a.name as 'name',
        a.goods_code as 'goodsCode',
        a.market_value as 'marketValue',
        a.specification as 'specification',
        a.remark as 'remark',
        a.type as 'type',
        a.integral as 'integral',
        a.description as 'description',
        a.sale_num as 'saleNum',
        a.limit_num as 'limitNum',
        a.shelf_flag as 'shelfFlag',
        a.home_show_flag as 'homeShowFlag',
        sl.shelf_date as 'shelfDate',
        sl.obtained_date as 'obtainedDate',
        b.id b_id,
        b.src b_src,
        b.type b_type,
        b.sort  b_sort
        from
            integral_goods a
        left join
            integral_goods_img b
        on
            a.uid = b.integral_goods_id
        left join
            shelf_log sl
        on
            a.uid = sl.integral_goods_uid
        <where>
                    a.uid
            IN
            <foreach collection="uidList" item="item" index="index" open="(" separator="," close=")">
                #{item}
            </foreach>
            <if test="order != null and orderType">
                order by ${order} ${orderType}
            </if>
        </where>

    </select>

 

执行的sql语句是这样的:

SELECT
    a.id AS 'id',
    a.uid AS 'uid',
    a.create_date AS 'createDate',
    a.update_date AS 'updateDate',
    a.update_id AS 'updateId',
    a.create_id AS 'createId',
    a.brand_uid AS 'brandUid',
    a.tid AS 'tid',
    a.stock AS 'stock',
    a. NAME AS 'name',
    a.goods_code AS 'goodsCode',
    a.market_value AS 'marketValue',
    a.specification AS 'specification',
    a.remark AS 'remark',
    a.type AS 'type',
    a.integral AS 'integral',
    a.description AS 'description',
    a.sale_num AS 'saleNum',
    a.limit_num AS 'limitNum',
    a.shelf_flag AS 'shelfFlag',
    a.home_show_flag AS 'homeShowFlag',
    sl.shelf_date AS 'shelfDate',
    sl.obtained_date AS 'obtainedDate',
    b.id b_id,
    b.src b_src,
    b.type b_type,
    b.sort b_sort
FROM
    integral_goods a
LEFT JOIN integral_goods_img b ON a.uid = b.integral_goods_id
LEFT JOIN shelf_log sl ON a.uid = sl.integral_goods_uid
WHERE
    a.uid IN (
        '09f163c30c504d7fb571186db9c10ff3',
        'd8a9184443524e369be59417e9edd409',
        '778b5c0fbc4141b4bd8de3b7756a1d9d',
        '58d53fe0126d47c8bf382647244e2b1d'
    )
ORDER BY
    integral ASC

 

转载于:https://www.cnblogs.com/sxdcgaq8080/p/9848016.html

<think>我们需求:按当前月往前推一年,每三个月统计一次(即按季度统计),展示已完成和未完成的数量。注意:不是自然年,而是从当前月往前推12个月,然后按每三个月一个季度划分,共四个季度。步骤:1.确定时间范围:当前时间减去12个月,作为起始时间,当前时间作为结束时间。2.将这一年的时间分成四个季度(每三个月为一个季度),注意这里可能不是自然季度,而是从起始月份开始的连续12个月,按每3个月划分。3.统计每个季度内,已完成和未完成的数量。实现思路:-由于不是自然季度,我们需要动态计算四个季度的起始和结束时间。-每个季度包含3个月,因此四个季度分别是:季度1:起始时间->起始时间+3个月季度2:起始时间+3个月->起始时间+6个月季度3:起始时间+6个月->起始时间+9个月季度4:起始时间+9个月->结束时间(当前时间)注意:结束时间可能不是整三个月,但实际需求是一年,所以刚好四个季度。数据库查询:使用MyBatis,我们可以编写动态SQL,通过时间范围过滤,然后按季度分组。但是,由于不是自然季度,我们不能直接使用QUARTER函数(因为QUARTER函数是按自然年季度1-4月,5-8月?不对,自然季度是1-3,4-6,7-9,10-12)。所以我们需要自己定义季度。因此,我们可以这样划分:季度1:起始月份~起始月份+2个月季度2:起始月份+3~起始月份+5个月季度3:起始月份+6~起始月份+8个月季度4:起始月份+9~起始月份+11个月具体步骤:1.计算起始时间:当前时间减去12个月(注意:如果当前是3月15日,那么起始时间就是去年3月15日?但是需求是当前月往前一年,所以应该是从去年当前月到今年当前月,共12个月)2.在Java中计算四个季度的起止时间点(注意月份加减的边界,使用Java8的LocalDate或Calendar)但是,在SQL中如何按我们自定义的季度分组呢?方案:在SQL查询中,我们可以使用CASEWHEN将每个订单记录根据其创建时间分配到四个季度中。例如:起始时间:startDate(去年当前月1号)结束时间:endDate(今年当前月最后一天?或者当前时间?)定义季度:季度1:startDate<=创建时间<startDate+3months季度2:startDate+3months<=创建时间<startDate+6months季度3:startDate+6months<=创建时间<startDate+9months季度4:startDate+9months<=创建时间<=endDate这样,我们就可以在SQL中通过条件判断将记录分到四个季度。然后按这个自定义的季度分组,统计每个季度的已完成和未完成数量。但是,这样写SQL可能会比较长,而且需要传入5个参数(startDate,startDate+3m,startDate+6m,startDate+9m,endDate)?另一种方案:在Java中计算四个季度的起止时间,然后分别查询四个季度的数据(四次查询,不推荐)。推荐方案:在SQL中通过传入的起始时间(startDate)和四个季度的偏移量,使用CASEWHEN进行分组。具体SQL思路:我们可以在查询中使用:CASEWHENcreate_time>=:startDateANDcreate_time<DATE_ADD(:startDate,INTERVAL3MONTH)THEN1WHENcreate_time>=DATE_ADD(:startDate,INTERVAL3MONTH)ANDcreate_time<DATE_ADD(:startDate,INTERVAL6MONTH)THEN2WHENcreate_time>=DATE_ADD(:startDate,INTERVAL6MONTH)ANDcreate_time<DATE_ADD(:startDate,INTERVAL9MONTH)THEN3WHENcreate_time>=DATE_ADD(:startDate,INTERVAL9MONTH)ANDcreate_time<=:endDateTHEN4ENDASquarter然后按这个quarter分组。注意:这里我们假设startDate和endDate已经计算好,并且endDate就是startDate加上12个月。但是,这样写SQL在MySQL中可能会因为日期函数而影响性能,而且如果数据量大,可以考虑建立索引。在Java中的步骤:1.计算时间范围:当前时间:LocalDate.now()起始时间:now.minusMonths(12)//往前12个月结束时间:now但是注意:起始时间我们一般取该月的第一天,结束时间取当前月的最后一天?还是精确到当前日期?需求是当前月往前一年,那么应该是从起始月份到当前月份,共12个月,所以起始时间应该是起始月份的第一天,结束时间是当前月份的最后一天?或者结束时间就是当前时间(包含到今天)?具体看需求。假设我们要求是整月,那么:起始时间:now.minusMonths(12).withDayOfMonth(1)//去年当前月的第一天结束时间:now.withDayOfMonth(now.lengthOfMonth())//当前月的最后一天但是,需求可能包含到今天,那么结束时间就是当前日期。这里我们按包含整月处理,即统计去年当前月到今年当前月(包含整个月)的数据。2.将计算好的时间参数传递给MyBatis的SQL查询。3.定义返回的DTO:publicclassQuarterlyStats{privateList<String>quarterLabels;//四个季度的标签,如"2023Q1","2023Q2"或者自定义显示privateList<Integer>completedCounts;//每个季度的已完成数量privateList<Integer>uncompletedCounts;//每个季度的未完成数量}4.在Service层计算时间参数,调用Mapper查询。5.Mapper中编写动态SQL,使用CASEWHEN进行季度分组。注意:如果某个季度没有数据,那么该季度应该显示0。上面的CASEWHEN可能会将没有数据的季度忽略,所以我们需要在查询后补全四个季度。我们可以在SQL中使用生成四个季度的方式,然后左连接?但是这样比较复杂。我们可以在Java中初始化四个季度的数据(0),然后用查询结果覆盖。具体实现:代码步骤:1.定义DTO2.Service层逻辑3.Mapper接口和XML详细代码:步骤1:定义返回DTO```java@DatapublicclassQuarterlyStatsDTO{//季度标签,例如:["2023年第一季度","2023年第二季度","2023年第三季度","2023年第四季度"]privateList<String>quarterLabels=newArrayList<>();//已完成数量privateList<Integer>completed=newArrayList<>();//未完成数量privateList<Integer>uncompleted=newArrayList<>();}```步骤2:Service层```java@Service@RequiredArgsConstructorpublicclassStatsService{privatefinalOrderStatsMapperorderStatsMapper;publicQuarterlyStatsDTOgetQuarterlyStats(){//1.计算时间范围LocalDatenow=LocalDate.now();LocalDatestartDate=now.minusMonths(12).withDayOfMonth(1);//起始日期:12个月前的第一天LocalDateendDate=now.withDayOfMonth(now.lengthOfMonth());//结束日期:当前月的最后一天//2.计算四个季度的标签(用于返回给前端展示)//季度1:startDate~startDate.plusMonths(3).minusDays(1)//注意:我们这里用起始月份+3个月得到下一个季度的开始,所以标签可以表示为:List<String>labels=newArrayList<>();LocalDatetemp=startDate;DateTimeFormatterformatter=DateTimeFormatter.ofPattern("yyyy年MM月");for(inti=0;i<4;i++){StringstartStr=temp.format(formatter);temp=temp.plusMonths(3);StringendStr=temp.minusDays(1).format(formatter);//季度结束是下一个季度的开始减一天labels.add(startStr+"~"+endStr);//注意:这里temp已经是下一个季度的第一天了,所以下一个季度从temp开始}//3.调用Mapper查询数据库List<QuarterlyData>quarterlyDataList=orderStatsMapper.getQuarterlyStats(startDate,endDate);//4.初始化四个季度的数据为0QuarterlyStatsDTOresult=newQuarterlyStatsDTO();result.setQuarterLabels(labels);//初始化四个季度的数据for(inti=0;i<4;i++){result.getCompleted().add(0);result.getUncompleted().add(0);}//5.将查询到的数据按季度设置到对应的位置(注意:查询结果中的quarter是1,2,3,4,对应索引0,1,2,3)for(QuarterlyDatadata:quarterlyDataList){//季度索引=data.getQuarter()-1intindex=data.getQuarter()-1;if(index>=0&&index<4){result.getCompleted().set(index,data.getCompleted());result.getUncompleted().set(index,data.getUncompleted());}}returnresult;}}```步骤3:定义QuarterlyData(用于MyBatis返回的每行数据)```java@DatapublicclassQuarterlyData{privateIntegerquarter;//季度(1,2,3,4)privateIntegercompleted;//已完成数量privateIntegeruncompleted;//未完成数量}```步骤4:Mapper接口```java@MapperpublicinterfaceOrderStatsMapper{List<QuarterlyData>getQuarterlyStats(@Param("startDate")LocalDatestartDate,@Param("endDate")LocalDateendDate);}```步骤5:MapperXML(使用MyBatis动态SQL)```xml<selectid="getQuarterlyStats"resultType="com.example.QuarterlyData">SELECTCASEWHENcreate_time>=#{startDate}ANDcreate_time<DATE_ADD(#{startDate},INTERVAL3MONTH)THEN1WHENcreate_time>=DATE_ADD(#{startDate},INTERVAL3MONTH)ANDcreate_time<DATE_ADD(#{startDate},INTERVAL6MONTH)THEN2WHENcreate_time>=DATE_ADD(#{startDate},INTERVAL6MONTH)ANDcreate_time<DATE_ADD(#{startDate},INTERVAL9MONTH)THEN3WHENcreate_time>=DATE_ADD(#{startDate},INTERVAL9MONTH)ANDcreate_time<=#{endDate}THEN4ENDASquarter,SUM(CASEWHENstatus=1THEN1ELSE0END)AScompleted,SUM(CASEWHENstatus=0THEN1ELSE0END)ASuncompletedFROMordersWHEREcreate_timeBETWEEN#{startDate}AND#{endDate}GROUPBYquarterORDERBYquarter</select>```注意:这里使用了MySQL的日期函数,其他数据库可能需要调整。步骤6:Controller```java@RestController@RequestMapping("/api/stats")@RequiredArgsConstructorpublicclassStatsController{privatefinalStatsServicestatsService;@GetMapping("/quarterly")publicQuarterlyStatsDTOgetQuarterlyStats(){returnstatsService.getQuarterlyStats();}}```返回的数据结构示例:```json{"quarterLabels":["2023年01月~2023年03月","2023年04月~2023年06月","2023年07月~2023年09月","2023年10月~2023年12月"],"completed":[120,95,150,180],"uncompleted":[30,25,40,35]}```前端就可以用这个数据来画柱状图了。优化点:1.数据库索引:在create_time和status上建立索引。2.缓存:如果数据更新不频繁,可以加入缓存,避免每次查询数据库注意:这里的时间范围计算,我们使用了整月(起始月份的第一天到结束月份的最后一天),如果需求是精确到天(即从当前日期的12个月前到当前日期),那么调整计算方式:LocalDatestartDate=now.minusMonths(12);//12个月前的今天LocalDateendDate=now;但这样划分季度时,每个季度可能不是完整的三个月(因为每个月天数不同),但划分逻辑不变,还是按3个月划分。另外,在SQL中,我们使用`create_timeBETWEEN#{startDate}AND#{endDate}`,注意BETWEEN是包含边界的,所以结束日期如果是当天,那么当天的数据也会被包含。根据需求调整。如果需求是当前月往前一年(12个月),那么起始日期应该是去年当前月的第一天,结束日期是当前月的最后一天,这样正好12个月,四个季度每个季度3个月。如果当前月是3月,那么:起始:去年3月1日结束:今年3月31日划分:第一季度:去年3月1日~去年5月31日(3、4、5月)第二季度:去年6月1日~去年8月31日第三季度:去年9月1日~去年11月30日第四季度:去年12月1日~今年3月31日(注意这里跨越了年份,有4个月?)等等,这样划分第四季度变成了4个月?不对,我们要求每三个月一个季度,所以应该是:第一季度:去年3月1日~去年5月31日(3个月)第二季度:去年6月1日~去年8月31日(3个月)第三季度:去年9月1日~去年11月30日(3个月)第四季度:去年12月1日~今年2月28日(或29日)(3个月)但是结束日期是今年3月31日,所以这样划分第四季度会包含到今年3月?实际上,我们划分四个季度应该是:第1季度:startDate(去年3月1日)~去年5月31日第2季度:去年6月1日~去年8月31日第3季度:去年9月1日~去年11月30日第4季度:去年12月1日~今年2月28/29日但是结束日期是今年3月31日,所以从去年12月1日到今年3月31日有4个月,这不符合每季度3个月。因此,我们需要调整:我们的结束时间应该是startDate+12个月,即今年3月1日的前一天(去年3月1日+12个月=今年3月1日,所以结束日期应该是今年2月的最后一天?)。这样刚好四个季度,每个季度3个月。所以,结束时间应该是:startDate.plusMonths(12).minusDays(1)//即去年3月1日加12个月是今年3月1日,减1天就是今年2月的最后一天。修改Service中的endDate计算:LocalDateendDate=startDate.plusMonths(12).minusDays(1);这样,时间范围就是:去年3月1日~今年2月28/29日(12个月,4个季度,每个季度3个月)。但是,需求是“当前月往前一年的数据”,当前月是3月,那么往前一年是去年3月到今年2月?这样理解是对的,因为今年3月才刚开始,可能不完整,所以通常统计到上个月。但是需求要求的是当前月往前一年,包含当前月吗?如果包含,那么结束时间应该是当前月的最后一天,那么时间范围就是13个月(去年3月1日到今年3月31日)?所以需要明确。根据需求调整:如果需求是包含当前月(即使不完整),那么结束日期就是当前月的最后一天,那么第四季度就会多出一个月(从去年12月到今年3月)。这样就不是严格的每三个月了。所以,我们需要和产品确认:是按自然季度还是按固定的三个月?如果是固定的三个月,那么应该从开始日期算起,每三个月一个区间,最后一个区间可能不足三个月(如果总月份不是3的倍数),但一年12个月,刚好4个季度。因此,我们按照严格的每三个月划分,结束日期应该是开始日期+12个月-1天(即整好12个月)。这样每个季度都是3个月。所以,修改Service中计算时间范围:LocalDatestartDate=now.minusMonths(12).withDayOfMonth(1);//12个月前的第一天LocalDateendDate=startDate.plusMonths(12).minusDays(1);//12个月后的前一天,即整12个月这样,无论当前月是哪个月,我们都统计整12个月的数据,并按每3个月划分。但是,这样划分后,我们生成的季度标签会有跨年,但我们的标签已经按实际年月生成,所以没有问题。例如:当前是2024年3月15日,那么:startDate=2023-03-01endDate=2024-02-29(2024是闰年)四个季度:季度1:2023-03-01~2023-05-31->标签:2023年03月~2023年05月季度2:2023-06-01~2023-08-31季度3:2023-09-01~2023-11-30季度4:2023-12-01~2024-02-29这样符合需求。因此,我们按照这个时间范围计算。最后,注意在生成季度标签时,我们使用:for(inti=0;i<4;i++){StringstartStr=temp.format(formatter);temp=temp.plusMonths(3);//加3个月,变成下一个季度的第一天//当前季度的结束日期应该是temp的前一天StringendStr=temp.minusDays(1).format(formatter);labels.add(startStr+"~"+endStr);}这样每个季度都是3个月,标签正确。注意:这里temp在循环结束后变成了startDate+12个月,也就是正好是结束日期的后一天(因为endDate=startDate+12个月-1天),所以标签生成正确。总结:按照上述实现即可。</think>我将为您提供一个完整的基于MyBatis和Spring Boot的实现方案,解决按季度统计一年内已完成/未完成数据的需求。 ### 解决方案思路 1. **时间范围计算**:获取当前月往前推12个月的时间范围 2. **季度划分**:将12个月数据划分为4个季度(每3个月) 3. **数据查询**:使用MyBatis进行分组统计 4. **结果封装**:返回结构化数据供前端ECharts使用 ### 实现步骤 #### 1. 定义返回DTO ```java @Data public class QuarterlyStatsDTO { private List<String> quarters; // 季度标签 ["Q1-2023", "Q2-2023", ...] private List<Integer> completed; // 已完成数量 private List<Integer> uncompleted; // 未完成数量 } ``` #### 2. Service层实现 ```java @Service @RequiredArgsConstructor public class StatsService { private final OrderMapper orderMapper; public QuarterlyStatsDTO getQuarterlyStats() { // 计算时间范围(当前月往前12个月) LocalDate endDate = LocalDate.now(); LocalDate startDate = endDate.minusMonths(11).withDayOfMonth(1); // 获取季度统计数据 List<QuarterData> quarterDataList = orderMapper.selectQuarterlyStats( startDate.atStartOfDay(), endDate.atTime(LocalTime.MAX) ); // 生成季度标签 List<String> quarterLabels = generateQuarterLabels(startDate); // 初始化结果集(确保4个季度都有数据) int[] completedArr = new int[4]; int[] uncompletedArr = new int[4]; // 填充查询结果 for (QuarterData data : quarterDataList) { int quarterIndex = data.getQuarter() - 1; if (quarterIndex >= 0 && quarterIndex < 4) { completedArr[quarterIndex] = data.getCompleted(); uncompletedArr[quarterIndex] = data.getUncompleted(); } } // 转换为DTO return new QuarterlyStatsDTO( quarterLabels, Arrays.stream(completedArr).boxed().collect(Collectors.toList()), Arrays.stream(uncompletedArr).boxed().collect(Collectors.toList()) ); } private List<String> generateQuarterLabels(LocalDate startDate) { List<String> labels = new ArrayList<>(); DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MMM"); for (int i = 0; i < 4; i++) { LocalDate quarterStart = startDate.plusMonths(i * 3); LocalDate quarterEnd = quarterStart.plusMonths(2); String label = "Q" + (i+1) + " " + quarterStart.format(formatter) + "-" + quarterEnd.format(formatter); labels.add(label); } return labels; } } ``` #### 3. MyBatis Mapper接口 ```java @Mapper public interface OrderMapper { @Select(""" SELECT FLOOR((MONTH(create_time) - MONTH(#{startDate}) + 12) / 3) + 1 AS quarter, SUM(CASE WHEN status = 1 THEN 1 ELSE 0 END) AS completed, SUM(CASE WHEN status = 0 THEN 1 ELSE 0 END) AS uncompleted FROM orders WHERE create_time BETWEEN #{startDate} AND #{endDate} GROUP BY quarter ORDER BY quarter """) @Results({ @Result(property = "quarter", column = "quarter"), @Result(property = "completed", column = "completed"), @Result(property = "uncompleted", column = "uncompleted") }) List<QuarterData> selectQuarterlyStats( @Param("startDate") Date startDate, @Param("endDate") Date endDate ); @Data class QuarterData { private int quarter; private int completed; private int uncompleted; } } ``` #### 4. Controller层 ```java @RestController @RequestMapping("/api/stats") @RequiredArgsConstructor public class StatsController { private final StatsService statsService; @GetMapping("/quarterly") public QuarterlyStatsDTO getQuarterlyStats() { return statsService.getQuarterlyStats(); } } ``` #### 5. 数据库索引优化 ```sql -- 创建索引提升查询性能 CREATE INDEX idx_order_time_status ON orders(create_time, status); ``` ### 返回数据结构示例 ```json { "quarters": [ "Q1 Jan-Mar", "Q2 Apr-Jun", "Q3 Jul-Sep", "Q4 Oct-Dec" ], "completed": [120, 95, 150, 180], "uncompleted": [30, 25, 40, 35] } ``` ### 前端ECharts使用示例 ```javascript axios.get(&#39;/api/stats/quarterly&#39;).then(response => { const data = response.data; const chart = echarts.init(document.getElementById(&#39;chart&#39;)); const option = { tooltip: { trigger: &#39;axis&#39; }, legend: { data: [&#39;已完成&#39;, &#39;未完成&#39;] }, xAxis: { type: &#39;category&#39;, data: data.quarters }, yAxis: { type: &#39;value&#39; }, series: [ { name: &#39;已完成&#39;, type: &#39;bar&#39;, data: data.completed }, { name: &#39;未完成&#39;, type: &#39;bar&#39;, data: data.uncompleted } ] }; chart.setOption(option); }); ``` ### 性能优化建议 1. **缓存机制**:使用Spring Cache缓存结果 ```java @Cacheable(value = "quarterlyStats", key = "#root.methodName") public QuarterlyStatsDTO getQuarterlyStats() { ... } ``` 2. **定时刷新**:结合引用[3]实现基于数据库的定时刷新 ```java @Scheduled(cron = "0 0 0 * * ?") // 每天凌晨执行 @CacheEvict(value = "quarterlyStats", allEntries = true) public void refreshStatsCache() { // 触发缓存刷新 } ``` 3. **分页统计**:对海量数据使用分批次统计 ```sql <select id="selectQuarterlyStats"> SELECT ... WHERE create_time BETWEEN #{startDate} AND #{endDate} AND id > #{lastId} ORDER BY id LIMIT 10000 </select> ``` ### 注意事项 1. **时区处理**:确保数据库与应用服务器时区一致 2. **空数据处理**:Java层初始化确保4个季度数据完整 3. **跨年处理**:FLOOR()函数确保正确计算季度编号 4. **索引优化**:在create_time和status字段创建复合索引[^1]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值