MyBatis項(xiàng)目實(shí)戰(zhàn) 快速將MySQL轉(zhuǎn)換成Oracle語(yǔ)句

一、前言

因項(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)步驟:
  1. 將項(xiàng)目改成支持雙庫(kù)配置(因項(xiàng)目基于mysql數(shù)據(jù)庫(kù)已經(jīng)全部完成县爬,也不想直接替換掉,于是新增oracle庫(kù),讓整個(gè)項(xiàng)目可支持多個(gè)數(shù)據(jù)庫(kù)画恰,這里不是多數(shù)據(jù)源哦!)
  2. Oracle中創(chuàng)建常用函數(shù)
  3. 遍歷項(xiàng)目中的xxxMapper.xml文件吸奴,找到mysql與oracle語(yǔ)句的區(qū)別允扇,然后替換絕大部分SQL
  4. 最后將一些特殊的mysql語(yǔ)句手動(dòng)修改為oracle語(yǔ)句

二、MyBatis-Plus 支持雙庫(kù)配置 【mysql则奥,oracle

1考润、application.yml中配置mybatis-plusdatabase-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 &lt;= 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ǔ)句)

替換步驟:
  1. xxxMapper.xml 中將所有sql語(yǔ)句上加入 databaseId="mysql"
  2. 復(fù)制一份mysql的sql(即 將替換的oracle語(yǔ)句)
  3. 在復(fù)制的sql上加入databaseId="oracle"
  4. 找出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 &lt;= 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 &lt;=1) WHERE ROW_ID > 0");
                    System.out.println();
                }
                line = StringUtils.replace(line, "limit 1", "AND ROWNUM &lt;= 1");
                line = StringUtils.replace(line, "LIMIT 1", "AND ROWNUM &lt;= 1");
                line = StringUtils.replace(line, "limit 0,1", "AND ROWNUM &lt;= 1");
                line = StringUtils.replace(line, "LIMIT 0,1", "AND ROWNUM &lt;= 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è)

  1. mysql: LIMIT 0,1
  2. 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í)行順序
  1. FROM子句組裝來(lái)自不同數(shù)據(jù)源的數(shù)據(jù)
  2. WHERE子句基于指定的條件對(duì)記錄進(jìn)行篩選
  3. GROUP BY子句將數(shù)據(jù)劃分為多個(gè)分組
  4. 聚集函數(shù)進(jìn)行計(jì)算
  5. HAVING子句篩選分組
  6. 計(jì)算所有表達(dá)式
  7. ORDER BY對(duì)結(jié)果進(jìn)行排序

2囊榜、oracle中字段名不能使用符號(hào) " ` " 包括

-- mysql
SELECT `字段名` FROM 表名

-- oracle
SELECT 字段名 FROM 表名

3、字符串拼接

注: Oracle中CONCAT函數(shù)只能放兩個(gè)參數(shù)亥宿,因此改為 || 拼接

  1. mysql: CONCAT('%', 'xxx' , '%')
  2. oracle: '%' || 'xxx' || '%'
-- mysql
SELECT 字段名 FROM 表名 WHERE 字段名 LIKE CONCAT('%','helloworld','%')

-- oracle
SELECT 字段名 FROM 表名 WHERE 字段名 LIKE ('%' || 'helloworld' || '%')

4卸勺、date類型時(shí)間轉(zhuǎn)10位時(shí)間戳

  1. mysql: UNIX_TIMESTAMP
  2. 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) ~

例如:

  1. level -> "LEVEL"
  2. user -> "USER"
  3. number -> "NUMBER"
  4. desc -> "DESC"
  5. date -> DATE

6圆到、判斷是否為 NULL: 如果x為NULL,則返回value,否則返回x值本身

  1. mysql: IFNULL(x, value)
  2. oracle: NVL(x, value)

7、日期時(shí)間互換

前mysql卑吭,后oracle

  1. 字符串類型轉(zhuǎn)時(shí)間類型: STR_TO_DATE -> TO_DATE
  2. 時(shí)間類型轉(zhuǎn)指定字符串類型: DATE_FORMAT -> TO_CHAR
  3. 獲取系統(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ò)!

解決:

  1. 查詢字段改為聚合函數(shù)
  2. 使用如下分析函數(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ù)中...

https://github.com/zhengqingya/mysql-to-oracle

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末客扎,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子罚斗,更是在濱河造成了極大的恐慌徙鱼,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,858評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件针姿,死亡現(xiàn)場(chǎng)離奇詭異袱吆,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)距淫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)绞绒,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人榕暇,你說(shuō)我怎么就攤上這事蓬衡。” “怎么了彤枢?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,282評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵狰晚,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我缴啡,道長(zhǎng)壁晒,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,842評(píng)論 1 295
  • 正文 為了忘掉前任盟猖,我火速辦了婚禮讨衣,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘式镐。我一直安慰自己反镇,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,857評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布娘汞。 她就那樣靜靜地躺著歹茶,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上惊豺,一...
    開(kāi)封第一講書(shū)人閱讀 51,679評(píng)論 1 305
  • 那天燎孟,我揣著相機(jī)與錄音,去河邊找鬼尸昧。 笑死揩页,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的烹俗。 我是一名探鬼主播爆侣,決...
    沈念sama閱讀 40,406評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼幢妄!你這毒婦竟也來(lái)了兔仰?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,311評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤蕉鸳,失蹤者是張志新(化名)和其女友劉穎乎赴,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體潮尝,經(jīng)...
    沈念sama閱讀 45,767評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡榕吼,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了勉失。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片友题。...
    茶點(diǎn)故事閱讀 40,090評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖戴质,靈堂內(nèi)的尸體忽然破棺而出度宦,到底是詐尸還是另有隱情,我是刑警寧澤告匠,帶...
    沈念sama閱讀 35,785評(píng)論 5 346
  • 正文 年R本政府宣布戈抄,位于F島的核電站,受9級(jí)特大地震影響后专,放射性物質(zhì)發(fā)生泄漏划鸽。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,420評(píng)論 3 331
  • 文/蒙蒙 一戚哎、第九天 我趴在偏房一處隱蔽的房頂上張望裸诽。 院中可真熱鬧,春花似錦型凳、人聲如沸丈冬。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,988評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)埂蕊。三九已至往弓,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蓄氧,已是汗流浹背函似。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,101評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留喉童,地道東北人撇寞。 一個(gè)月前我還...
    沈念sama閱讀 48,298評(píng)論 3 372
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像堂氯,于是被迫代替她去往敵國(guó)和親重抖。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,033評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 背景: 閱讀新聞 12C CDB模式下RMAN備份與恢復(fù) [日期:2016-11-29] 來(lái)源:Linux社區(qū) 作...
    陽(yáng)屯okyepd閱讀 3,425評(píng)論 0 7
  • 什么是數(shù)據(jù)庫(kù)祖灰? 數(shù)據(jù)庫(kù)是存儲(chǔ)數(shù)據(jù)的集合的單獨(dú)的應(yīng)用程序。每個(gè)數(shù)據(jù)庫(kù)具有一個(gè)或多個(gè)不同的API畔规,用于創(chuàng)建局扶,訪問(wèn),管理...
    chen_000閱讀 4,035評(píng)論 0 19
  • 原文:https://my.oschina.net/liuyuantao/blog/751438 查詢集API 參...
    陽(yáng)光小鎮(zhèn)少爺閱讀 3,826評(píng)論 0 8
  • .數(shù)據(jù)庫(kù) 數(shù)據(jù)庫(kù)的發(fā)展: 文件系統(tǒng)(使用磁盤(pán)文件來(lái)存儲(chǔ)數(shù)據(jù))=>第一代數(shù)據(jù)庫(kù)(出現(xiàn)了網(wǎng)狀模型,層次模型的數(shù)據(jù)庫(kù))=...
    小Q逛逛閱讀 965評(píng)論 0 2
  • 有一股酸甜苦辣的心情叁扫,說(shuō)不清三妈,道不明 就像曾經(jīng),如何開(kāi)始又如何結(jié)束 驀然回首莫绣,現(xiàn)在是四點(diǎn)和十一點(diǎn)的距離 回不去畴蒲,卻...
    IMCAI閱讀 254評(píng)論 0 0