一、前言
因項(xiàng)目需求飘痛,小編要將項(xiàng)目從mysql遷移到oracle中 ~
之前已經(jīng)完成 數(shù)據(jù)遷移
(https://zhengqing.blog.csdn.net/article/details/103694901)
現(xiàn)在將完成 基于MyBatis-Plus
將項(xiàng)目中的MySQL
語(yǔ)句全部轉(zhuǎn)換
成Oracle
語(yǔ)句
大概實(shí)現(xiàn)步驟:
- 將項(xiàng)目改成支持雙庫(kù)配置(因項(xiàng)目基于mysql數(shù)據(jù)庫(kù)已經(jīng)全部完成县爬,也不想直接替換掉,于是新增oracle庫(kù),讓整個(gè)項(xiàng)目可支持多個(gè)數(shù)據(jù)庫(kù)画恰,這里不是多數(shù)據(jù)源哦!)
- Oracle中創(chuàng)建常用函數(shù)
- 遍歷項(xiàng)目中的
xxxMapper.xml
文件吸奴,找到mysql與oracle語(yǔ)句的區(qū)別允扇,然后替換絕大部分SQL - 最后將一些特殊的mysql語(yǔ)句手動(dòng)修改為oracle語(yǔ)句
二、MyBatis-Plus 支持雙庫(kù)配置 【mysql
则奥,oracle
】
1考润、application.yml
中配置mybatis-plus
的database-id
# mybatis-plus配置
mybatis-plus:
configuration:
jdbc-type-for-null: 'null' # 解決oracle更新數(shù)據(jù)為null時(shí)無(wú)法轉(zhuǎn)換報(bào)錯(cuò)
database-id: oracle # 支持多庫(kù)配置 mysql,oracle
2、MybatisPlus核心配置文件 -> 根據(jù)不同的數(shù)據(jù)庫(kù)廠商執(zhí)行不同的SQL
@Configuration
@MapperScan("com.zhengqing.demo.modules.**.mapper*")
public class MybatisPlusConfig {
/**
* `xxxMapper.xml`文件中的`databaseId`會(huì)自動(dòng)識(shí)別使用的數(shù)據(jù)庫(kù)類型與這里相對(duì)應(yīng)
* 注: 如果沒(méi)有指定`databaseId`則該SQL語(yǔ)句適用于所有數(shù)據(jù)庫(kù)哦~
*
* databaseIdProvider:支持多數(shù)據(jù)庫(kù)廠商
* VendorDatabaseIdProvider: 得到數(shù)據(jù)庫(kù)廠商的標(biāo)識(shí)(驅(qū)動(dòng)getDatabaseProductName())读处,mybatis就能根據(jù)數(shù)據(jù)庫(kù)廠商標(biāo)識(shí)來(lái)執(zhí)行不同的sql;
* MySQL糊治,Oracle,SQL Server,xxxx
*/
@Bean
public DatabaseIdProvider getDatabaseIdProvider(){
DatabaseIdProvider databaseIdProvider = new VendorDatabaseIdProvider();
Properties properties = new Properties();
// 為不同的數(shù)據(jù)庫(kù)廠商起別名
properties.setProperty("MySQL","mysql");
properties.setProperty("Oracle","oracle");
databaseIdProvider.setProperties(properties);
return databaseIdProvider;
}
}
3罚舱、xxxMapper.xml
中通過(guò)databaseId
指定數(shù)據(jù)庫(kù)類型
<select id="selectUserInfo" resultMap="UserVO" databaseId="mysql">
SELECT * FROM 表名 LIMIT 1
</select>
<select id="selectUserInfo" resultMap="UserVO" databaseId="oracle">
SELECT * FROM 表名 WHERE ROWNUM <= 1
</select>
三井辜、Oracle中創(chuàng)建常用函數(shù)
這里根據(jù)個(gè)人項(xiàng)目情況去實(shí)際應(yīng)用即可~
1、ORACLE_TO_UNIX
Oracle時(shí)間 Date類型轉(zhuǎn)換為Unix時(shí)間戳管闷,等同于mysql中的UNIX_TIMESTAMP
create or replace function ORACLE_TO_UNIX(in_date IN DATE) return number is
begin
return( ROUND( (in_date -TO_DATE('19700101','yyyymmdd'))*86400 - TO_NUMBER(SUBSTR(TZ_OFFSET(sessiontimezone),1,3))*3600, 0) );
end ORACLE_TO_UNIX;
2粥脚、FIND_IN_SET
CREATE OR REPLACE FUNCTION FIND_IN_SET(piv_str1 varchar2, piv_str2 varchar2, p_sep varchar2 := ',')
RETURN NUMBER IS
l_idx number:=0; -- 用于計(jì)算piv_str2中分隔符的位置
str varchar2(500); -- 根據(jù)分隔符截取的子字符串
piv_str varchar2(500) := piv_str2; -- 將piv_str2賦值給piv_str
res number:=0; -- 返回結(jié)果
loopIndex number:=0;
BEGIN
-- 如果piv_str中沒(méi)有分割符,直接判斷piv_str1和piv_str是否相等包个,相等 res=1
IF instr(piv_str, p_sep, 1) = 0 THEN
IF piv_str = piv_str1 THEN
res:= 1;
END IF;
ELSE
-- 循環(huán)按分隔符截取piv_str
LOOP
l_idx := instr(piv_str,p_sep);
loopIndex:=loopIndex+1;
-- 當(dāng)piv_str中還有分隔符時(shí)
IF l_idx > 0 THEN
-- 截取第一個(gè)分隔符前的字段str
str:= substr(piv_str,1,l_idx-1);
-- 判斷 str 和piv_str1 是否相等刷允,相等 res=1 并結(jié)束循環(huán)判斷
IF str = piv_str1 THEN
res:= loopIndex;
EXIT;
END IF;
piv_str := substr(piv_str,l_idx+length(p_sep));
ELSE
-- 當(dāng)截取后的piv_str 中不存在分割符時(shí),判斷piv_str和piv_str1是否相等赃蛛,相等 res=1
IF piv_str = piv_str1 THEN
res:= loopIndex;
END IF;
-- 無(wú)論最后是否相等恃锉,都跳出循環(huán)
EXIT;
END IF;
END LOOP;
-- 結(jié)束循環(huán)
END IF;
-- 返回res
RETURN res;
END FIND_IN_SET;
四、工具類(MySQL語(yǔ)句轉(zhuǎn)換Oracle語(yǔ)句)
替換步驟:
- 在
xxxMapper.xml
中將所有sql語(yǔ)句上加入databaseId="mysql"
- 復(fù)制一份mysql的sql(即 將替換的oracle語(yǔ)句)
- 在復(fù)制的sql上加入
databaseId="oracle"
- 找出mysql與oracle語(yǔ)句區(qū)別呕臂,然后替換sql
溫馨小提示: 這里工具類只供參考破托,實(shí)際操作根據(jù)自己的項(xiàng)目做修改哦,操作前建議先備份自己的項(xiàng)目歧蒋,以防操作不當(dāng)丟失代碼哦土砂!
import org.apache.commons.lang3.StringUtils;
import org.junit.Test;
import java.io.*;
import java.util.*;
/**
* <p> mysql遷移oracle 測(cè)試工具類 </p>
*
* @description :
* @author : zhengqing
* @date : 2020/1/08 10:10
*/
public class MySQLToOracleTest {
private final static String ORACLE_SQL = " <!-- ====================================== ↓↓↓↓↓↓ oracle ↓↓↓↓↓↓ ====================================== -->";
@Test // 替換項(xiàng)目中的sql語(yǔ)句
public void testSQL() throws Exception {
String path = System.getProperty("user.dir") + "\\src\\main\\java\\com\\zhengqing\\xxx"; // TODO 這里替換為自己的項(xiàng)目路徑
File file = new File(path);
HashMap<Object, Object> fileMap = new HashMap<>();
getAllFileByRecursion(fileMap, file);
fileMap.forEach((key, value) -> {
String fileXmlName = (String) key;
File fileXml = (File) value;
String filePath = fileXml.getPath();
if (fileXmlName.equals("Test.xml")) {
System.out.println(filePath);
try {
// 1、加入 databaseId="mysql"
addMysql(filePath);
// 2谜洽、復(fù)制一份oracle的sql
if (!checkHasOracle(filePath)) {
copyMysqlToOracle(filePath);
}
// 3萝映、加入 databaseId="oracle"
addOracle(filePath);
// 4、替換mybatis `xxxMapper.xml` 中的sql語(yǔ)句
repalceSQL(filePath);
} catch (IOException e) {
e.printStackTrace();
}
}
});
System.out.println(fileMap);
}
/**
* 替換mybatis `xxxMapper.xml` 中的sql語(yǔ)句
*/
private static void repalceSQL(String path) throws IOException {
File file = new File(path);
FileReader in = new FileReader(file);
BufferedReader bufIn = new BufferedReader(in);
// 內(nèi)存流, 作為臨時(shí)流
CharArrayWriter tempStream = new CharArrayWriter();
// 替換
String line = null;
int row = 0;
int rowOracle = 0;
while ((line = bufIn.readLine()) != null) {
row++;
if (line.contains(ORACLE_SQL)) {
rowOracle = row;
}
if (rowOracle != 0 && row > rowOracle) {
// ① 替換 `LIMIT` -> `AND ROWNUM <= 1` TODO 【注: 部分包含`ORDER BY` 關(guān)鍵字阐虚,需單獨(dú)處理】
if (line.contains("limit") || line.contains("LIMIT")) {
System.out.println();
System.out.println(" ==============================↓↓↓↓↓↓ copy分頁(yè)所需 (" + row + ") ↓↓↓↓↓↓===================================== ");
System.out.println("SELECT * FROM ( SELECT TMP.*, ROWNUM ROW_ID FROM (");
System.out.println();
System.out.println(") TMP WHERE ROWNUM <=1) WHERE ROW_ID > 0");
System.out.println();
}
line = StringUtils.replace(line, "limit 1", "AND ROWNUM <= 1");
line = StringUtils.replace(line, "LIMIT 1", "AND ROWNUM <= 1");
line = StringUtils.replace(line, "limit 0,1", "AND ROWNUM <= 1");
line = StringUtils.replace(line, "LIMIT 0,1", "AND ROWNUM <= 1");
// ② oracle中不能使用“ ` ”符號(hào)
line = StringUtils.replace(line, "`", "");
// ③ CONCAT('%', #{name}, '%') -> '%'||#{name}||'%' (Oracle中concat函數(shù)只能放兩個(gè)參數(shù))
if (line.contains("concat")) {
// String newLine = line.substring(line.indexOf("concat(") + 7, line.lastIndexOf("'%')") + 3);
line = line.replaceAll(",", " || ");
line = line.replaceAll("concat", "");
}
if (line.contains("CONCAT")) {
// String newLine = line.substring(line.indexOf("CONCAT(") + 7, line.lastIndexOf("'%')") + 3);
line = line.replaceAll(",", " || ");
line = line.replaceAll("CONCAT", "");
}
// ④ `UNIX_TIMESTAMP` -> `ORACLE_TO_UNIX` date類型時(shí)間轉(zhuǎn)10位時(shí)間戳
line = line.replaceAll("UNIX_TIMESTAMP", "ORACLE_TO_UNIX");
// ⑤ 部分關(guān)鍵字需加上雙引號(hào) TODO 【注: 字段名大寫(xiě)序臂,映射的別名需保存原本小寫(xiě)!】 `level -> "LEVEL"` `user -> "USER"` `number -> "NUMBER"` `desc -> "DESC"`
List<String> keywordList = new ArrayList<>(Arrays.asList("level", "user", "number"));
if (!line.contains("test=")) {
for (String e : keywordList) {
// StringUtils.swapCase(e) : 大小寫(xiě)互換
line = line.replaceAll(" " + e + " ", " \"" + StringUtils.swapCase(e) + "\" ");
line = line.replaceAll("." + e + " ", "\\.\"" + StringUtils.swapCase(e) + "\" ");
if (line.endsWith(e) || line.endsWith(e + ",")) {
line = line.replaceAll(e, "\"" + StringUtils.swapCase(e) + "\"");
}
}
}
if (line.endsWith(" date") || line.endsWith(" date,") || line.endsWith(" 'date'") || line.endsWith(" 'DATE'") || line.endsWith("DATE")) {
line = line.replaceAll(" date", " \"date\"");
line = line.replaceAll(" date,", " \"date,\"");
line = line.replaceAll(" 'date'", " \"date\"");
line = line.replaceAll(" 'DATE'", " \"date\"");
line = line.replaceAll(" DATE", " \"date\"");
}
line = line.replaceAll(" date ", " \"date\" ");
line = line.replaceAll(" DATE ", " \"date\" ");
// ⑥ `IFNULL` -> `NVL`
line = line.replaceAll("IFNULL", "NVL");
line = line.replaceAll("ifnull", "NVL");
// ⑦ 時(shí)間 `str_to_date` -> `to_date` `date_format` -> `to_char`
// `%Y-%m-%d` -> `yyyy-MM-dd` `%Y-%m` -> `yyyy-MM`
line = line.replaceAll("str_to_date", "TO_DATE");
line = line.replaceAll("STR_TO_DATE", "TO_DATE");
line = line.replaceAll("date_format", "TO_CHAR");
line = line.replaceAll("DATE_FORMAT", "TO_CHAR");
// 這里注意替換順序問(wèn)題,最長(zhǎng)的應(yīng)該放最前面0赂选Q放怼!
line = line.replaceAll("%Y-%m-%d %H:%i:%S", "yyyy-MM-dd HH24:mi:ss");
line = line.replaceAll("%Y-%m-%d %H:%i:%s", "yyyy-MM-dd HH24:mi:ss");
line = line.replaceAll("%Y-%m-%d %H:%i", "yyyy-MM-dd HH24:mi");
line = line.replaceAll("%Y-%m-%d %H", "yyyy-MM-dd HH24");
line = line.replaceAll("%Y-%m-%d %h", "yyyy-MM-dd HH");
line = line.replaceAll("%Y-%m-%d", "yyyy-MM-dd");
line = line.replaceAll("%Y-%m", "yyyy-MM");
line = line.replaceAll("%Y", "yyyy");
line = line.replaceAll("%H", "HH24");
line = line.replaceAll("%k", "HH24");
line = line.replaceAll("now\\(\\)", "(SELECT SYSDATE + 8/24 FROM DUAL)");
line = line.replaceAll("NOW\\(\\)", "(SELECT SYSDATE + 8/24 FROM DUAL)");
// ⑧ ...
// 需手動(dòng)處理的SQL 【 group by | 批量插入 | ... 】
}
// 將該行寫(xiě)入內(nèi)存
tempStream.write(line);
// 添加換行符
tempStream.append(System.getProperty("line.separator"));
}
// 關(guān)閉 輸入流
bufIn.close();
// 將內(nèi)存中的流 寫(xiě)入 文件
FileWriter out = new FileWriter(file);
tempStream.writeTo(out);
out.close();
}
/**
* 加入 databaseId="mysql"
*/
private static void addMysql(String path) throws IOException {
File file = new File(path);
FileReader in = new FileReader(file);
BufferedReader bufIn = new BufferedReader(in);
// 內(nèi)存流, 作為臨時(shí)流
CharArrayWriter tempStream = new CharArrayWriter();
// 替換
String line = null;
while ((line = bufIn.readLine()) != null) {
if ((line.contains("<select") || line.contains("<update") || line.contains("<insert") || line.contains("<delete")) && !line.contains("databaseId")) {
if (line.endsWith(">")) {
line = line.replaceAll(">", " databaseId=\"mysql\">");
} else {
line = line + " databaseId=\"mysql\"";
}
}
// 將該行寫(xiě)入內(nèi)存
tempStream.write(line);
// 添加換行符
tempStream.append(System.getProperty("line.separator"));
}
// 關(guān)閉 輸入流
bufIn.close();
// 將內(nèi)存中的流 寫(xiě)入 文件
FileWriter out = new FileWriter(file);
tempStream.writeTo(out);
out.close();
}
/**
* 加入 databaseId="oracle"
*/
private static void addOracle(String path) throws IOException {
File file = new File(path);
FileReader in = new FileReader(file);
BufferedReader bufIn = new BufferedReader(in);
// 內(nèi)存流, 作為臨時(shí)流
CharArrayWriter tempStream = new CharArrayWriter();
HashSet<String> lineSet = new HashSet<>();
// 替換
String line = null;
while ((line = bufIn.readLine()) != null) {
if (line.contains("databaseId=\"mysql\"")) {
if (lineSet.contains(line)) {
line = line.replaceAll("databaseId=\"mysql\"", "databaseId=\"oracle\"");
}
lineSet.add(line);
}
// 將該行寫(xiě)入內(nèi)存
tempStream.write(line);
// 添加換行符
tempStream.append(System.getProperty("line.separator"));
}
// 關(guān)閉 輸入流
bufIn.close();
// 將內(nèi)存中的流 寫(xiě)入 文件
FileWriter out = new FileWriter(file);
tempStream.writeTo(out);
out.close();
}
/**
* 復(fù)制一份oracle的sql
*/
private static void copyMysqlToOracle(String path) throws IOException {
File file = new File(path);
FileReader in = new FileReader(file);
BufferedReader bufIn = new BufferedReader(in);
// 內(nèi)存流, 作為臨時(shí)流
CharArrayWriter tempStream = new CharArrayWriter();
// 替換
String line = null;
// 需要替換的行
List<String> lineList = new LinkedList<>();
int row = 0;
int firstRow = 0;
while ((line = bufIn.readLine()) != null) {
row++;
if (line.contains("<select") || line.contains("<update") || line.contains("<insert") || line.contains("<delete")) {
firstRow = row;
}
// 添加替換內(nèi)容
if (firstRow != 0 && row >= firstRow && !line.contains("</mapper>")) {
lineList.add(line);
}
// 查詢結(jié)束位置
if (line.contains("</mapper>")) {
tempStream.append(System.getProperty("line.separator"));
tempStream.write(ORACLE_SQL);
tempStream.append(System.getProperty("line.separator"));
tempStream.append(System.getProperty("line.separator"));
lineList.forEach(lineValue -> {
// copy mysql 語(yǔ)句 轉(zhuǎn)為oracle
try {
tempStream.write(lineValue);
tempStream.append(System.getProperty("line.separator"));
} catch (IOException e) {
e.printStackTrace();
}
});
tempStream.append(System.getProperty("line.separator"));
}
// 將該行寫(xiě)入內(nèi)存
tempStream.write(line);
// 添加換行符
tempStream.append(System.getProperty("line.separator"));
}
// 關(guān)閉 輸入流
bufIn.close();
// 將內(nèi)存中的流 寫(xiě)入 文件
FileWriter out = new FileWriter(file);
tempStream.writeTo(out);
out.close();
}
/**
* 檢查是否已經(jīng)復(fù)制SQL
*/
private static boolean checkHasOracle(String path) throws IOException {
File file = new File(path);
FileReader in = new FileReader(file);
BufferedReader bufIn = new BufferedReader(in);
// 內(nèi)存流, 作為臨時(shí)流
CharArrayWriter tempStream = new CharArrayWriter();
// 替換
String line = null;
boolean result = false;
while ((line = bufIn.readLine()) != null) {
if (line.contains(ORACLE_SQL)) {
result = true;
}
// 將該行寫(xiě)入內(nèi)存
tempStream.write(line);
// 添加換行符
tempStream.append(System.getProperty("line.separator"));
}
// 關(guān)閉 輸入流
bufIn.close();
// 將內(nèi)存中的流 寫(xiě)入 文件
FileWriter out = new FileWriter(file);
tempStream.writeTo(out);
out.close();
return result;
}
/**
* 遞歸文件夾 -> 找到所有xml文件
*/
private static void getAllFileByRecursion(HashMap<Object, Object> fileMap, File file) {
File[] fs = file.listFiles();
for (File f : fs) {
String fileName = f.getName();
if (f.isDirectory()) {
// 若是目錄則遞歸构订,否則打印該目錄下的文件
getAllFileByRecursion(fileMap, f);
}
if (f.isFile() && fileName.endsWith(".xml")) {
fileMap.put(fileName, f);
}
}
}
}
五侮叮、總結(jié)
這里簡(jiǎn)單說(shuō)下MySQL和Oracle中的SQL區(qū)別,以及mysql語(yǔ)句轉(zhuǎn)換oracle語(yǔ)句示例
1悼瘾、分頁(yè)
- mysql:
LIMIT 0,1
- oracle:
ROWNUM <= 1
情景①:mysql中不含ORDER BY
-- mysql
SELECT * FROM 表名 LIMIT 1
-- oracle
SELECT * FROM 表名 WHERE ROWNUM <= 1
情景②:mysql中含ORDER BY
-- mysql
SELECT * FROM 表名 ORDER BY 字段名 DESC LIMIT 1
-- oracle
SELECT * FROM (
SELECT TMP.*, ROWNUM ROW_ID FROM (
SELECT * FROM 表名 ORDER BY 字段名 DESC
) TMP WHERE ROWNUM <= 1 )
WHERE ROW_ID > 0;
溫馨小知識(shí):SQL
SELECT語(yǔ)句執(zhí)行順序
-
FROM
子句組裝來(lái)自不同數(shù)據(jù)源的數(shù)據(jù) -
WHERE
子句基于指定的條件對(duì)記錄進(jìn)行篩選 -
GROUP BY
子句將數(shù)據(jù)劃分為多個(gè)分組 -
聚集函數(shù)
進(jìn)行計(jì)算 -
HAVING
子句篩選分組 -
計(jì)算
所有表達(dá)式
-
ORDER BY
對(duì)結(jié)果進(jìn)行排序
2囊榜、oracle中字段名不能使用符號(hào) " ` " 包括
-- mysql
SELECT `字段名` FROM 表名
-- oracle
SELECT 字段名 FROM 表名
3、字符串拼接
注: Oracle中
CONCAT
函數(shù)只能放兩個(gè)參數(shù)亥宿,因此改為||
拼接
- mysql:
CONCAT('%', 'xxx' , '%')
- oracle:
'%' || 'xxx' || '%'
-- mysql
SELECT 字段名 FROM 表名 WHERE 字段名 LIKE CONCAT('%','helloworld','%')
-- oracle
SELECT 字段名 FROM 表名 WHERE 字段名 LIKE ('%' || 'helloworld' || '%')
4卸勺、date類型時(shí)間轉(zhuǎn)10位時(shí)間戳
- mysql:
UNIX_TIMESTAMP
- oracle:
ORACLE_TO_UNIX
(注:此函數(shù)為步驟三中手動(dòng)創(chuàng)建的,并非oracle自帶哦箩绍!)
5孔庭、字段名
為Oracle關(guān)鍵字
需加上雙引號(hào)
溫馨小提示: 字段名需大寫(xiě),如果Java實(shí)體類對(duì)應(yīng)字段為小寫(xiě)材蛛,映射的別名注意需保持原本小寫(xiě)與之對(duì)應(yīng) ~
例如:
level -> "LEVEL"
user -> "USER"
number -> "NUMBER"
desc -> "DESC"
-
date
->DATE
6圆到、判斷是否為 NULL: 如果x為NULL,則返回value,否則返回x值本身
- mysql:
IFNULL(x, value)
- oracle:
NVL(x, value)
7、日期時(shí)間互換
前mysql卑吭,后oracle
- 字符串類型轉(zhuǎn)時(shí)間類型:
STR_TO_DATE
->TO_DATE
- 時(shí)間類型轉(zhuǎn)指定字符串類型:
DATE_FORMAT
->TO_CHAR
- 獲取系統(tǒng)當(dāng)前時(shí)間:
NOW()
->SELECT SYSDATE FROM DUAL
-- 時(shí)間類型轉(zhuǎn)指定字符串類型
SELECT DATE_FORMAT( NOW(),'%Y-%m-%d %H:%i:%s'); -- mysql
SELECT TO_CHAR( SYSDATE,'yyyy-MM-dd HH24:mi:ss') FROM DUAL; -- oracle
-- 字符串類型轉(zhuǎn)時(shí)間類型
SELECT STR_TO_DATE( NOW(), '%Y-%m-%d %H'); -- mysql
SELECT TO_DATE( '2020-01-09', 'yyyy-MM-dd') FROM DUAL; -- oracle 【 注:oracle中前者字符串時(shí)間的格式需與后者轉(zhuǎn)換格式相同哦~ 】
-- 獲取系統(tǒng)當(dāng)前時(shí)間
SELECT NOW(); -- mysql
SELECT SYSDATE + 8/24 FROM DUAL; -- oralce 【注:如果服務(wù)器時(shí)間沒(méi)有相差8小時(shí)則無(wú)需加上`8/24`】
-- mysql
SELECT YEAR( NOW() ); -- 求年份
SELECT QUARTER( NOW() ); -- 求季度
SELECT MONTH( NOW() ); -- 求月份
-- oracle
SELECT TO_CHAR(SYSDATE, 'Q') FROM DUAL; -- 求季度
另外這里給出小編所用到的時(shí)間標(biāo)識(shí)符格式
-- 前:mysql 后:oracle
"%Y-%m-%d %H:%i:%S" "yyyy-MM-dd HH24:mi:ss"
"%Y-%m-%d %H:%i:%s" "yyyy-MM-dd HH24:mi:ss"
"%Y-%m-%d %H:%i" "yyyy-MM-dd HH24:mi"
"%Y-%m-%d %H" "yyyy-MM-dd HH24"
"%Y-%m-%d %h" "yyyy-MM-dd HH"
"%Y-%m-%d" "yyyy-MM-dd"
"%Y-%m" "yyyy-MM"
"%Y" "yyyy"
"%H" "HH24"
"%k" "HH24"
8芽淡、判斷時(shí) 左
右
字段類型
必須相同
這里注意是必須,可能在oracle版本不同的情況下豆赏,老版本不同類型也會(huì)查詢出來(lái)挣菲,但建議還是改為相同類型關(guān)聯(lián),避免以后數(shù)據(jù)庫(kù)版本升級(jí)出現(xiàn)問(wèn)題V腊睢0渍汀!
建議小轉(zhuǎn)大抚岗,比如:數(shù)字轉(zhuǎn)字符串或杠;并使用CONCAT
去修改類型,因?yàn)閙ysql和oracle都支持此函數(shù)宣蔚,并且不會(huì)在特殊類型上出現(xiàn)問(wèn)題 ~
-- ex: `JOIN` 關(guān)聯(lián)表時(shí) 兩張表的關(guān)聯(lián)`字段類型`必須`相同`
SELECT a.*,b.*
FROM 表1 a
LEFT JOIN 表2 b on a.字符串類型字段 = CONCAT(b.數(shù)字類型字段, '')
9向抢、批量插入
-- mysql
<insert id="insertBatch" databaseId="mysql">
INSERT INTO 表名( `字段名1`, `字段名2`, `字段...`) VALUES
<foreach collection="list" item="item" separator="," open="(" close=")">
#{item.字段1},#{item.字段2},#{item中的每一個(gè)字段名...}
</foreach>
</insert>
-- oracle
<insert id="insertBatch" databaseId="oracle">
INSERT INTO 表名(字段名1,字段名2,xxx...)
SELECT A.*
FROM(
<foreach collection="list" item="item" index="index" separator="UNION ALL" >
SELECT
#{item.字段1},#{item.字段2},#{item中的每一個(gè)字段名...}
FROM DUAL
</foreach>
) A
</insert>
10、分組 GROUP BY
oracle中GROUP BY
分組后胚委,查詢出來(lái)的所有字段(除分組字段)必須為聚合函數(shù)的字段挟鸠,否則會(huì)報(bào)錯(cuò)!
解決:
- 查詢字段改為聚合函數(shù)
- 使用如下
分析函數(shù) OVER (Partition BY ...) 及開(kāi)窗函數(shù)
-- mysql
SELECT 字段名,xxx... FROM 表名 GROUP BY 分組字段
-- oracle
SELECT
*
FROM (
SELECT tb.*, ROW_NUMBER ( ) OVER ( PARTITION BY tb.分組字段 ORDER BY tb.排序字段 DESC ) AS result
FROM (
SELECT 字段名,xxx... FROM 表名 -- 此處為查詢sql亩冬,去掉`GROUP BY`分組條件艘希,將分組字段加到上面 【 注:此sql的查詢字段中要么全是聚合函數(shù)字段,要么都不是! 】
) tb
) WHERE result = 1
11覆享、Oracle中表的別名不能用AS
, 列的別名可以用AS
why ?:為了防止和Oracle存儲(chǔ)過(guò)程中的關(guān)鍵字AS沖突的問(wèn)題
12鸠姨、注意Oracle對(duì)數(shù)據(jù)類型要求很?chē)?yán)!Q驼妗!
MySQL與Oracle不同之處遠(yuǎn)不止小編提及的如上幾點(diǎn)连茧,更多的還需要大家根據(jù)在實(shí)際項(xiàng)目中做對(duì)比核蘸,然后去修改哦 ~
六、本文MySQL轉(zhuǎn)Oracle語(yǔ)句工具源碼
溫馨小提示:如果后期小編空閑會(huì)將上文中提供的測(cè)試替換工具類再加以修改啸驯,到時(shí)候會(huì)存放在如下github倉(cāng)庫(kù)中...