將查詢從 Oracle 資料庫轉換至 MySQL 適用的 Cloud SQL,並進行最佳化

本文將說明 Oracle® 和 MySQL 適用的 Cloud SQL 之間的基本查詢差異,以及 Oracle 中的功能如何對應至 MySQL 適用的 Cloud SQL 中的功能。並概略說明 Cloud SQL for MySQL 的效能考量事項,以及如何分析及改善Google Cloud的查詢效能。雖然本文會介紹如何為 MySQL 適用的 Cloud SQL 最佳化儲存的程序和觸發事件,但不會說明如何將 PL/SQL 程式碼轉譯為 MySQL 儲存的程序和函式。

將查詢從 Oracle 資料庫轉換為 Cloud SQL for MySQL 時,請注意某些 SQL 方言的差異。這兩個資料庫平台之間也有幾個內建函式不相容或不相容。

基本查詢差異

雖然 Oracle 和 MySQL 適用的 Cloud SQL 都支援 ANSI SQL,但在查詢資料時,兩者仍有幾個基本差異,主要在於系統函式的使用方式。

下表列出 Oracle 和 Cloud SQL for MySQL 的 SELECTFROM 語法差異。

Oracle 功能名稱 Oracle 實作 MySQL 適用的 Cloud SQL 支援 MySQL 適用的 Cloud SQL 等同項目
資料擷取的 SQL 基本語法 SELECT
FROM
WHERE
GROUP BY
HAVING
ORDER BY
SELECT
FROM
WHERE
GROUP BY
HAVING
ORDER BY
SELECT 輸出沖印相片 SELECT 1 FROM DUAL SELECT 1

SELECT 1 FROM DUAL
欄別名 SELECT COL1 AS C1 SELECT COL1 AS C1

SELECT COL1 C1
資料表名稱區分大小寫 不區分大小寫 (例如,資料表名稱可以是 ordersORDERS) 根據定義的資料表名稱區分大小寫 (例如資料表名稱只能是 ordersORDERS)

內嵌檢視畫面

內嵌檢視畫面 (也稱為衍生資料表) 是 SELECT 陳述式,位於 FROM 子句中,用於做為子查詢。內嵌檢視畫面可移除複合計算或排除彙整作業,同時將多個個別查詢濃縮為單一簡易查詢,進而簡化複雜查詢。

以下範例說明從 Oracle 11g/12c 轉換至 Cloud SQL for MySQL 的內嵌檢視畫面。

Oracle 11g/12c 中的內嵌檢視畫面:

 SELECT FIRST_NAME,
            DEPARTMENT_ID,
            SALARY,
            DATE_COL
     FROM EMPLOYEES,
          (SELECT SYSDATE AS DATE_COL FROM DUAL);

在 Cloud SQL for MySQL 5.7 中使用別名建立的有效檢視畫面:

SELECT FIRST_NAME,
              DEPARTMENT_ID,
              SALARY,
              DATE_COL
       FROM EMPLOYEES, (SELECT SYSDATE() AS DATE_COL FROM DUAL) AS A1;

彙整

Cloud SQL for MySQL 支援 Oracle 的彙整類型,但不支援 FULL JOIN。MySQL 適用的 Cloud SQL 彙整支援使用其他語法,例如 USING 子句、WHERE 子句 (取代 ON 子句),以及 JOIN 陳述式中的 SUBQUERY

下表列出 JOIN 轉換範例。

Oracle JOIN 類型 MySQL 適用的 Cloud SQL 支援 MySQL 適用的 Cloud SQL JOIN 語法
INNER JOIN SELECT E.FIRST_NAME, D.DEPARTMENT_NAME FROM EMPLOYEES E JOIN DEPARTMENTS D ON E.DEPARTMENT_ID = D.DEPARTMENT_ID;
CROSS JOIN SELECT E.FIRST_NAME, D.DEPARTMENT_NAME FROM EMPLOYEES E CROSS JOIN DEPARTMENTS D
FULL JOIN 建議您將 UNIONLEFTRIGHT JOINS: 搭配使用
SELECT E.FIRST_NAME, D.DEPARTMENT_NAME FROM EMPLOYEES E LEFT JOIN DEPARTMENTS D ON E.DEPARTMENT_ID = D.DEPARTMENT_ID UNION SELECT E.FIRST_NAME, D.DEPARTMENT_NAME FROM EMPLOYEES E RIGHT JOIN DEPARTMENTS D ON E.DEPARTMENT_ID = D.DEPARTMENT_ID;
LEFT JOIN [ OUTER ] SELECT E.FIRST_NAME, D.DEPARTMENT_NAME FROM EMPLOYEES E LEFT JOIN DEPARTMENTS D ON E.DEPARTMENT_ID = D.DEPARTMENT_ID;
RIGHT JOIN [ OUTER ] SELECT E.FIRST_NAME, D.DEPARTMENT_NAME FROM EMPLOYEES E RIGHT JOIN DEPARTMENTS D ON E.DEPARTMENT_ID = D.DEPARTMENT_ID;
SUBQUERY SELECT E.FIRST_NAME, D.DEPARTMENT_NAME FROM EMPLOYEES E JOIN (SELECT * FROM DEPARTMENTS)D ON E.DEPARTMENT_ID = D.DEPARTMENT_ID;

雖然 MySQL 適用的 Cloud SQL 支援 UNIONUNION ALL 函式,但不支援 Oracle 的 INTERSECTMINUS 函式:

  • UNION 會在刪除重複記錄後,附加兩個 SELECT 陳述式的結果集。
  • UNION ALL 會附加兩個 SELECT 陳述式的結果集,但不會刪除重複的記錄。
  • INTERSECT 只會在兩個查詢的結果集中找到記錄時,傳回兩個 SELECT 陳述式的交集。
  • MINUS 會比較兩個或多個 SELECT 陳述式,只傳回第一個查詢中不包含第二個查詢所傳回的資料列。

下表列出一些 Oracle 到 MySQL 適用的 Cloud SQL 轉換範例。

Oracle 函式 Oracle 實作 MySQL 適用的 Cloud SQL 支援 MySQL 適用的 Cloud SQL 等同項目
UNION SELECT COL1 FROM TBL1
UNION
SELECT COL1 FROM TBL2
SELECT COL1 FROM TBL1
UNION
SELECT COL1 FROM TBL2
UNION ALL SELECT COL1 FROM TBL1
UNION ALL
SELECT COL1 FROM TBL2
SELECT COL1 FROM TBL1
UNION ALL
SELECT COL1 FROM TBL2
INTERSECT SELECT COL1 FROM TBL1
INTERSECT
SELECT COL1 FROM TBL2
SELECT COL1 FROM TBL1
WHERE COL1 IN
(SELECT COL1 FROM TBL2)
MINUS SELECT COL1 FROM TBL1
MINUS
SELECT COL1 FROM TBL2
SELECT A.COL1 FROM TBL1 A LEFT JOIN TBL2 B ON USING(COL1) WHERE B.COL1 IS NULL

純量和群組函式

MySQL 適用的 Cloud SQL 提供大量的標量 (單列) 和匯總函式。部分 MySQL 適用的 Cloud SQL 函式與 Oracle 函式相似 (名稱和功能相同,或名稱不同但功能相似)。雖然部分 MySQL 適用的 Cloud SQL 函式名稱與 Oracle 函式相同,但兩者可能會顯示不同的功能。

下表說明 Oracle 和 MySQL 適用的 Cloud SQL 字元函式在名稱和功能上相等的情況,以及建議轉換的情況。

Oracle 函式 Oracle 實作 MySQL 適用的 Cloud SQL 等同項目 MySQL 適用的 Cloud SQL 函式 MySQL 適用的 Cloud SQL 導入作業
CONCAT 傳回與第二個字串連接的第一個字串:
CONCAT('A', 1) = A1
CONCAT CONCAT('A', 1) = A1
CONCAT USING PIPE FNAME |' '| LNAME CONCAT CONCAT(FNAME, ' ', LNAME)
LOWERUPPER 傳回字串,所有字母皆為小寫或大寫:
LOWER('SQL') = sql
LOWERUPPER LOWER('SQL') = sql
LPAD/RPAD 傳回 expression1,左或右以 expression2 中的字元序列填入,長度為 n 個字元:
LPAD('A',3,'*') = **A
LPADRPAD LPAD('A',3,'*') = **A
SUBSTR 傳回字串的一部分,從 x 位置開始 (在本例中為 3),長度為 y。字串中的第一個位置是 1。
SUBSTR('MySQL', 3, 3) = SQL
SUBSTR SUBSTR('MySQL', 3, 3) = SQL
INSTR 傳回指定字串中字串的位置 (索引):
INSTR('MySQL', 'y') = 2
INSTR INSTR('MySQL', 'y') = 2
REPLACE 傳回字串,其中每個搜尋字串都已替換為替換字串:
REPLACE('ORADB', 'ORA', 'MySQL') = MySQLDB
REPLACE REPLACE('ORADB', 'ORA', 'MySQL') = MySQLDB
TRIM 從字串中裁去開頭或結尾的字元 (或兩者皆裁去):
TRIM(both '-' FROM '-MySQL-') = MySQL TRIM(' MySQL ') = MySQL
TRIM TRIM(both '-' FROM '-MySQL-') = MySQL TRIM(' MySQL ') = MySQL
LTRIM/RTRIM 從字串的左側或右側移除搜尋結果中出現的所有字元:
LTRIM(' MySQL', ' ') = MySQL
部分支援 LTRIM or RTRIM Oracle LTRIMRTRIM 函式會採用第二個參數,指定要從字串中移除哪些開頭或結尾字元。MySQL 適用的 Cloud SQL 函式只會從指定字串移除開頭和結尾的空白:
LTRIM(' MySQL') = MySQL
ASCII 接收單一字元並傳回其數字 ASCII 程式碼:
ASCII('A') = 65
ASCII ASCII('A') = 65
CHR 傳回 ASCII 代碼值,這是從 0 到 225 的數值,對應至字元:
CHR(65) = A
需要不同的函式名稱 CHAR MySQL 適用的 Cloud SQL 會使用 CHAR 函式提供相同功能,因此您需要變更函式名稱:
CHAR(65) = A
LENGTH 傳回指定字串的長度:
LENGTH('MySQL') = 5
LENGTH LENGTH('MySQL') = 5
REGEXP_REPLACE 在字串中搜尋規則運算式模式:
REGEXP_REPLACE('John', '[hn].', 'e') = Joe
不適用 自 MySQL 8 版起提供支援。解決方法是盡可能使用 REPLACE 函式,或將邏輯移至應用程式層。
REGEXP_SUBSTR 透過搜尋字串中的規則運算式模式,擴充 SUBSTR 函式的功能:
REGEXP_SUBSTR('https://console.cloud.google.com/sql/instances','https://([[:alnum:]]+\.?){3,4}/?') = https://console.cloud.google.com/
不適用 自 MySQL 8 版起提供支援。解決方法:盡可能使用 SUBSTR 函式,或將邏輯移至應用程式層
REGEXP_COUNT 傳回模式在來源字串中出現的次數 不適用 但 MySQL 適用的 Cloud SQL 沒有等同的函式。將此邏輯移至應用程式層。
REGEXP_INSTR 搜尋字串位置 (索引) 的規則運算式模式 不適用 自 MySQL 8 版起提供支援。如果是舊版,請將這項邏輯移至應用程式層。
REVERSE 傳回指定字串的反向字串:
REVERSE('MySQL') = LQSyM
REVERSE REVERSE('MySQL') = LQSyM

下表說明 Oracle 和 MySQL 適用的 Cloud SQL 數值函式在名稱和功能上是否相等,以及建議轉換的情況。

Oracle 函式 Oracle 實作 MySQL 適用的 Cloud SQL 等同項目 MySQL 適用的 Cloud SQL 函式 MySQL 適用的 Cloud SQL 導入作業
ABS 傳回指定數字的絕對值:
ABS(-4.6) = 4.6
ABS ABS(-4.6) = 4.6
CEIL 傳回大於或等於指定數字的最小整數:
CEIL(21.4) = 22
CEIL CEIL(21.4) = 22
FLOOR 傳回等於或小於指定數字的最大整數:
FLOOR(-23.7) = -24
FLOOR FLOOR(-23.7) = -24
MOD 傳回 m 除以 n 後的餘數:
MOD(10, 3) = 1
MOD MOD(10, 3) = 1
ROUND 傳回 n 四捨五入到小數點右側的整數位數:
ROUND(1.39, 1) = 1.4
ROUND ROUND(1.39, 1) = 1.4
TRUNC(數字) 傳回經截斷至 n2 小數位數的 n1。第二個參數為選用參數,
TRUNC(99.999) = 99 TRUNC(99.999, 0) = 99
需要不同的函式名稱 TRUNCATE(數字) MySQL 適用的 Cloud SQL 函式名稱不同,且第二個參數為必填。
TRUNCATE(99.999, 0) = 99

下表說明 Oracle 和 MySQL 適用的 Cloud SQL datetime 函式在名稱和功能上的對應關係,以及建議轉換的情況。

Oracle 函式 Oracle 實作 MySQL 適用的 Cloud SQL 等同項目 MySQL 適用的 Cloud SQL 函式 MySQL 適用的 Cloud SQL 導入作業
SYSDATE 傳回資料庫伺服器所在作業系統的目前日期和時間設定:
SELECT SYSDATE FROM DUAL; = 31-JUL-2019
SYSDATE()

MySQL 適用的 Cloud SQL SYSDATE() 必須包含括號,且預設會傳回與 Oracle SYSDATE 函式不同的 datetime 格式:

SELECT SYSDATE() FROM DUAL; = 2019-01-31 10:01:01.0

您可以在工作階段層級變更 datetime 格式

SYSTIMESTAMP 傳回系統日期,包括秒數小數和時區:
SELECT SYSTIMESTAMP FROM DUAL = 01-JAN-19 07.37.11.622187000 AM +00:00
需要不同的函式名稱 CURRENT_ TIMESTAMP 根據預設,MySQL 適用的 Cloud SQL 函式會傳回不同的 datetime 格式。如要重新格式化輸出內容,請使用 DATE_FORMAT() 函式。
SELECT CURRENT_TIMESTAMP FROM DUAL; = 2019-01-31 06:55:07
LOCAL_ TIMESTAMP TIMESTAMP 類型的形式傳回目前的日期和時間:
SELECT LOCALTIMESTAMP FROM DUAL = 01-JAN-19 10.01.10.123456 PM
傳回不同的 datetime 格式 LOCAL_ TIMESTAMP Cloud SQL for MySQL 函式傳回的 datetime 格式與 Oracle 的預設格式不同。如要重新格式化輸出內容,請使用 DATE_FORMAT() 函式。
SELECT LOCAL_TIMESTAMP FROM DUAL = 2019-01-01 10:01:01.0
CURRENT_DATE 傳回目前的日期:
SELECT CURRENT_DATE FROM DUAL = 31-JAN-19
傳回不同的 datetime 格式 CURRENT_ DATE MySQL 適用的 Cloud SQL 函式會傳回不同的 datetime 格式。如要重新格式化輸出內容,請使用 DATE_FORMAT() 函式。
SELECT CURRENT_DATE FROM DUAL = 2019-01-31
CURRENT_ TIMESTAMP 傳回目前的日期和時間:
SELECT CURRENT_TIMESTAMP FROM DUAL = 31-JAN-19 06.54.35.543146 AM +00:00
傳回不同的 datetime 格式 CURRENT_ TIMESTAMP MySQL 適用的 Cloud SQL 函式會傳回不同的 datetime 格式。如要重新格式化輸出內容,請使用 DATE_FORMAT() 函式。
SELECT CURRENT_TIMESTAMP FROM DUAL = 2019-01-31 06:55:07
ADD_MONTHS 傳回日期加上整數月:
ADD_MONTHS(SYSDATE, 1) = 31-JAN-19
需要不同的函式名稱 ADDDATE MySQL 適用的 Cloud SQL 函式會傳回不同的 datetime 格式。如要重新格式化輸出內容,請使用 DATE_FORMAT() 函式。
ADDDATE(SYSDATE(), 1) = 2019-08-01 06:42:49.0
EXTRACT (日期部分) 根據間隔運算式傳回 datetime 欄位的值:
EXTRACT(YEAR FROM DATE '2019-01-31') = 2019
EXTRACT (日期部分) EXTRACT(YEAR FROM DATE '2019-01-31') = 2019
LAST_DAY 傳回特定日期當月最後一天:
LAST_DAY('01-JAN-2019') = 31-JAN-19
部分支援 LAST_DAY MySQL 適用的 Cloud SQL 函式會傳回與 Oracle 預設格式不同的 datetime 格式。如要重新格式化輸出內容,請使用 DATE_FORMAT() 函式。
LAST_DAY('2019-01-01') = 2019-01-31
MONTH_ BETWEEN 傳回指定日期 date1date2 之間的月份數:
MONTHS_BETWEEN( SYSDATE, SYSDATE-60) = 1.96
部分支援 PERIOD_DIFF MySQL 適用的 Cloud SQL PERIOD_DIFF 函式會以整數形式,傳回兩個期間 (格式為 YYMMYYYYMM) 之間的月份差異:
PERIOD_DIFF( '201903', '201901') = 2
TO_CHAR (Datetime) 將數字、datetime 或時間戳記類型轉換為字串類型
TO_CHAR( SYSDATE,'DD-MM-YYYY HH24:MI:SS') = 01-01-2019 10:01:01
需要不同的函式名稱 DATE_FORMAT MySQL 適用的 Cloud SQL DATE_FORMAT 函式會根據格式字串格式化日期值:
DATE_FORMAT( SYSDATE(),'%d-%m-%Y %H:%i:%s') 01-01-2019 10:01:01

下表說明 Oracle 和 MySQL 適用的 Cloud SQL 編碼和解碼函式在名稱和功能上是否相同,以及建議轉換的情況。

Oracle 函式 Oracle 實作 MySQL 適用的 Cloud SQL 等同項目 MySQL 適用的 Cloud SQL 函式 MySQL 適用的 Cloud SQL 導入作業
DECODE 使用 IF-THEN-ELSE 陳述式的功能,逐一將運算式與每個搜尋值進行比較 CASE 使用 MySQL 適用的 Cloud SQL CASE 陳述式來執行類似功能
DUMP 傳回 VARCHAR2 值,其中包含資料類型代碼、長度 (以位元組為單位) 和運算式的內部表示法 不適用 不支援
ORA_HASH 計算指定運算式的雜湊值 MD5 or SHA 使用 MD5 函式計算 128 位元總和檢查碼,或使用 SHA 函式計算 160 位元總和檢查碼

下表說明 Oracle 和 MySQL 適用的 Cloud SQL 轉換函式在名稱和功能上是否相等,以及建議轉換的情況。

Oracle 函式 Oracle 實作 MySQL 適用的 Cloud SQL 等同項目 MySQL 適用的 Cloud SQL 函式 MySQL 適用的 Cloud SQL 導入作業
CAST 將一個內建資料類型或集合型值轉換為另一個內建資料類型或集合型值:
CAST('1' as int) + 1 = 2
部分支援 CAST 視是否需要明確或隱含轉換而調整:
CAST('1' AS SIGNED) + 1 = 2
CONVERT 將字元字串從一個字元集轉換為另一個字元集:
CONVERT ( 'Ä Ê Í Õ Ø A B C D E ', 'US7ASCII', 'WE8ISO8859P1') = ?? ?? ?? A B C
部分支援 CONVERT 您需要對 MySQL 適用的 Cloud SQL CONVERT 函式進行一些語法和參數調整:
CONVERT( 'Ä Ê Í A B C ' USING utf8) = Ä Ê Í A B C
TO_CHAR (字串/數字) 這個函式會將數字或日期轉換為字串:
TO_CHAR(22.73,'$99.9') = $22.7
FORMAT MySQL 適用的 Cloud SQL FORMAT 函式會將數字轉換為 #,###,###.## 等格式,並將結果四捨五入為小數位數,然後以字串格式傳回結果:
CONCAT('$', FORMAT(22.73, 1)) = $22.7
TO_DATE Oracle 的 TO_DATE 函式會根據 datetimecode 格式將字串轉換為日期:
TO_DATE( '2019/01/01', 'yyyy-mm-dd') = 01-JAN-2019
需要不同的函式名稱 STR_TO_DATE Cloud SQL for MySQL STR_TO_DATE 函式會接收字串,並根據 datetime 格式傳回日期:
STR_TO_DATE( '2019/01/01', '%Y/%m/%d') = 2019-01-01
TO_NUMBER 將運算式轉換為 NUMBER 資料類型的值:
TO_NUMBER('01234') = 1234
需要不同的函式名稱 CAST 使用 MySQL 適用的 Cloud SQL CAST 函式,即可傳回與 Oracle TO_NUMBER 函式相同的結果:
CAST('01234' as SIGNED) = 1234

下表說明 Oracle 和 MySQL 適用的 Cloud SQL 的條件 SELECT 函式在名稱和功能上相等,以及建議轉換的情況。

Oracle 函式 Oracle 實作 MySQL 適用的 Cloud SQL 等同項目 MySQL 適用的 Cloud SQL 函式 MySQL 適用的 Cloud SQL 導入作業
CASE CASE 陳述式會從一連串條件中選擇,並使用以下語法執行對應的陳述式:
CASE WHEN condition THEN result [WHEN ...] [ELSE result] END
CASE 除了 CASE 函式之外,Cloud SQL for MySQL 也支援在 SELECT 陳述式中使用 IF/ELSE 條件處理:
CASE WHEN condition THEN result [WHEN ...] [ELSE result] END

下表說明 Oracle 和 MySQL 適用的 Cloud SQL 空值函式在名稱和功能上是否相等,以及建議轉換的情況。

Oracle 函式 Oracle 實作 MySQL 適用的 Cloud SQL 等同項目 MySQL 適用的 Cloud SQL 函式 MySQL 適用的 Cloud SQL 導入作業
COALESCE 傳回運算式清單中的第一個非空值運算式:
COALESCE( null, '1', 'a') = a
COALESCE COALESCE( null, '1', 'a') = 1
NULLIF 比較 expression1expression2。如果兩者相等,函式會傳回 null。如果不相等,函式會傳回 expression1
NULLIF('1', '2') = a
NULLIF NULLIF('1', '2') = a
NVL 在查詢結果中,將 null 值替換為字串:
NVL(null, 'a') = a
IFNULL IFNULL(null, 'a') = a
NVL2 根據運算式是否為空值,判斷查詢傳回的值 CASE CASE 陳述式會從一系列條件中選擇,並執行相應的陳述式:
CASE WHEN condition THEN result [WHEN ...] [ELSE result] END

下表說明 Oracle 和 MySQL 適用的 Cloud SQL 環境和 ID 函式在名稱和功能上是否相等,以及建議轉換的情況。

Oracle 函式 Oracle 實作 MySQL 適用的 Cloud SQL 等同項目 MySQL 適用的 Cloud SQL 函式 MySQL 適用的 Cloud SQL 導入作業
SYS_GUID 產生並傳回由 16 個位元組組成的全域專屬 ID (RAW 值):
SELECT SYS_GUID() FROM DUAL = 8EFA4A31468B4C6DE05011AC0200009E
REPLACEUUID 解決方法是使用 REPLACEUUID 函式模擬 SYS_GUID 函式:
REPLACE( UUID(), '-', '')
UID 傳回可唯一識別工作階段使用者 (已登入的使用者) 的整數:
SELECT UID FROM DUAL = 43
不適用 不適用
USER 傳回已連線至目前工作階段的使用者使用者名稱:
SELECT USER FROM DUAL = username
USER + INSTR + SUBSTR MySQL 適用的 Cloud SQL USER 函式會傳回連線的使用者名稱和主機名稱 (root@IP_ADDRESS)。如要只擷取使用者名稱,請使用其他支援函式:
SELECT SUBSTR(USER(), 1, INSTR(USER(), '@') -1) FROM DUAL = root
USERENV 傳回目前 Oracle 工作階段的相關資訊,例如工作階段的語言:
SELECT USERENV('LANGUAGE') FROM DUAL = ENGLISH_AMERICA. AL32UTF8
SHOW SESSION VARIABLES MySQL 適用的 Cloud SQL SHOW SESSION VARIABLES 陳述式會傳回目前工作階段的設定:
SHOW SESSION VARIABLES LIKE '%collation%'; = utf8_general_ci
ROWID Oracle 會為資料表的每個資料列指派專屬的 ROWID,用於識別資料表中的資料列。ROWID 是資料行位址,其中包含資料物件編號、資料列的資料區塊、資料列位置和資料檔案。 部分支援 不適用 ROW_NUMBER() 可從 MySQL 8.0 開始使用。如果您使用的是舊版,請使用工作階段變數 @row_number 模擬相同功能。
ROWNUM 傳回數字,代表 Oracle 資料表傳回資料列的順序 部分支援 不適用 ROW_NUMBER() 自 MySQL 8.0 起提供。如果您使用的是舊版,請使用工作階段變數 @row_number 模擬相同功能。

下表說明 Oracle 和 MySQL 適用的 Cloud SQL 的匯總 (群組) 函式在名稱和功能上相等的情況,以及建議轉換的情況。

Oracle 函式 Oracle 實作 MySQL 適用的 Cloud SQL 等同項目 MySQL 適用的 Cloud SQL 函式 MySQL 適用的 Cloud SQL 導入作業
AVG 傳回資料欄或運算式的平均值 AVG 等同於 Oracle
COUNT 傳回查詢傳回的資料列數 COUNT 等同於 Oracle
COUNT (DISTINCT) 傳回資料欄或運算式中不重複值的數量 COUNT (DISTINCT) 等同於 Oracle
MAX 傳回資料欄或運算式的最大值 MAX 等同於 Oracle
MIN 傳回資料欄或運算式的最小值 MIN 等同於 Oracle
SUM 傳回資料欄或運算式的值總和 SUM 等同於 Oracle
LISTAGG ORDER BY 子句中指定的每個群組內資料排序,並連結計量欄的值:
SELECT LISTAGG( DEPARTMENT_NAME, ', ') WITHIN GROUP (ORDER BY DEPARTMENT_NAME) DEPT FROM DEPARTMENTS; -- Single line results = Accounting, Administration, Benefits, Construction
需要不同的函式名稱和語法 GROUP_ CONCAT 使用 MySQL 適用的 Cloud SQL GROUP_CONCAT 函式,即可傳回等效結果:
SELECT GROUP_CONCAT( DEPARTMENT_NAME ORDER BY DEPARTMENT_NAME SEPARATOR ', ') DEPT FROM DEPARTMENTS; -- Single line results = Accounting, Administration, Benefits, Construction

下表說明 Oracle 和 MySQL 適用的 Cloud SQL FETCH 函式在名稱和功能上的對應關係。

Oracle 函式 Oracle 實作 MySQL 適用的 Cloud SQL 等同項目 MySQL 適用的 Cloud SQL 函式 MySQL 適用的 Cloud SQL 導入作業
FETCH 從多列查詢的結果集擷取指定數量的資料列:
SELECT * FROM EMPLOYEES FETCH FIRST 10 ROWS ONLY;
LIMIT 使用 MySQL LIMIT 子句,從查詢中擷取資料列:
SELECT * FROM EMPLOYEES LIMIT 10;

基本篩選、運算子和子查詢

基本篩選、運算子函式和子查詢的轉換作業相對簡單,只需花費少許心力即可完成。由於 Oracle 和 MySQL 適用的 Cloud SQL 使用不同的預設日期格式,因此大部分的努力都集中在轉換日期格式:

  • Oracle SYSDATE 函式預設會傳回以下格式:01-AUG-19
  • 預設情況下,MySQL 適用的 Cloud SQL SYSDATE() 函式會傳回以下格式:2019-08-01 12:04:05

如要設定日期和時間格式,請使用 MySQL DATE_FORMATSTR_TO_DATE 函式。

下表說明 Oracle 和 MySQL 適用的 Cloud SQL 基本篩選、運算子和子查詢函式的名稱和功能相等,以及建議轉換的情況。

Oracle 函式 Oracle 實作 MySQL 適用的 Cloud SQL 等同項目 MySQL 適用的 Cloud SQL 函式
EXISTS/ NOT EXISTS EXISTS/ NOT EXISTS SELECT * FROM DEPARTMENTS D WHERE EXISTS (SELECT 1 FROM EMPLOYEES E WHERE E.DEPARTMENT_ID = D.DEPARTMENT_ID);
IN/NOT IN IN/NOT IN SELECT * FROM DEPARTMENTS D WHERE DEPARTMENT_ID IN (SELECT DEPARTMENT_ID FROM EMPLOYEES E); -- OR SELECT * FROM EMPLOYEES WHERE (EMPLOYEE_ID, DEPARTMENT_ID) IN((100, 90));
LIKE/NOT LIKE LIKE/NOT LIKE SELECT * FROM EMPLOYEES WHERE FIRST_NAME LIKE '_e_n%';
BETWEEN/ NOT BETWEEN BETWEEN/ NOT BETWEEN SELECT * FROM EMPLOYEES WHERE EXTRACT(YEAR FROM HIRE_DATE) NOT BETWEEN 2001 and 2004;
AND/OR AND/OR SELECT * FROM EMPLOYEES WHERE DEPARTMENT_ID IN(100, 101) AND (SALARY >= 1000 OR HIRE_DATE <= '2006-02-05');
SubQuery SubQuery MySQL 適用的 Cloud SQL 支援 SELECT 子句、JOIN 子句中的子查詢,以及 WHERE/AND 子句中的篩選:
-- SELECT Subquery SELECT D.DEPARTMENT_NAME, (SELECT AVG(SALARY) AS AVG_SAL FROM EMPLOYEES E WHERE E.DEPARTMENT_ID = D.DEPARTMENT_ID) AVG_SAL FROM DEPARTMENTS D; -- JOIN Subquery SELECT FIRST_NAME, LAST_NAME, SALARY FROM EMPLOYEES E JOIN (SELECT * FROM DEPARTMENTS WHERE LOCATION_ID = 2700) D ON E.DEPARTMENT_ID = D.DEPARTMENT_ID; -- Filtering Subquery SELECT FIRST_NAME, LAST_NAME, SALARY FROM EMPLOYEES WHERE SALARY < (SELECT AVG(SALARY) FROM EMPLOYEES);
運算子 運算子 MySQL 適用的 Cloud SQL 支援所有基本運算子:
> | >= | < | <= | = | <> | !=

針對 MySQL 適用的 Cloud SQL 查詢的最佳做法

為維持 MySQL 適用的 Cloud SQL 與 Oracle 之間的效能水準,您可能需要對查詢進行最佳化。這些最佳化措施包括變更索引結構和調整資料庫結構定義。本節提供幾項指南,協助您在 MySQL 適用的 Cloud SQL 上獲得類似的查詢效能。

建立叢集索引

使用 InnoDB 儲存引擎時,最佳做法是定義具有主鍵的資料表,因為這個鍵會在該資料表上建立叢集索引。除了改善查詢效能之外,這個方法還可讓您建立其他次要索引。不過,請避免建立過多索引。建立多個索引並不會提升效能,反而可能會減緩 DML 的執行速度。這項最佳做法會引導您採用第二項最佳做法:定期監控是否有重複的索引,如果有,請從資料庫中刪除。

使用以下查詢,找出沒有主鍵的資料表,以便為這些資料表建立主鍵:

mysql> SELECT t.table_schema, t.table_name
       FROM information_schema.tables t LEFT JOIN
       information_schema.statistics s
       ON t.table_schema=s.table_schema AND t.table_name=s.table_name
       AND s.non_unique=0
       WHERE s.table_name IS NULL
       AND t.table_schema NOT IN('sys', 'information_schema', 'mysql',
       'performance_schema')
       AND t.`TABLE_TYPE` <> 'VIEW';

使用以下查詢找出沒有索引的資料表,以便為這些資料表建立索引:

mysql> SELECT t.table_schema, t.table_name FROM INFORMATION_SCHEMA.tables t
       WHERE table_name NOT IN
             (SELECT  table_name FROM (
                      SELECT  table_name, index_name
                      FROM information_schema.statistics
                  GROUP BY  table_name, index_name) tab_ind_cols
           GROUP BY table_name)
AND table_schema NOT IN('sys', 'information_schema', 'mysql', 'performance_schema')
AND TABLE_TYPE <> 'VIEW';

請使用以下查詢檢查是否有重複的索引,以便移除重複項目:

mysql> SELECT * FROM sys.schema_redundant_indexes;

調整查詢參數

如要調整查詢效能,您可能需要調整工作階段參數。MySQL 適用的 Cloud SQL 提供一組旗標,可供您用於這項用途,包括下列旗標:

  • InnoDB 相關參數
  • SORT 個參數
  • JOIN 個參數
  • 快取處理參數

監控查詢

執行速度緩慢的查詢可能會導致系統停止回應或導致其他瓶頸,因此請務必定期監控查詢。

您可以透過以下幾種方式診斷執行速度緩慢的 SQL 陳述式:

  • 使用 MySQL 適用的 Cloud SQL 資訊主頁,取得執行速度緩慢的查詢相關的即時和歷來洞察資料。
  • 使用 Cloud Monitoring 監控 MySQL 適用的 Cloud SQL 慢速查詢記錄。
  • 使用 MySQL 適用的 Cloud SQL statement_analysis 檢視畫面,查看 SQL 陳述式的執行階段統計資料:

    mysql> SELECT * FROM sys.statement_analysis;
    

分析 MySQL 適用的 Cloud SQL 查詢

Cloud SQL for MySQL 中的查詢最佳化工具會為 SELECTINSERTUPDATEDELETE 陳述式產生執行計畫。當您調整執行速度緩慢的查詢時,這些計畫就很實用。請注意以下幾點:

  • 執行計畫並非需要遷移的資料庫物件,而是用於分析 Oracle 和 MySQL 適用的 Cloud SQL 之間,在相同資料集上執行相同陳述式的效能差異。
  • MySQL 適用的 Cloud SQL 不支援與 Oracle 相同的執行計畫語法、功能或輸出內容。

以下是說明 Oracle 執行計畫和 MySQL 適用的 Cloud SQL 執行計畫差異的示例計畫:

SQL> EXPLAIN PLAN FOR
     SELECT * FROM EMPLOYEES WHERE EMPLOYEE_ID = 105;

SQL> SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY (FORMAT=>'ALL +OUTLINE'));

Plan hash value: 1833546154

---------------------------------------------------------------------------------------------
| Id  | Operation                   | Name          | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |               |     1 |    69 |     1   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| EMPLOYEES     |     1 |    69 |     1   (0)| 00:00:01 |
|*  2 |   INDEX UNIQUE SCAN         | EMP_EMP_ID_PK |     1 |       |     0   (0)| 00:00:01 |
---------------------------------------------------------------------------------------------

mysql> EXPLAIN SELECT * FROM EMPLOYEES WHERE EMPLOYEE_ID = 105;

+----+-------------+-----------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| id | select_type | table     | partitions | type  | possible_keys | key     | key_len | ref   | rows | filtered | Extra |
+----+-------------+-----------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | EMPLOYEES | NULL       | const | PRIMARY       | PRIMARY | 3       | const |    1 |   100.00 | NULL  |
+----+-------------+-----------+------------+-------+---------------+---------+---------+-------+------+----------+-------+

最佳化內儲程序和觸發條件

與 Oracle 相反,MySQL 適用的 Cloud SQL 會在每次執行時剖析儲存的程序和函式。MySQL BENCHMARK() 公用程式是一項實用的工具,可用於基準化儲存程序和函式效能。這項工具會採用兩個參數 (一個為迭代計數,另一個為運算式),並估算指定運算式的執行時間 (例如儲存程序、函式和 SELECT 陳述式)。輸出內容代表所有疊代作業的總執行時間。

以下範例說明 BENCHMARK() 公用程式:

-- SELECT Expression Example

mysql> select benchmark(10000000, 'select sysdate()');
+-----------------------------------------+
| benchmark(10000000, 'select sysdate()') |
+-----------------------------------------+
|                                       0 |
+-----------------------------------------+
1 row in set (0.12 sec)

-- Result: Run time of 0.12 sec for 1,0000,000 iterations

-- FUNCTION Example
mysql> select benchmark(1000000, func1());
+-----------------------------+
| benchmark(1000000, func1()) |
+-----------------------------+
|                           0 |
+-----------------------------+
1 row in set (2.54 sec)

-- Result: Run time of 2.54 sec for 1,000,000 iterations

如果您在轉換期間發現效能下降,請使用 MySQL EXPLAIN 指令找出導致效能下降的可能因素。針對效能緩慢問題的常見解決方案,就是變更資料表索引結構,以便配合 MySQL 最佳化工具。另一個常見做法是減少不必要的資料擷取,或在 MySQL 程序碼中使用暫時性資料表,以便最佳化轉換的 PL/SQL 程式碼。

後續步驟

  • 進一步瞭解 MySQL 使用者帳戶。
  • 探索 Google Cloud 的參考架構、圖表和最佳做法。歡迎瀏覽我們的雲端架構中心