目录
oracle转mysql,上云,批量转换decode,decode转case when
1. 业务背景:
系统上云,原oracle需转为mysql数据库。mysql不支持decode。
系统使用mybatis框架
2. 方案汇总:
1) 方案一:手工改造sql语句。
适用性:系统较小,含有decode的sql较少。
2) 方案二:增加mybatis拦截器
方式:sql执行前变更sql,将decode变更为mysql
缺点:增加系统复杂性,改造后系统较乱。啥样的sql都有,四不像。
3) 方案三:mysql编写decode function
适用性: 因为mysql function不支持动态参数,有多少个参数就需
要传递多少个参数,所以oracle中使用的decode参数需固定。
局限性太大
4) 方案四:编写程序批量替换decode
优点:一劳永逸。
适用性:原sql中decode使用较多。
4. 重点:批量替换代码
1) 整体思路
- 获取系统内所有mybatis 的 sql xml文件
- 判断是否存在decode.
- 获取decode整个字符串,如:decode(a, 1, '好', 2, '坏')
- 获取decode内部所有条件、结果字段;如:a, 1, '好', 2, '坏'。需考虑特殊情况,如decode嵌套,decode内部有函数,内部有语句等情况
2) 具体代码
如有用,麻烦点个赞。
转载请注明出处
import org.apache.commons.lang3.StringUtils;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
/**
* oracle sql语句 decode 批量替换为 case when
*
* @author :zhangbaoyu
* @date :Created in 2021/5/28 下午3:41
*/
public class OracleDecodeToCaseWhenProcess {
// 扫描文件夹
private static final String mybatisXmlDir = "填写包含mybatis xml的目录";
public static void main(String[] args) {
// 所有待处理文件路径 list
List<String> fileNameList = findMapperXmlFile();
for (String mybatisXmlFile : fileNameList) {
// 获取文件内容
String xmlContent = getXmlStr(mybatisXmlFile);
// 文件decode替换
while (xmlContent.contains("decode") || xmlContent.contains("DECODE")) {
// 包含大写的deocde?
boolean upperContains = xmlContent.contains("DECODE");
String containsDecodeStr = upperContains ? "DECODE" : "decode";
// 取出decode内容 DECODE(STATUS_FLAG, 'S', '完成', '未完成')
String decodeStr = getDecodeStr(xmlContent, containsDecodeStr);
String caseWhenStr = decodeToCaseWhenStr(decodeStr);
System.out.println("old decode = " + decodeStr);
System.out.println("new case when = " + caseWhenStr);
xmlContent = xmlContent.replace(decodeStr, caseWhenStr);
writeFile(mybatisXmlFile, xmlContent);
}
}
// 测试
// String decodeToCaseWhenStr = decodeToCaseWhenStr("decode(sum(yanshou_khqt06_all),0,0,round((sum(yanshou_khqt06_all)-sum(yanshou_khqt06_over))/sum(yanshou_khqt06_all)*100,2))");
// String decodeToCaseWhenStr = decodeToCaseWhenStr("decode(decode(tot.timeout_type, null, -1, tot.timeout_type),0,1,0)");
}
/**
* 获取mybatis xml 文件
*
* @return
*/
public static List<String> findMapperXmlFile() {
// 待扫描文件夹
Stack<String> waitDealDirectory = new Stack<>();
waitDealDirectory.push(mybatisXmlDir);
// 所有待处理文件路径 list
List<String> fileNameList = new ArrayList<>();
// 获取所有mybatis xml文件
while (!waitDealDirectory.empty()) {
String fileDir = waitDealDirectory.pop();
File dir = new File(fileDir);
for (File file : dir.listFiles()) {
if (file.isFile()) {
// 是否是mybatis xml 文件
if (file.getName().lastIndexOf("Mapper.xml") != -1) {
fileNameList.add(file.getAbsolutePath());
}
} else if (file.isDirectory()) {
waitDealDirectory.push(file.getAbsolutePath());
}
}
}
return fileNameList;
}
/**
* 获取文件内容
*
* @param filePath
* @return
*/
public static String getXmlStr(String filePath) {
// 文件内容读取
File xmlFile = new File(filePath);
StringBuffer sb = new StringBuffer();
BufferedReader bufferedReader = null;
try {
bufferedReader = new BufferedReader(new FileReader(xmlFile));
String lineStr = null;
while ((lineStr = bufferedReader.readLine()) != null) {
sb.append(lineStr + "\n");
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (null != bufferedReader) {
try {
bufferedReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return sb.toString();
}
/**
* 获取decode 内容
*
* @return
*/
public static String getDecodeStr(String xmlContent, String containsDecodeStr) {
// decode 内的符号
Stack<String> decodeStack = new Stack<>();
decodeStack.push(containsDecodeStr);
// decode字符位置
int decodeBeginIdx = xmlContent.indexOf(containsDecodeStr) + containsDecodeStr.length();
// 基于decode index 增加的index,
int decodeEndIdx = decodeBeginIdx + 1;
while (!decodeStack.empty()) {
// 获取当前字符
String str = xmlContent.substring(decodeEndIdx - 1, decodeEndIdx);
if ("(".contains(str)) {
// 左括号 入栈
decodeStack.push(str);
decodeEndIdx++;
} else if (")".contains(str)) {
// 右括号,出栈,直至匹配到对应的左括号
while (!"(".equals(decodeStack.pop())) {
}
// 如果栈顶 是 最开始压入栈的,直接弹出;程序结束
if (containsDecodeStr.equals(decodeStack.peek())) {
decodeStack.pop();
} else {
decodeEndIdx++;
}
} else {
decodeEndIdx++;
}
}
// 取出decode内容 DECODE(STATUS_FLAG, 'S', '完成', '未完成')
String decodeStr = xmlContent.substring(decodeBeginIdx - containsDecodeStr.length(), decodeEndIdx);
return decodeStr;
}
/**
* decode 转换为 case when
*
* @return
*/
public static String decodeToCaseWhenStr(String decodeStr) {
// 1. 取出decode内部的内容
int indexBegin = decodeStr.indexOf("(");
int indexEnd = decodeStr.lastIndexOf(")");
String decodeContent = decodeStr.substring(indexBegin + 1, indexEnd);
// 各字段 list
List<String> itemList = new ArrayList<>();
// 查找字段的栈
Stack<String> columnFindStack = new Stack<>();
// 每个字段
StringBuffer itemSb = new StringBuffer();
for (int i = 1; i <= decodeContent.length(); i++) {
String charAtStr = decodeContent.substring(i - 1, i);
if ("(".equals(charAtStr)) {
// 左括号 入栈
columnFindStack.push(charAtStr);
// 拼接字段
itemSb.append(charAtStr);
} else if (")".equals(charAtStr)) {
// 右括号 匹配左括号
while (!"(".equals(columnFindStack.pop())) {
}
// 拼接字段
itemSb.append(charAtStr);
} else if (",".equals(charAtStr) && columnFindStack.isEmpty()) {
// 匹配','号,并且栈内为空。取出字段,并开始下一个字段获取
itemList.add(itemSb.toString());
itemSb = new StringBuffer();
} else {
// 拼接字段
itemSb.append(charAtStr);
}
}
// 最后的str
if (StringUtils.isNotEmpty(itemSb.toString())) {
itemList.add(itemSb.toString());
}
if (itemList.size() < 3) {
System.out.println("error itemList size <3,decodeStr=" + decodeStr);
}
StringBuffer caseWhenSb = new StringBuffer("(case ");
for (int i = 0; i < itemList.size(); i++) {
String item = itemList.get(i);
if (i == 0) {
caseWhenSb.append(item);
} else if (i % 2 == 1) {
if (i == itemList.size() - 1) {
caseWhenSb.append(" else ").append(item);
} else {
caseWhenSb.append(" when ").append(item);
}
} else if (i % 2 == 0) {
caseWhenSb.append(" then ").append(item);
}
}
caseWhenSb.append(" end)");
return caseWhenSb.toString();
}
/**
* 写入文件
* @param filePath
* @param xmlContent
*/
public static void writeFile(String filePath, String xmlContent) {
File f = new File(filePath);
FileWriter fw = null;
BufferedWriter bw = null;
try {
if (!f.exists()) {
f.createNewFile();
}
fw = new FileWriter(f.getAbsoluteFile(), false);
bw = new BufferedWriter(fw);
bw.write(xmlContent);
bw.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}