需求:做管理后台报表时,给定一个日期范围,查出库中这个日期范围内的每一天数据,库中不存在的日期对应数据要补0
方法一:
获取每一天的sql语句:
SELECT
date_add( date_sub(#{startDate},interval 1 day), INTERVAL ( cast( help_topic_id AS signed INTEGER ) + 1 ) day ) days
FROM mysql.help_topic
WHERE
help_topic_id < DATEDIFF(#{endDate},date_sub(#{startDate},interval 1 day))
ORDER BY
help_topic_id
对于表中不存在的日期,我们用右外连接即可解决!!!当然左连接也可以,看你的主表是谁。
注:我们不一定非要用表 mysql.help_topic,这里只是用到了它的id,我们可自行建一个有id列的表,但是自己建的表id不能自增,且id必须从0开始(为什么id不能自增:id自增就不能从0开始,只能从1开始;为什么id必须从0开始:如果id没有从0开始,那么所得到的日期列表就是#{startDate}+1 ~ #{endDate})
上面这种sql呢有两个明显的缺陷:首先你可能没有权限访问 mysql 库里的表 help_topic,那你就的自行建张表;其次是 当你的时间段范围非常大,而mysql.help_topic/自己建的表 表里没有那么多的id个数,就不能满足你的需求,那你可能就要写存储过程、视图、还有很多的方法。
案例:
因为我没权限所以用不到存储过程,而且时间段范围也就一个月,所以上面的sql也就满足了
这是我库中的数据:(表中不存在日期21、22、24、25)
右外连接查询解决问题:
方法二:
select adddate((select min(date) from t_pivotal_behavior_report),t2.i*10+t1.i*10 + t0.i) dateStr from
(select 0 i union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t0,
(select 0 i union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t1,
(select 0 i union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t2
;
再用左连接或有链接即可查出不存在的日期
进而可衍生出:
这段日期内的某几天
select * from (
select subdate("2022-03-04", t1.i*10+t0.i) dateStr from
(select 0 i union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t0,
(select 0 i union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t1
) a where a.dateStr>=DATE_SUB("2022-03-04", INTERVAL 14 DAY) and dateStr<="2022-03-04"
;
方法三:
思路:得到你要查询日期范围,然后使用Java的方法罗列出日期段内的每一天,然后用Java把它拼接成SQL语句,然后使用mybatis的 ${} 接收,然后左连接或右连接查询出不存在的日期
示例:我要查库中最小日到今天这段日期内不存在的日期:
1.查出库中最小日期,
2.罗列最小日期到今天的每一天:
/**
* 获取每一天日期
* @param dBegin
* @param dEnd
* @return
*/
public static List findDates(Date dBegin, Date dEnd) {
Date tomorrow = DateUtil.getMinDateOfDay(DateUtil.getNextDate(new Date()));
List lDate = new ArrayList();
lDate.add(dBegin);
Calendar calBegin = Calendar.getInstance();
// 使用给定的 Date 设置此 Calendar 的时间
calBegin.setTime(dBegin);
Calendar calEnd = Calendar.getInstance();
// 使用给定的 Date 设置此 Calendar 的时间
calEnd.setTime(dEnd);
// 测试此日期是否在指定日期之后
while (dEnd.after(calBegin.getTime())) {
// 根据日历的规则,为给定的日历字段添加或减去指定的时间量
calBegin.add(Calendar.DAY_OF_MONTH, 1);
if(calBegin.getTime().getTime() < tomorrow.getTime())
lDate.add(calBegin.getTime());
}
return lDate;
}
3.使用select和union将日期集合拼接成SQL语句
//将date集合拼接成SQL语句
private static String getDateListSqlByMinDateAndToday(String minDate) {
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
Date parse = null;
//日期类型转化
try {
parse = df.parse(minDate);
} catch (ParseException e) {
e.printStackTrace();
}
//得到每一天日期
List dates = DateUtil.findDates(parse, new Date());
if (dates == null || dates.size() ==0) {
return null;
}
//SQL凭借
String sql="";
for (Date date : dates) {
//把每个日期用引号引起来,Java需要字符转义
sql+="select \""+df.format(date)+"\" as `date` union ";
}
//去掉空格
sql=sql.trim();
//去掉最后一个union
sql=sql.substring(0,sql.length()-"union".length());
return sql;
}
例如得到拼接的SQL:
select "2021-12-01" as `date` union select "2021-12-02" as `date` union select "2021-12-03" as `date` union select "2021-12-04" as `date` union select "2021-12-05" as `date` union select "2021-12-06" as `date` union select "2021-12-07" as `date` union select "2021-12-08" as `date` union select "2021-12-09" as `date` union select "2021-12-10" as `date`
4.将得到的SQL传给mybatis
5.mybatis接收:
select t1.`date` from ( ${dateListSql} ) t1
left join (select `date` from t_pivotal_behavior_report where `type`=#{type} and app_type=#{appType} GROUP BY `date`) t2
on t1.`date`=t2.`date`
where t2.`date` is null