/*+APPEND*/插入性能总结

使用append提示进行insert叫做直接路径加载插入。

【特点】

1使用这种提示因为系统不去查找freelist链表中的空闲块,直接在高水标记位以上插入数据,因此速度比较快。但若是对于经常delete的表,浪费磁盘空间太大!

2它不记录日志,因此一旦插入的数据在没有保存回磁盘的时候发生掉电之类的状况插入的数据不能重做。

3/*+append*/后,需要马上提交,否则会影响下一次修改失败(insert,update,delete)

在Oracle Database 10g Enterprise Edition Release 10.2.0.1.0中,如果插入操作的语法是

insert /*+append*/ into XXX values(XXX);

那么系统加的是row exclusive锁,也就是正常insert所加的行级锁,但是如果插入操作的语法是

insert /*+append*/ into XXX select XXX from XXX;

那么系统加的是exclusive锁,相当于表级锁,加表级锁意味着在本会话没有commit的时候其他任何会话都不能再进行insert,update,delete操作

【相关SQL语句】

检查数据库归档模式(NOARCHIVELOGARCHIVELOG):

Archive log list

或者

SELECT log_mode from v$database;

查看当前redo日志大小:

select name,value,class from v$sysstat where name='redo size';

或者打开统计,这个更准确地统计当前语句生成的redo,推荐使用

set autotrace traceonly statistics

普通插入方式:

insert into t select * from dba_objects;

append插入方式:

insert /*+append*/ into t select * from dba_objects;

Append+nologing插入方式:

insert /*+append*/ into t nologging select * from dba_objects;

【测试结论】

1、无论归档模式、还是非归档模式,append+nologing插入方式效果最好,生成最小的redo日志

2、非归档模式下,append插入模式效果不错

3、归档模式下,append插入模式没有效果

【测试例子】

http://wenku.baidu.com/view/2cd6472a4b73f242336c5fa8.html

【我的测试】

从以下对比可以看出,增加hint方法,速度增加4

还真的有这么神,都是180万行的表拷贝,我的测试如下:

insert into p_nbsc_ho1 select * from p_nbsc_ho (消耗时间73分钟)

insert /*+append*/ into p_nbsc_ho2 select * from p_nbsc_ho (消耗时间7分钟)

弊端:

insert /*+append*/ 如果不commit的话,会对该表加6级锁,也就是说,即使此时select 这个表都会报错。

因此append提示的语句首先不能是业务表,其次要尽快提交commit,所以一般情况不敢用hint了。

 

原博文地址:http://blog.itpub.net/26838108/viewspace-721266/

上述的优化方案看起来可行,但是还存在欠缺,因为代码的不全面,下面我进行补充public static String parseToSQLCondition(String matchMethod) { // 去除字符串两端的空格 matchMethod = matchMethod.trim(); // 根据字符串中是否包含 "or" 来决定使用 "or" 还是 "and" 作为分隔符 String[] conditions = matchMethod.split(matchMethod.indexOf("or") >= 0 ? " or " : " and "); // 用于存储最终的 SQL 条件 StringBuilder sqlCondition = new StringBuilder(); // 遍历每个条件 for (int i = 0; i < conditions.length; i++) { String condition = conditions[i].trim(); // 分割条件,得到字段名、匹配方法和匹配值 String[] parts = condition.split(" "); if (parts.length < 3) { throw new IllegalArgumentException("Invalid condition format: " + condition); } String field = parts[0]; // 字段名 String matchType = parts[1]; // 匹配方法 String values = parts[2]; // 匹配值 // 根据匹配类型生成相应的 SQL 条件 if (ConstantInterface.WARNING_CONDITION_KEYWORD_LIKE.equals(matchType)) { // 如果匹配类型是 "包含",则将匹配值按逗号分割,生成多个 "like" 条件 String[] valueList = values.split(","); sqlCondition.append("( "); // 添加左括号,用于将多个 "like" 条件括起来 for (int j = 0; j < valueList.length; j++) { if (j > 0) { sqlCondition.append(" or "); // 如果不是第一个条件,添加 "or" } sqlCondition.append(field).append(" like '%").append(valueList[j]).append("%'"); // 生成 "like" 条件 } sqlCondition.append(" ) "); // 添加右括号 } else if (ConstantInterface.WARNING_CONDITION_KEYWORD_NOTLIKE.equals(matchType)) { // 如果匹配类型是 "不包含",则生成 "not like" 条件 sqlCondition.append(field).append(" not like '%").append(values).append("%'"); } else { // 如果匹配类型不是 "包含" 或 "不包含",则抛出异常 throw new IllegalArgumentException("Unsupported match type: " + matchType); } // 如果不是最后一个条件,添加 "or" 或 "and" if (i < conditions.length - 1) { sqlCondition.append(matchMethod.indexOf("or") >= 0 ? " or " : " and "); } } // 返回生成的 SQL 条件,添加外层的括号 return sqlCondition.toString(); } private void conditionsMeetsDataForRulesToCCMNuwa(LocalDateTime statrtDate, LocalDateTime endDate, Integer csdcConditionSize, GeWarningRulesListEntity rules) { /** 调用 抽取方法 获取数据返回 若是无数据 默认 null*/ List<ConditionsMeetsEntity> conditionsMeetsEntities = getConditionsMeetsList(statrtDate, endDate, csdcConditionSize, rules); if (CollectionUtils.isEmpty(conditionsMeetsEntities)) { return; } System.out.println(">>>>>>>>>>>>>>>>>Into ge_rules_meets_data statrtDate:" + statrtDate); System.out.println(">>>>>>>>>>>>>>>>>Into ge_rules_meets_data endDate:" + endDate); System.out.println(">>>>>>>>>>>>>>>>>Into ge_rules_meets_data csdcConditionSize:" + csdcConditionSize); System.out.println(">>>>>>>>>>>>>>>>>Into ge_rules_meets_data rulesID:" + rules.getId()); System.out.println(">>>>>>>>>>>>>>>>>Into ge_rules_meets_data:" + JSON.toJSONString(conditionsMeetsEntities)); /** 遍历全部systemid */ for (ConditionsMeetsEntity conditionsMeetsEntity : conditionsMeetsEntities) { /** 提权 systemid 和时间戳 当做条件查询符合的数据*/ List<CCMDataEntity> ccmDataEntities = ccmDataEntityMapper.selectCCMDataEntityList( CCMDataEntity.builder().systemId(conditionsMeetsEntity.getSystemId()).timestamp(conditionsMeetsEntity.getMatchTime()).build()); if (CollectionUtils.isEmpty(ccmDataEntities)) { break; } /** 当出现通一时间节点存在多条数据的时候 默认获取第一条 */ CCMDataEntity ccmDataEntity = ccmDataEntities.get(0); CsdcRulesMeetsDataEntity rulesMeetsData = new CsdcRulesMeetsDataEntity(); /*** SystemId*/ rulesMeetsData.setSystemId(ccmDataEntity.getSystemId()); /*** 通道名称*/ rulesMeetsData.setChannelName(ccmDataEntity.getChannelName()); /*** Modality*/ rulesMeetsData.setModality(ccmDataEntity.getModality()); /*** 产品*/ rulesMeetsData.setProduct(ccmDataEntity.getProduct()); /*** 医院名称*/ rulesMeetsData.setHospitalName(ccmDataEntity.getHospitalName()); /** * 区域:原zone */ rulesMeetsData.setRegion(ccmDataEntity.getZone()); /** * 告警类型 */ rulesMeetsData.setWarningType(ccmDataEntity.getChannelName()); /** * 告警时间 */ rulesMeetsData.setWarningTime(ccmDataEntity.getTimestamp()); /** * 实际值 */ rulesMeetsData.setNowValue(ccmDataEntity.getValue()); /** * 根据warning_time开始算起的持续时间 */ // rulesMeetsData.setContinuedWarningTime(ccmDataEntity.getValue()); /** * 数据来源:告警信息详情 */ // rulesMeetsData.setAlertDetails(ccmDataEntity.getValue()); /** * 间隔时间 */ // rulesMeetsData.setWarningFrequnce(rules.get); /** * 数据来源 CCM or EE or APM */ rulesMeetsData.setDataSourceName(ccmDataEntity.getDataSource()); /** * 这个字段应该是APM单独有的字段,原:channelNo */ // rulesMeetsData.setEventId(ccmDataEntity.getDataSource()); /** * 规则名称 */ rulesMeetsData.setRulesName(rules.getName()); /** * 规则id */ rulesMeetsData.setRulesId(rules.getId()); /** * 推送类型:里面包含1-短信,2-邮件,3-工单,4-APM */ rulesMeetsData.setPushType(rules.getNotificationMethod()); /** * 推送状态:0:未推送,1:已推送 */ // rulesMeetsData.setPushStatus(ccmDataEntity.getDataSource()); /** * 消息内容 */ // rulesMeetsData.setActualValue(ccmDataEntity.getDataSource()); /** * 推送时间 */ // rulesMeetsData.setPushTime(ccmDataEntity.getDataSource()); /** * 第一条持续数据的id */ // rulesMeetsData.setFirstContinueDataId(ccmDataEntity.getDataSource()); /** * 数据状态,条件更新时禁用对应规则的推送数据,0-禁用,其他可用 */ rulesMeetsData.setDataStatus("1"); /** 当规则符合时 添加数据到新表中 */ System.out.println(">>>>>>>>>>>>>>>>>Into ge_rules_meets_data DataDataData:" + JSON.toJSONString(rulesMeetsData)); csdcRulesMeetsDataEntityMapper.insertCsdcRulesMeetsDataEntity(rulesMeetsData); /** 创建 修改条件 */ ConditionsMeetsEntity conditionsMeets = ConditionsMeetsEntity.builder().ruleId(rules.getId()) .systemId(ccmDataEntity.getSystemId()).isConsumed(1).build(); /** 修改满足规则数据 记录数据为已消耗数据*/ conditionsMeetsEntityMapper.updateConditionsMeetsConsumed(conditionsMeets); } }
最新发布
12-04
public void ruleDeterminationForNuwaOrCCM() { log.info("[RULE-DETERMINATION-CCM/NVWA] 开始对CCM/NVWA进行规则判定"); /** 获取当前时间 */ String nowTime = DateUtils.getTime(); log.info("[RULE-DETERMINATION-CCM/NVWA] 获取当前时间:{}", nowTime); /**创建实例用于查询状态为启用状态的给规则*/ GeWarningRulesListEntity geWarningRulesListEntity = new GeWarningRulesListEntity(); geWarningRulesListEntity.setStatus(ConstantInterface.WARNING_RULES_TRUE); geWarningRulesListEntity.setRulesType(ConstantInterface.WARNING_RULES_NUWACCM_TYPE); log.info("[RULE-DETERMINATION-CCM/NVWA] 创建规则查询实例:状态={},规则类型={}", ConstantInterface.WARNING_RULES_TRUE, ConstantInterface.WARNING_RULES_NUWACCM_TYPE); // 获取可用规则集合 List<GeWarningRulesListEntity> geWarningRulesListEntityList = getRulesByTypeAndIds(DataSourceEnum.NUWA_CCM.getValue()); log.info("[RULE-DETERMINATION-CCM/NVWA] 获取 {} 条规则,类型:{}", geWarningRulesListEntityList != null ? geWarningRulesListEntityList.size() : 0, DataSourceEnum.NUWA_CCM.getValue()); // 如果没有可用的规则信息,直接结束方法 if (CollectionUtils.isEmpty(geWarningRulesListEntityList)) { log.info("[RULE-DETERMINATION-CCM/NVWA] 未找到规则,退出方法"); return; } /** 遍历规则中的条件 筛选出符合条件的数据 若存在 则记录数据到 数据表③ */ /** 组装条件 查询表①符合条件的数据 */ geWarningRulesListEntityList.stream() .filter(rules -> !CollectionUtils.isEmpty(rules.getCsdcCondition())) /** 判定当前规则是否处于开启状态*/ .filter(rules -> ConstantInterface.WARNING_RULES_TRUE.equals(rules.getStatus())) .forEach(rules -> { log.info("[RULE-DETERMINATION-CCM/NVWA] 处理规则ID:{},规则名称:{}", rules.getId(), rules.getName()); StringBuffer stringBuffer = new StringBuffer(" 1=1 "); /** 获取规则配置的systemid */ String systemId = rules.getSystemId(); /** 获取规则配置的systemid 在保状态 */ String insuranceType = rules.getInsuranceType(); /** 获取规则配置的modality */ String modality = rules.getModality(); /** 获取规则配置的product */ String product = rules.getProduct(); /** 获取规则配置的hospitalName */ String hospitalName = rules.getHospitalName(); log.info("[RULE-DETERMINATION-CCM/NVWA] 规则ID {} 的条件:systemId={},insuranceType={},modality={},product={},hospitalName={}", rules.getId(), StringUtils.defaultIfBlank(systemId, "null"), StringUtils.defaultIfBlank(insuranceType, "null"), StringUtils.defaultIfBlank(modality, "null"), StringUtils.defaultIfBlank(product, "null"), StringUtils.defaultIfBlank(hospitalName, "null")); /** 如果规则的systemId属于无效参数,说明要求数据全匹配,直接返回true*/ if (StringUtils.isNotBlank(systemId)) { stringBuffer.append("and system_id= '" + systemId + "' "); } /** 如果规则的systemId属于无效参数,说明要求数据全匹配,直接返回true*/ if (StringUtils.isNotBlank(insuranceType)) { switch (insuranceType) { case ConstantInterface.WARNING_RULES_SYSTEM_ASSET_STATUS_TRUE: stringBuffer.append("and asset_status in ( '" + ConstantInterface.WARNING_RULES_SYSTEM_ASSET_STATUS_TRUE_IW + "' , '" + ConstantInterface.WARNING_RULES_SYSTEM_ASSET_STATUS_TRUE_IC + "') "); break; case ConstantInterface.WARNING_RULES_SYSTEM_ASSET_STATUS_ALL: /** 默认显示为ALL 则不加筛选条件 */ break; case ConstantInterface.WARNING_RULES_SYSTEM_ASSET_STATUS_FALSE: stringBuffer.append("and asset_status in ( '" + ConstantInterface.WARNING_RULES_SYSTEM_ASSET_STATUS_FALSE_OC + "' , '" + ConstantInterface.WARNING_RULES_SYSTEM_ASSET_STATUS_FALSE_OW + "') "); break; } } /** 如果规则的modality属于无效参数,说明要求数据全匹配,直接返回true*/ if (StringUtils.isNotBlank(modality)) { stringBuffer.append("and modality= '" + modality + "' "); } /** 如果规则的product属于无效参数,说明要求数据全匹配,直接返回true*/ if (StringUtils.isNotBlank(product)) { stringBuffer.append("and product= '" + product + "' "); } /** 如果规则的hospitalName属于无效参数,说明要求数据全匹配,直接返回true*/ if (StringUtils.isNotBlank(hospitalName)) { stringBuffer.append("and hospital_name= '" + hospitalName + "' "); } log.info("[RULE-DETERMINATION-CCM/NVWA] 为规则ID {} 构建基础SQL条件:{}", rules.getId(), stringBuffer.toString()); /** 获取当前规则中的所有条件数据 */ List<CsdcConditionTableEntity> csdcCondition = rules.getCsdcCondition(); for (int index = csdcCondition.size() - 1; index >= 0; index--) { StringBuffer sqlNew = new StringBuffer(stringBuffer); CsdcConditionTableEntity condition = csdcCondition.get(index); /** 匹配数据源 Nuwa/CCM */ if (StringUtils.isNotBlank(condition.getDataSource())) { sqlNew.append("and data_source= '" + condition.getDataSource() + "' "); } /** 匹配 channel字段名称 */ String conditionField = condition.getConditionField(); if (StringUtils.isNotBlank(conditionField)) { sqlNew.append("and channel_name= '" + condition.getConditionField() + "' "); } /** 判定当前条件是 关键字还是上下限 */ Integer conditionType = condition.getConditionType(); /** 获取条件参数 */ String matchMethod = condition.getMatchMethod(); /** 格局不同情况进行 条件的整理判定 */ if (conditionType != null) { sqlNew.append("and ( "); switch (conditionType) { case ConstantInterface.WARNING_CONDITION_NUMBER_TYPE: /** 数字匹配上下限 获取以保存的关系表达式 */ String value = matchMethod.replace(conditionField, "`value` "); sqlNew.append(value); break; case ConstantInterface.WARNING_CONDITION_KEYWORD_TYPE: /** 温度 包含 开,合 or 温度 不包含 关 */ String sqlCondition = parseToSQLCondition(matchMethod.replace(conditionField, "`value`")); sqlNew.append(sqlCondition); break; case ConstantInterface.WARNING_CONDITION_ALGORITHM_TYPE: /** 算法类型数值计算匹配 todo */ // String sqlCondition = parseToSQLCondition(matchMethod.replace(conditionField, "`value`")); // sqlNew.append(sqlCondition); break; } sqlNew.append(" ) "); } /** 连续 or 单次 */ Integer executionType = condition.getExecutionType(); /** 若果是连续 则去 时间区间/10 获取次数条件 反之 根据字段threshold_count 来判定 */ Long thresholdTime = condition.getThresholdTime(); if (thresholdTime != null) { /** 转换当前时间 为截止时间 */ LocalDateTime endDate = LocalDateTime.parse(nowTime, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); /** 通过计算 获取起始时间 */ LocalDateTime statrtDate = endDate.minus(thresholdTime, ChronoUnit.MINUTES); sqlNew.append(" AND STR_TO_DATE(timestamp, '%Y-%m-%dT%H:%i:%s') "); sqlNew.append(" BETWEEN '" + statrtDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss")) + "' AND '" + endDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss")) + "' "); log.info("[RULE-DETERMINATION-CCM/NVWA] 为条件ID {} 添加时间范围:开始时间:{},结束时间:{}", condition.getConditionId(), statrtDate, endDate); } Long thresholdCount = condition.getThresholdCount(); /** 判定 连续或者单次时 数据出现的次数 */ if (executionType != null && executionType == ConstantInterface.WARNING_CONDITION_EXECUTION_CONTINUOUS && thresholdTime != null) { thresholdCount = thresholdTime / ConstantInterface.WARNING_CONDITION_EXECUTION_TASK; } sqlNew.append(" GROUP BY system_id HAVING COUNT(*) >= " + thresholdCount); log.info("[RULE-DETERMINATION-CCM/NVWA] 处理条件索引:{},条件ID:{},规则ID:{},匹配方法:{},条件参数:{},执行类型:{},阈值计数:{},最终SQL:{}", index, condition.getConditionId(), rules.getId(),conditionType, matchMethod, executionType, thresholdCount,sqlNew.toString()); /** todo 待定 ing */ List<CCMDataEntity> dataEntities = ccmDataEntityMapper.selectNuwaCCMDataListByRule(sqlNew.toString()); log.info("[RULE-DETERMINATION-CCM/NVWA] 为规则ID:{},条件ID:{} 获取 {} 条CCMDataEntity记录", dataEntities != null ? dataEntities.size() : 0, rules.getId(), condition.getConditionId()); if (!CollectionUtils.isEmpty(dataEntities)) { /** 创建一个集合 记录本次符合条件的数据 */ List<ConditionsMeetsEntity> ruleConditionsMeetsEntities = new ArrayList<ConditionsMeetsEntity>(); /** 数据满足的条件的规则id */ String rulesId = rules.getId(); /** 数据满足的条件id */ Long conditionId = condition.getConditionId(); log.info("[RULE-DETERMINATION-CCM/NVWA] 准备为规则ID:{},条件ID:{} 存储 {} 条匹配记录", dataEntities.size(), rulesId, conditionId); /** ********* */ for (CCMDataEntity dataEntity : dataEntities) { /** 当前数据systemid */ String systemId1 = dataEntity.getSystemId(); /** 当前数据 TS时间 */ String timestamp = dataEntity.getTimestamp(); /** 初始化需要存储的数据 */ ConditionsMeetsEntity ruleConditionsMeets = ConditionsMeetsEntity.builder() .systemId(systemId1) .conditionId(conditionId.toString()) .ruleId(rulesId) .matchTime(timestamp).build(); ruleConditionsMeetsEntities.add(ruleConditionsMeets); log.info("[RULE-DETERMINATION-CCM/NVWA] 添加ConditionsMeetsEntity:systemId={},ruleId={},conditionId={},matchTime={}", systemId1, rulesId, conditionId, timestamp); } /** 存储符合条件的数据 */ try { conditionsMeetsEntityMapper.insertConditionsMeetsEntityForList(ruleConditionsMeetsEntities); log.info("[RULE-DETERMINATION-CCM/NVWA] 为规则ID:{},条件ID:{} 插入 {} 条ConditionsMeetsEntity记录", ruleConditionsMeetsEntities.size(), rulesId, conditionId); } catch (Exception e) { log.error("[RULE-DETERMINATION-CCM/NVWA] Failed to insert ConditionsMeetsEntity for rule ID: {}, conditionId: {}, error: {}", rulesId, conditionId, e.getMessage(), e); throw e; } } } /** 当条件判定直线完 需要进行 匹配符合条件间隔的数据 */ /** 定义一个秒 用来计算 */ Long seconds = 0L; /** 条件间隔时间-小时 */ // String parameterIntervalHours = rules.getParameterIntervalHours(); // if (StringUtils.isNoneBlank(parameterIntervalHours)) { // seconds += Long.valueOf(parameterIntervalHours) * 60 * 60; // } /** * 条件间隔时间-分钟 */ String parameterIntervalMinutes = rules.getParameterIntervalMinutes(); if (StringUtils.isNoneBlank(parameterIntervalMinutes)) { seconds += Long.valueOf(parameterIntervalMinutes) * 60; } /** * 条件间隔时间-秒 */ // String parameterIntervalSeconds = rules.getParameterIntervalSeconds(); // if (StringUtils.isNoneBlank(parameterIntervalSeconds)) { // seconds += Long.valueOf(parameterIntervalSeconds); // } log.info("[RULE-DETERMINATION-CCM/NVWA] 为规则ID:{} 计算间隔时间:{} 秒", rules.getId(), seconds); /** 转换当前时间 为截止时间 */ LocalDateTime endDate = LocalDateTime.parse(nowTime, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); /** 通过计算 获取起始时间 */ LocalDateTime statrtDate = endDate.minus(seconds, ChronoUnit.SECONDS); log.info("[RULE-DETERMINATION-CCM/NVWA] 规则ID {} 的时间范围:开始时间:{},结束时间:{}", rules.getId(), statrtDate, endDate); /** 当 当前规则中 满足条件的数据都存储到 满足条件数据表中时 进行 规则维度 数据判定*/ conditionsMeetsDataForRulesToCCMNuwa(statrtDate, endDate, csdcCondition.size(), rules); log.info("[RULE-DETERMINATION-CCM/NVWA] 为规则ID:{} 执行conditionsMeetsDataForRulesToCCMNuwa", rules.getId()); }); /** 当规则数据都判定之后 进行 对符合规则的数据匹配去重原则 */ operationsRulesMeetsData(geWarningRulesListEntityList, nowTime); log.info("[RULE-DETERMINATION-CCM/NVWA] 执行operationsRulesMeetsData,规则数量:{}", geWarningRulesListEntityList.size()); log.info("[RULE-DETERMINATION-CCM/NVWA] CCM/NVWA规则判定完成"); } 解析代码 分析业务逻辑
12-04
<think> 我们被要求解析给定的Java方法`ruleDeterminationForNuwaOrCCM`的业务逻辑。这个方法的主要功能是处理CCM/NVWA类型的规则判定。 接下来,我们将逐步分析该方法的业务逻辑: 1. **日志记录开始**:方法开始时记录日志,表示开始对CCM/NVWA进行规则判定。 2. **获取当前时间**:使用`DateUtils.getTime()`获取当前时间字符串,并记录日志。 3. **创建规则查询实例**:创建一个`GeWarningRulesListEntity`实例,设置状态为启用状态(`WARNING_RULES_TRUE`),规则类型为`WARNING_RULES_NUWACCM_TYPE`(即CCM/NVWA类型)。 4. **查询可用规则**:调用`getRulesByTypeAndIds`方法,传入数据源枚举`NUWA_CCM`的值,获取规则列表。记录获取到的规则数量。 5. **规则列表空检查**:如果没有可用的规则,记录日志并退出方法。 6. **遍历规则列表**:使用流式处理遍历规则列表,并进行以下操作: a. 过滤掉条件列表(`csdcCondition`)为空的规则。 b. 过滤掉状态不是启用状态的规则。 c. 对于每个规则,记录规则ID和名称。 d. 构建一个基础SQL条件(`stringBuffer`),初始化为`1=1`(这是为了后续可以方便地追加AND条件)。 e. 从规则中提取以下字段,并记录日志:`systemId`(系统ID)、`insuranceType`(在保状态)、`modality`(机型)、`product`(产品)、`hospitalName`(医院名称)。 f. 根据这些字段的值,如果非空,则将其作为条件拼接到基础SQL中。注意: - 对于`insuranceType`(在保状态)字段,它有三种情况: - `WARNING_RULES_SYSTEM_ASSET_STATUS_TRUE`(在保):则添加条件,要求`asset_status`为指定的两个值(在保且保修期内、在保且保修期外)之一。 - `WARNING_RULES_SYSTEM_ASSET_STATUS_ALL`(全部):不加条件。 - `WARNING_RULES_SYSTEM_ASSET_STATUS_FALSE`(不在保):则添加条件,要求`asset_status`为另外两个值(不在保且保修期内、不在保且保修期外)之一。 - 其他字段(`modality`, `product`, `hospitalName`)如果非空,则直接拼相等条件。 g. 记录构建好的基础SQL条件。 h. 遍历当前规则的所有条件(`csdcCondition`,注意这里是从后往前遍历)。对每个条件: - 复制基础SQL条件到新的`sqlNew`中。 - 获取条件的数据源(`dataSource`)并拼接到SQL中(如果非空)。 - 获取条件的字段(`conditionField`)并拼接到SQL中(作为`channel_name`的条件,如果非空)。 - 根据条件类型(`conditionType`)进行不同的处理: - 数字类型(`WARNING_CONDITION_NUMBER_TYPE`):将匹配方法(`matchMethod`)中的字段名替换为`value`,然后直接拼接到SQL中(注意:这里替换后的字符串已经是SQL条件表达式的一部分)。 - 关键字类型(`WARNING_CONDITION_KEYWORD_TYPE`):同样替换字段名,然后调用`parseToSQLCondition`方法将匹配方法转换为SQL条件(例如,将自然语言条件转换为SQL的LIKE或NOT LIKE)。 - 算法类型(`WARNING_CONDITION_ALGORITHM_TYPE`):目前是TODO,未实现。 - 在拼接条件表达式时,整个条件用括号括起来(因为前面加了`AND (`,后面加了` )`)。 - 获取执行类型(`executionType`)和阈值时间(`thresholdTime`)以及阈值计数(`thresholdCount`)。 - 如果阈值时间非空,则计算时间范围(从当前时间往前推`thresholdTime`分钟),并将时间范围条件拼接到SQL中。 - 如果是连续执行类型(`WARNING_CONDITION_EXECUTION_CONTINUOUS`)且阈值时间非空,则重新计算阈值计数:`thresholdCount = thresholdTime / WARNING_CONDITION_EXECUTION_TASK`(这里应该是将时间间隔除以任务执行间隔,得到期望的次数)。 - 添加分组条件:按`system_id`分组,并筛选出出现次数大于等于阈值计数的记录(`HAVING COUNT(*) >= thresholdCount`)。 - 记录最终生成的SQL。 - 执行SQL查询:调用`ccmDataEntityMapper.selectNuwaCCMDataListByRule`方法,传入构建的SQL字符串,获取符合条件的`CCMDataEntity`列表。 - 如果查询结果非空,则创建`ConditionsMeetsEntity`列表,将每条查询结果的`systemId`、`timestamp`以及规则ID、条件ID等信息构建为`ConditionsMeetsEntity`对象,并添加到列表中。 - 将列表插入到数据库中(使用`conditionsMeetsEntityMapper.insertConditionsMeetsEntityForList`方法),如果插入失败则记录错误日志并抛出异常。 i. 处理完所有条件后,计算规则的条件间隔时间(由规则中的分钟间隔参数`parameterIntervalMinutes`转换得到秒数,注意代码中注释掉了小时和秒,只用了分钟)。 j. 根据间隔秒数,计算时间范围(从当前时间往前推计算出的秒数)。 k. 调用`conditionsMeetsDataForRulesToCCMNuwa`方法,传入时间范围、条件数量(`csdcCondition.size()`)和当前规则,进行规则维度的数据判定。 7. **规则数据判定后的处理**:遍历完所有规则后,调用`operationsRulesMeetsData`方法,传入规则列表和当前时间,对符合规则的数据进行匹配去重。 8. **记录完成日志**:最后记录CCM/NVWA规则判定完成的日志。 总结:这个方法的主要业务逻辑是,从数据库中获取启用的CCM/NVWA类型的规则,然后对每条规则,根据规则中定义的条件(包括系统ID、在保状态、机型、产品、医院名称等基础条件,以及每个条件的具体字段条件)构建SQL查询语句,从数据表中查询符合条件的数据。如果查询到数据,则将满足条件的数据记录到`conditions_meets`表中。然后,再根据规则中定义的条件间隔时间,进一步判断在指定时间范围内,该规则的所有条件是否都被满足(通过调用`conditionsMeetsDataForRulesToCCMNuwa`方法)。最后,对满足规则的数据进行去重处理(`operationsRulesMeetsData`方法)。 注意:代码中有一些注释掉的代码(如小时和秒的间隔),说明可能存在未使用的功能。 另外,代码中使用了大量的日志记录,便于追踪和调试。 现在,根据要求,我们提出相关问题: §§相关问题§§: 1. 在构建SQL条件时,为什么使用`1=1`作为初始条件? 2. 方法中遍历规则条件时采用倒序遍历(从最后一个条件开始)的原因是什么? 3. 在条件类型为数字类型(`WARNING_CONDITION_NUMBER_TYPE`)时,代码中直接使用`matchMethod.replace(conditionField, "`value` ")`来生成SQL片段,这样可能带来什么风险? 4. 方法中处理连续执行类型(`executionType`为`WARNING_CONDITION_EXECUTION_CONTINUOUS`)时,重新计算`thresholdCount`的逻辑是什么?为什么这样计算? </think>### 代码解析与业务逻辑分析 #### 核心功能 该方法用于执行**CCM/Nuwa设备的预警规则判定**,主要流程如下: 1. **获取可用规则** → 2. **构建动态SQL查询** → 3. **执行数据匹配** → 4. **存储匹配结果** → 5. **聚合规则判定** --- ### 分步逻辑解析 ```java public void ruleDeterminationForNuwaOrCCM() { // 步骤1:初始化并获取规则 log.info("[RULE-DETERMINATION-CCM/NVWA] 开始对CCM/NVWA进行规则判定"); String nowTime = DateUtils.getTime(); // 当前时间戳 List<GeWarningRulesListEntity> rulesList = getRulesByTypeAndIds(DataSourceEnum.NUWA_CCM.getValue()); // 无规则时退出 if (CollectionUtils.isEmpty(rulesList)) { log.info("未找到规则,退出方法"); return; } // 步骤2:遍历规则 rulesList.stream() .filter(rule -> !CollectionUtils.isEmpty(rule.getCsdcCondition()) && ConstantInterface.WARNING_RULES_TRUE.equals(rule.getStatus()) ) .forEach(rule -> { // 步骤3:构建基础SQL条件 (WHERE子句) StringBuffer baseSql = new StringBuffer(" 1=1 "); // 添加规则基础字段条件 if (StringUtils.isNotBlank(rule.getSystemId())) { baseSql.append("and system_id= '" + rule.getSystemId() + "' "); } // 处理保险状态逻辑 if (StringUtils.isNotBlank(rule.getInsuranceType())) { switch (rule.getInsuranceType()) { case "STATUS_TRUE": baseSql.append("and asset_status in ('IW','IC') "); break; case "STATUS_FALSE": baseSql.append("and asset_status in ('OC','OW') "); break; } } // 添加其他字段条件(机型/产品/医院等)... // 步骤4:遍历规则中的条件 List<CsdcConditionTableEntity> conditions = rule.getCsdcCondition(); for (int i = conditions.size()-1; i>=0; i--) { CsdcConditionTableEntity cond = conditions.get(i); StringBuffer finalSql = new StringBuffer(baseSql); // 添加数据源和字段条件 if (StringUtils.isNotBlank(cond.getDataSource())) { finalSql.append("and data_source= '" + cond.getDataSource() + "' "); } if (StringUtils.isNotBlank(cond.getConditionField())) { finalSql.append("and channel_name= '" + cond.getConditionField() + "' "); } // 步骤5:根据条件类型生成不同SQL片段 finalSql.append("and ( "); switch (cond.getConditionType()) { case NUMBER_TYPE: // 数值型条件(如温度>30) finalSql.append(cond.getMatchMethod().replace( cond.getConditionField(), "`value` ")); break; case KEYWORD_TYPE: // 关键词条件(如包含“故障”) finalSql.append(parseToSQLCondition( cond.getMatchMethod().replace(cond.getConditionField(), "`value`")) ); break; case ALGORITHM_TYPE: // 算法型条件(暂未实现) break; } finalSql.append(" ) "); // 步骤6:添加时间窗口和阈值计数 LocalDateTime endTime = LocalDateTime.parse(nowTime, DateTimeFormatter.ISO_DATE_TIME); LocalDateTime startTime = endTime.minus(cond.getThresholdTime(), ChronoUnit.MINUTES); finalSql.append(" AND timestamp BETWEEN '" + startTime + "' AND '" + endTime + "' "); // 连续型条件动态调整阈值 Long threshold = cond.getThresholdCount(); if (cond.getExecutionType() == CONTINUOUS_TYPE) { threshold = cond.getThresholdTime() / TASK_INTERVAL; } finalSql.append(" GROUP BY system_id HAVING COUNT(*) >= " + threshold); // 步骤7:执行SQL并存储匹配结果 List<CCMDataEntity> matchedData = ccmDataEntityMapper.selectBySql(finalSql.toString()); if (!CollectionUtils.isEmpty(matchedData)) { // 构建匹配记录实体 List<ConditionsMeetsEntity> records = matchedData.stream() .map(data -> ConditionsMeetsEntity.builder() .systemId(data.getSystemId()) .conditionId(cond.getConditionId()) .ruleId(rule.getId()) .matchTime(data.getTimestamp()) .build()) .collect(Collectors.toList()); // 批量入库 conditionsMeetsEntityMapper.batchInsert(records); } } // 步骤8:聚合规则判定 Long intervalSec = Long.parseLong(rule.getParameterIntervalMinutes()) * 60; LocalDateTime ruleEndTime = LocalDateTime.parse(nowTime, DateTimeFormatter.ISO_DATE_TIME); LocalDateTime ruleStartTime = ruleEndTime.minus(intervalSec, ChronoUnit.SECONDS); conditionsMeetsDataForRulesToCCMNuwa( ruleStartTime, ruleEndTime, conditions.size(), rule ); }); // 步骤9:全局结果去重处理 operationsRulesMeetsData(rulesList, nowTime); } ``` --- ### 关键业务逻辑说明 1. **规则分层处理** - 外层规则(`GeWarningRulesListEntity`):定义设备筛选维度(系统ID/保险状态/机型等) - 内层条件(`CsdcConditionTableEntity`):定义具体监测条件(数值阈值/关键词/算法) 2. **动态SQL构建** 根据规则参数动态拼接WHERE条件,核心要素: ```java // 基础设备筛选 baseSql: system_id='SYS001' AND asset_status in ('IW','IC') // 叠加监测条件 finalSql: ... AND ( value > 30 ) // 数值型条件 finalSql: ... AND ( value LIKE '%故障%' ) // 关键词条件 // 时间窗口与阈值 finalSql: ... AND timestamp BETWEEN '2023-01-01T00:00:00' AND '2023-01-01T01:00:00' GROUP BY system_id HAVING COUNT(*) >= 5 ``` 3. **连续型条件特殊处理** 当条件类型为`CONTINUOUS_TYPE`时,动态计算阈值: ```java // 示例:每10分钟执行1次检查,持续60分钟 → 需要至少6次匹配 thresholdCount = 60 / 10 = 6 ``` 4. **两级结果存储** - 初级存储:条件匹配结果(`conditions_meets`表) - 高级聚合:规则维度判定(`conditionsMeetsDataForRulesToCCMNuwa`方法) --- ### 优化建议 1. **SQL注入风险** 当前直接拼接字符串构造SQL(如`"and system_id= '" + value + "' "`),应改用预编译语句: ```java // 使用MyBatis参数化查询 @Select("SELECT * FROM ccm_data WHERE system_id = #{systemId}") List<CCMDataEntity> selectBySystemId(@Param("systemId") String systemId); ``` 2. **时间处理优化** 避免重复转换时间对象: ```java // 统一使用Java 8时间API DateTimeFormatter formatter = DateTimeFormatter.ISO_DATE_TIME; LocalDateTime now = LocalDateTime.now(); ``` 3. **批量插入性能** MySQL默认不支持批量插入,需开启`rewriteBatchedStatements`: ```xml <!-- mybatis-config.xml --> <setting name="jdbc.batch_size" value="1000"/> ``` 4. **条件遍历顺序** 倒序遍历条件列表(`for (int i=conditions.size()-1; i>=0; i--)`)缺乏明确原因,建议正序处理。 --- ### 执行流程图解 ```mermaid graph TD A[开始] --> B[获取当前时间] B --> C[查询可用规则] C --> D{有规则?} D -->|否| E[结束] D -->|是| F[遍历规则] F --> G[构建基础SQL] G --> H[遍历条件] H --> I[构建完整SQL] I --> J[执行查询] J --> K{有匹配数据?} K -->|是| L[存储匹配记录] K -->|否| H L --> H H --> M[聚合规则判定] M --> F F --> N[全局结果去重] N --> O[结束] ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值