MySQL遇到的一些坑
坑
第一大坑
后端代码没问题,前端拉取代码的时候拉取的是master分支,而master分支还没有跟新到最新进度,导致某些菜单访问正常但是有几个菜单模块访问不了,浏览器进度条卡住
后端也要改,可以重新拉取或者merge,后端和前端要对应上
git pull http://117.73.11.3:8090/WindPowerPrediction-server/wpp-web.git zz_develop
git pull http://117.73.11.3:8090/WindPowerPrediction-server/wpp-server.git zz_serve_develop
建议:这里建议git命令拉取代码的时候,指定分支,否则默认master
写sql
为什么表名表字段没有提示
为什么字段对应了还要取别名才对
ONLY_FUll_GROUP_BY的意思是:对于GROUP BY聚合操作,如果在SELECT中的列,没有在GROUP BY中出现,那么这个SQL是不合法的,因为列不在GROUP BY语句中,也就是说查出来的列必须是GROUP BY之后的字段,或者这个字段出现在聚合函数里面
分组时只按time分组会报错,因为启用了 ONLY_FULL_GROUP_BY,count是聚合函数可以不用
<select id="predictedStatistics" resultType="com.wpp.platform.predictedData.domain.PredictedDataDto">
select p.predicted_power as predictedPower,p.time,sum(p.predicted_power) as count
from wpp_prediction_data p
where p.type = #{type} and p.device_id = #{deviceId}
group by p.time, p.predicted_power
order by p.time;
</select>
查询出来是一组null组成的List
或者是 数据库查询没问题,但是前端/代码中service层或者controller层打印出来有的参数是null, 这说明问题出在Mapper层,没有自定义映射—parameterType和resultMap
多次踩坑
原因是:首先,参数写错了…返回的结果List泛型是实体类,疏忽写成了实体类对应的DTO,(DTO是用来接收前端传过来的参数的)
然后Mapper.xml文件没有自定义映射结果集,而是使用的resultType,应该使用resultMap,参数类型也写上parameterType
月报年报统计
// 将日期格式化为年月日,去掉时间部分
DATE(`time`)
// 将日期格式化为想要的格式
DATE_FORMAT(`time`, '%Y-%m')
// 但是format之后是字符串,使用CAST转换一下
CAST(DATE_FORMAT(`time`, '%Y-%m') AS DATETIME) AS `time`,
月报统计时间查询不出来
大坑
错误使用:
这里需要对数据进行按月聚合统计,所以需要对时间进行处理将时间格式化为年-月,然后取别名为time,耽耽耽是原始表中的时间列也叫time,直接使用time进行group by会默认使用原始表的故而不会按月聚合,所以group by之后的时间使用DATE_FORMAT(time, '%Y-%m')
不要使用CAST转换:CAST(DATE_FORMAT(time, '%Y-%m') AS DATETIME)
有时候又报不能转字符串到Date…
同理,当下面查询被当作子查询时,直接写select time会出错,可以写表名.

处理后:

mybatis映射的时候,比如我查出来count在映射map 中添加一行映射count到实体类,但是这个count是Double型的,我在实体类添加了一个Integer整形的字段,那么它不会报错也不会将count映射出来,因为方法参数不对,通过setter方法
数据库时间能查出来2024-01精确到月份,但是mybatis查出time为null,通过在实体类添加一个String类型字段timeMonth,在自定义映射中将timeMonth进行映射,最后在业务层处理将其赋值给time属性
SQL
count(*)
select count() from table括号内可以是也可以是列名,当然不是列名不行哦
COUNT(*) 统计数据行数,COUNT(column) 统计column非NULL的行数.
limit 的两种格式:
limit 2, 5 第一个参数表示从第2条数据开始,第二个参数表示往后截取5条数据
limit 6 offset 3 很明显第二个参数是表示偏移量,从第3开始截取6个数据
limit 3 截取3个数据,偏移量省略默认为0
当我在controller层封装一个对象参数,但是我有检查字段是否非空的需求时,首先想到@requestParam(required=false),但是这是对于单个参数,不能用于对象,且如果使用单个参数配注解,会使得参数列表很长影响可读性,所以可以使用JSR303校验,在参数对象的实体类字段上加上@NotBlank注解,在参数使用前面加上注解@Valid
sql计算差值聚合

超短期精度 = 1 - |一天的实际功率求和 - 预测功率求和|/实际功率求和
按天聚合,计算绝对值,除法,减法
// 按天聚合查询实际功率的sum和时间
SELECT DATE(t.`time`) as time, SUM(t.actual_power_output) as actual_power_output from wpp_wind_turbines_data as t
where t.device_id = 217 and DATE(t.`time`) BETWEEN '2024-01-01' and '2024-12-31'
GROUP BY DATE(t.`time`)
最后的sql长这样:
// 将预测表中的时间设备id和预测功率查出来,join风电机组表的实际功率时间和设备id,依据时间和设备id连接,
// 再将这个连接表作为表查询计算比值
SELECT
fir.time as time, fir.device_id as device_id,
ROUND(1 - ABS(s.actual_power_output - fir.predicted_power) / s.actual_power_output, 2) as rate,
ROUND(1 - ABS(s.actual_power_output - fir.reported_power) / s.actual_power_output, 2) as report_rate,
fir.count as count
from
(SELECT
DATE(p.`time`) AS time,
p.device_id as device_id,
SUM(p.predicted_power) as predicted_power,
SUM(p.reported_power) as reported_power,
count(*) as count
FROM
wpp_reported_data as p
WHERE
p.`time` BETWEEN '2024-01-01 00:00:00.0' AND '2024-12-31 23:59:59.0'
AND p.device_id = 217
GROUP BY
DATE(p.`time`), p.device_id
) as fir
left join
(
SELECT DATE(t.`time`) as time, t.device_id as device_id, SUM(t.actual_power_output) as actual_power_output from wpp_wind_turbines_data as t
where t.device_id = 217 and DATE(t.`time`) BETWEEN '2024-01-01' and '2024-12-31'
GROUP BY DATE(t.`time`)
) as s on s.time = fir.time and fir.device_id = s.device_id
sql子查询妙用
blog
valueOf() 和 parseDouble()
- valueOf参数可以是字符串和double型,返回的是Double,parseDouble只能是字符串,返回的是double
占位符:
三个占位符—整型int,long用%d
浮点型float,double用%s
字符串使用%s
DATE_FORMAT(t.time,'%Y-%m') as timeMonth,此处使用timeMonth接收转换后的时间,使用Stirng类型接收。因为time字段为Date类型,转换为年-月格式的字符串不能转为时间戳,所以会报错2024-01不能转为timestamp;
这里使用timeMonth中间量接收在业务层将其重新赋给time即可
注意:只有年报,以月格式才需要,如果是DATE(t.time) time,不需要,因为年-月-日格式可以转为timestamp
SELECT
DATE_FORMAT(t.time,'%Y-%m') as timeMonth,
t.device_id deviceId,
CAST(ROUND( AVG( t.speed ), 2 ) AS CHAR) AS speed,
CAST(ROUND( AVG( t.direction_angle ), 2 ) AS CHAR) AS directionAngle,
CAST(ROUND( AVG( t.ideal_power ), 2 ) AS CHAR) AS idealPower,
CAST(ROUND( AVG( t.available_power ), 2 ) AS CHAR) AS availablePower,
CAST(ROUND( AVG( t.actual_power_output ), 2 ) AS CHAR) AS actualPowerOutput,
CAST(ROUND( AVG( t.capacity_power ), 2 ) AS CHAR) AS capacityPower,
CAST(ROUND( AVG( k.predicted_power ), 2 ) AS CHAR) AS predictedPower,
(
select direction
from wpp_wind_turbines_data s
WHERE date_format(s.time,'%Y-%m') = max(DATE_FORMAT(t.time,'%Y-%m')) AND device_id = #{deviceId}
group by direction
order by count(direction) desc
limit 1
) direction
FROM wpp_wind_turbines_data t
left join wpp_prediction_data k on t.device_id =k.device_id and DATE_FORMAT(t.time,'%Y-%m') =DATE_FORMAT(k.time,'%Y-%m')
WHERE date_format(t.time,'%Y') = #{curretDate} AND t.device_id = #{deviceId}
GROUP BY DATE_FORMAT(t.time,'%Y-%m'),t.device_id
LocalDate和Date
Date过时了,因为其可读性差,转换麻烦而且某些方法线程不安全;所以建议使用Java8新特性LocalDate,在SpringBoot中应用
接口和接口默认方法
public interface Iterable<T> {
Iterator<T> iterator();
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
default Spliterator<T> spliterator() {
return Spliterators.spliteratorUnknownSize(iterator(), 0);
}
}
解释:接口是个双刃剑,好处是面向抽象而不是面向具体编程,缺陷是,当需要修改接口时候,需要修改全部实现该接口的类,目前的java 8之前的集合框架没有foreach方法,通常能想到的解决办法是在JDK里给相关的接口添加新的方法及实现。然而,对于已经发布的版本,是没法在给接口添加新方法的同时不影响已有的实现。所以引进的默认方法。他们的目的是为了解决接口的修改与现有的实现不兼容的问题。
我们常用的forEach方法就是一个defualt方法,有自己的实现,类实现此接口时不需要去实现forEach方法
try-catch
在做完整性统计分析,统计数据精度的时候,由于没有数据有的数据没有值,在解析的时候报空指针异常,解决方案是使用try-catch捕获异常,在finally里执行赋值vo的操作,如果值为null也会被try掉,然后继续执行
需要注意的是:
- 在catch块里不能再throw new 异常;
- 且catch的异常范围必须涵盖错误异常,否则捕获不到;
mybatis的标签判断参数是否存在或等于时,如果是数字1或‘1’都是表示数字
需要的数据:设备名字,设备坐标,设备状态,设备风速 风向 实时功率
2024年3月1日 星期五
-
Springboot项目部署
项目测试没问题之后打包,找到jar包 -> 拖动到MobaXterm根目录 -> 命令后台启动nohup java -jar ***.jar >/data/log.log 2>/data/err.log &
前端项目build之后拖动到nginx目录下,前端无需启动 -
插入数据和更新数据等返回值int,useGeneratedKeys的妙用,会直接将id给到入参,不需要再查数据库获得id
-
sql优化
子查询耗时非常严重,一条sql查询耗时五六秒,优化后做到耗时0.6s -
远程调用为什么要序列化
远程调用序列化首先是因为在网络上传输需要使用二进制流形式,再者序列化之后语言无关,反序列提供便捷
将 Student 对象保存到文件,保存到文件的数据是二进制数据,所以并不是说序列化只用在网络传输场景里,只要是保存数据的场景只能是二进制数据时,就需要用序列化。 -
插入数据重复主键则修改ON DUPLICATE KEY UPDATE
适合批量插入时,但又出现重复主键错误时使用<insert id="insertPredictedDataHavingId"> INSERT INTO wpp_prediction_data ( device_id, time, predicted_power, is_reported, lower_confidence_limit, upper_confidence_limit, create_time ) VALUES <foreach collection="list" item="item" index="index" separator=","> ( #{item.deviceId}, #{item.time}, #{item.predictedPower}, #{item.isReported}, #{item.lowerConfidenceLimit}, #{item.upperConfidenceLimit}, <![CDATA[now()]]> ) </foreach> ON DUPLICATE KEY UPDATE device_id = VALUES(device_id), time = VALUES(time), predicted_power = VALUES(predicted_power), is_reported = VALUES(is_reported), lower_confidence_limit = VALUES(lower_confidence_limit), upper_confidence_limit = VALUES(upper_confidence_limit),create_time= VALUES(create_time); </insert>
2024年3月5日 星期二
-
分页查询
离谱错误:getWindMeasurementTowerDataList()刚开始没有(),导致在测风塔页面点击分页页码时,不触发函数,但是页数页码传值准确,需要手动再点击查询<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="queryParams.selectDataSource == 0?getDeviceDataList():queryParams.selectDataSource == 1?getWindMeasurementTowerDataList():''" /> -
前端首次刷新不传值
在前端页面钩子函数调用时,方法是异步执行的,同时执行,也就是不保证钩子函数里的三个方法执行顺序,导致后续有一个查询参数在前端控制台显示有值,但在异步调用后端接口时没有值
应该在getMeasurementArchiveList方法里调用getList这种嵌套方法的格式保证方法顺序执行created() { this.setTime(); this.getMeasurementArchiveList(); this.getList(); }, -
SQL查询
time between '2024-02-04' and '2024-03-05'在时间范围方面,time是精确到时分秒的datetime类型,如果不加转换,上面SQL会在日期后面自动补0,也就是查到的时间范围是2024-02-04 00:00:00~2024-03-05 00:00:00
DATE(time) between '2024-02-04' and '2024-03-05'表示时间范围2024-02-04 00:00:00~2024-03-05 23:59:59
2024年3月6日 星期三
-
sql批量插入,重复更新
说明:插入时,插入字段给id,但是values值id为null,也可以生成id,因为数据库主键设置自增的<insert id="insertWeatherForecastDataList"> insert into wpp_weather_forecast_data(id, time, ten_speed, ten_direction, ten_direction_angle, thirty_speed, thirty_direction, thirty_direction_angle, fifty_speed, fifty_direction, fifty_direction_angle,seventy_speed, seventy_direction, seventy_direction_angle, hub_speed, hub_direction, hub_direction_angle, surface_temperature, surface_humidity, surface_pressure, create_time) values <foreach collection="list" item="item" index="index" separator=","> ( <if test="item.id != null"> #{item.id}, </if> #{item.time}, #{item.tenSpeed}, #{item.tenDirection}, #{item.tenDirectionAngle}, #{item.thirtySpeed}, #{item.thirtyDirection}, #{item.thirtyDirectionAngle}, #{item.fiftySpeed}, #{item.fiftyDirection}, #{item.fiftyDirectionAngle}, #{item.seventySpeed}, #{item.seventyDirection}, #{item.seventyDirectionAngle}, #{item.hubSpeed}, #{item.hubDirection}, #{item.hubDirectionAngle}, #{item.surfaceTemperature}, #{item.surfaceHumidity}, #{item.surfacePressure}, now() ) </foreach> ON DUPLICATE KEY UPDATE time = VALUES(time), ten_speed = VALUES(ten_speed), ten_direction = VALUES(ten_direction), ten_direction_angle = VALUES(ten_direction_angle), thirty_speed = VALUES(thirty_speed),thirty_direction= VALUES(thirty_direction), thirty_direction_angle = VALUES(thirty_direction_angle), fifty_speed = VALUES(fifty_speed),fifty_direction= VALUES(fifty_direction), fifty_direction_angle = VALUES(fifty_direction_angle), seventy_speed = VALUES(seventy_speed), seventy_direction = VALUES(seventy_direction), seventy_direction_angle = VALUES(seventy_direction_angle), hub_speed = VALUES(hub_speed), hub_direction = VALUES(hub_direction), hub_direction_angle = VALUES(hub_direction_angle), surface_temperature = VALUES(surface_temperature), surface_humidity = VALUES(surface_humidity), surface_pressure = VALUES(surface_pressure) </insert> -
批量插入,如果集合isEmpty,sql会报错,应该提前判断一下集合是否非空
-
批量更新
下面这样写,还是会执行n次sql<update id="updateBatch"> <foreach collection="list" item="item" index="index" open="" close="" separator=";"> update wpp_weather_forecast_data <set> <trim suffixOverrides=","> time = #{item.time}, ten_speed = #{item.tenSpeed}, ten_direction = #{item.tenDirection}, ten_direction_angle = #{item.tenDirectionAngle}, thirty_speed = #{item.thirtySpeed}, thirty_direction = #{item.thirtyDirection}, thirty_direction_angle = #{item.thirtyDirectionAngle}, fifty_speed = #{item.fiftySpeed}, fifty_direction = #{item.fiftyDirection}, fifty_direction_angle = #{item.fiftyDirectionAngle}, seventy_speed = #{item.seventySpeed}, seventy_direction = #{item.seventyDirection}, seventy_direction_angle = #{item.seventyDirectionAngle}, hub_direction = #{item.hubDirection}, hub_direction_angle = #{item.hubDirectionAngle}, surface_temperature = #{item.surfaceTemperature}, surface_humidity = #{item.surfaceHumidity}, surface_pressure = #{item.surfacePressure}, create_time = now() </trim> </set> where id = ${item.id} </foreach> </update>mysql并没有提供直接的方法来实现批量更新,但是可以用点小技巧来实现。使用case when then
SQL Server 负数显示零的,不用case when 啦_sql 负数 转换0-CSDN博客
UPDATE mytable SET myfield = CASE id WHEN 1 THEN 'value' WHEN 2 THEN 'value' WHEN 3 THEN 'value' END WHERE id IN (1,2,3) -
负数转换为0,类似于
ifnull(speed,'-') as speed函数select device_id as deviceId, time as time, speed as speed, direction as direction, direction_angle as directionAngle, case when 0 > actual_power_output + 0 then 0 else actual_power_output end as actualPowerOutput, case when 0 > capacity_power + 0 then 0 else capacity_power end as capacityPower, case when 0 > available_power + 0 then 0 else available_power end as availablePower, case when 0 > ideal_power + 0 then 0 else ideal_power end as idealPower from wpp_wind_turbines_data as t where DATE_ADD(t.`time`, INTERVAL 11 DAY) >= #{time} // actual_power_output + 0在sql中,是将字符串转为数字,这里用的字段类型是varchar -
resultMap不添加映射,在实体类添加字段,但是查出来timeMonth别名和实体类字段对应,也会映射成功,说明自定义映射的作用其实是帮你取别名
-
end
- useGeneratedKeys你不知道的秘密
【Mybatis】useGeneratedKeys参数用法及遇到的问题_java usegeneratedkeys-CSDN博客 - nginx反向代理
Nginx详解(一文带你搞懂Nginx)-CSDN博客
举例:我问访问google需要中间代理,我们访问的是Google的真实服务器地址,但是直接用国内的服务器无法访问国外的服务器,或者是访问很慢,需要在本地搭建一个服务器来帮助我们去访问。那这种就是正向代理。
淘宝访问压力过大,往往部署多台服务器,因为服务器中间session不共享,那我们是不是在服务器之间访问需要频繁登录,那这个时候淘宝搭建一个过渡服务器,对我们是没有任何影响的,我们是登录一次,但是访问所有,这种情况就是 反向代理。此时反向代理服务器和目标服务器对外就是一个服务器,暴露的是代理服务器地址,隐藏了真实服务器的地址 - cron表达式