将 Oracle 数据库查询转换为 Cloud SQL for MySQL 查询并对其进行优化

本文档介绍了 Oracle® 和 Cloud SQL for MySQL 之间的基本查询差异,以及 Oracle 中的功能与 Cloud SQL for MySQL 中的功能之间的对应关系。此外,还概述了 Cloud SQL for MySQL 的性能注意事项,以及分析和优化 Google Cloud 查询性能的方法。虽然本文档介绍了为 Cloud SQL for MySQL 优化存储过程和触发器的方法,但并没有介绍如何将 PL/SQL 代码转换为 MySQL 存储过程和函数。

将 Oracle 数据库查询转换为 Cloud SQL for MySQL 查询时,需要考虑某些 SQL 方言差异。此外,还有多个内置函数在两个数据库平台之间不同或不兼容。

基本查询差异

虽然 Oracle 和 Cloud SQL for MySQL 均支持 ANSI SQL,但在查询数据时存在一些基本差异,主要是系统函数的使用。

下表重点介绍了 Oracle 和 Cloud SQL for MySQL 的 SELECTFROM 语法之间的差异。

Oracle 功能名称 Oracle 实现 Cloud SQL for MySQL 支持 Cloud SQL for MySQL 等效函数
用于数据检索的 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。Cloud SQL for MySQL 联接支持在 JOIN 语句中使用备用语法,例如 USING 子句、WHERE 子句(而不是 ON 子句)和 SUBQUERY

下表显示了 JOIN 转换示例。

Oracle JOIN 类型 Cloud SQL for MySQL 支持 Cloud SQL for MySQL 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;

虽然 Cloud SQL for MySQL 同时支持 UNIONUNION ALL 函数,但不支持 Oracle 的 INTERSECTMINUS 函数:

  • UNION 会在附加两个 SELECT 语句的结果集之前消除重复记录。
  • UNION ALL 会附加两个 SELECT 语句的结果集,但不会消除重复记录。
  • 只要两个查询的结果集中都存在同一记录,INTERSECT 就会返回两个 SELECT 语句的交集。
  • MINUS 会比较两个或更多个 SELECT 语句,仅返回第一个查询中未由第二个查询返回的不同行。

下表显示了一些从 Oracle 到 Cloud SQL for MySQL 的转换示例。

Oracle 函数 Oracle 实现 Cloud SQL for MySQL 支持 Cloud SQL for MySQL 等效函数
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

标量函数和组函数

Cloud SQL for MySQL 提供了一个详尽的标量(单行)和聚合函数列表。一些 Cloud SQL for MySQL 函数与其对应的 Oracle 函数类似(通过名称和功能,或使用不同的名称但功能类似)。虽然一些 Cloud SQL for MySQL 函数在名称上可以与其对应的 Oracle 函数相同,但它们也可以展示不同的功能。

下表按名称和功能描述了 Oracle 和 Cloud SQL for MySQL 字符函数等效的场景,以及建议转换的场景。

Oracle 函数 Oracle 实现 Cloud SQL for MySQL 等效函数 Cloud SQL for MySQL 函数 Cloud SQL for MySQL 实现
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 函数接受第二个参数,此参数指定要从字符串中移除的开头或结尾字符。Cloud SQL for MySQL 函数仅从给定字符串中移除开头和结尾空格:
LTRIM(' MySQL') = MySQL
ASCII 接受单个字符并返回其数字形式的 ASCII 码:
ASCII('A') = 65
ASCII ASCII('A') = 65
CHR 返回 ASCII 码值(由介于 0-225 之间的数字值转换为字符):
CHR(65) = A
需要不同的函数名称 CHAR Cloud SQL for MySQL 使用 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 返回模式在源字符串中出现的次数 不适用 Cloud SQL for MySQL 没有等效函数可用。将此逻辑移至应用层。
REGEXP_INSTR 在字符串位置(索引)中搜索正则表达式模式 不适用 从 MySQL 版本 8 开始支持。如果是使用旧版本,请将此逻辑移至应用层。
REVERSE 返回给定字符串的倒序字符串:
REVERSE('MySQL') = LQSyM
REVERSE REVERSE('MySQL') = LQSyM

下表按名称和功能描述了 Oracle 和 Cloud SQL for MySQL 数字函数等效的场景,以及建议转换的场景。

Oracle 函数 Oracle 实现 Cloud SQL for MySQL 等效函数 Cloud SQL for MySQL 函数 Cloud SQL for MySQL 实现
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(number) 返回截断到 n2 个小数位的 n1。第二个参数是可选的。
TRUNC(99.999) = 99 TRUNC(99.999, 0) = 99
需要不同的函数名称 TRUNCATE(number) Cloud SQL for MySQL 函数具有不同的名称,第二个参数是必需参数。
TRUNCATE(99.999, 0) = 99

下表按名称和功能描述了 Oracle 和 Cloud SQL for MySQL datetime 函数等效的场景,以及建议转换的场景。

Oracle 函数 Oracle 实现 Cloud SQL for MySQL 等效函数 Cloud SQL for MySQL 函数 Cloud SQL for MySQL 实现
SYSDATE 返回针对数据库服务器所在操作系统设置的当前日期和时间:
SELECT SYSDATE FROM DUAL; = 31-JUL-2019
SYSDATE()

Cloud SQL for MySQL 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 默认情况下,Cloud SQL for MySQL 函数会返回不同的 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 函数返回与 Oracle 默认格式不同的 datetime 格式。如需重新设置输出格式,请使用 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 Cloud SQL for MySQL 函数返回不同的 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 Cloud SQL for MySQL 函数返回不同的 datetime 格式。如需重新设置输出格式,请使用 DATE_FORMAT() 函数。
SELECT CURRENT_TIMESTAMP FROM DUAL = 2019-01-31 06:55:07
ADD_MONTHS 返回日期加整数月数:
ADD_MONTHS(SYSDATE, 1) = 31-JAN-19
需要不同的函数名称 ADDDATE Cloud SQL for MySQL 函数返回不同的 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 Cloud SQL for MySQL 函数返回与 Oracle 默认格式不同的 datetime 格式。如需重新设置输出格式,请使用 DATE_FORMAT() 函数。
LAST_DAY('2019-01-01') = 2019-01-31
MONTH_ BETWEEN 返回给定日期 date1date2 之间的月数:
MONTHS_BETWEEN( SYSDATE, SYSDATE-60) = 1.96
部分 PERIOD_DIFF 为计算月数之差值,Cloud SQL for MySQL 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 Cloud SQL for MySQL DATE_FORMAT 函数会根据格式字符串设置日期值的格式:
DATE_FORMAT( SYSDATE(),'%d-%m-%Y %H:%i:%s') 01-01-2019 10:01:01

下表按名称和功能描述了 Oracle 和 Cloud SQL for MySQL 编码和解码函数等效的场景,以及建议转换的场景。

Oracle 函数 Oracle 实现 Cloud SQL for MySQL 等效函数 Cloud SQL for MySQL 函数 Cloud SQL for MySQL 实现
DECODE 使用 IF-THEN-ELSE 语句的功能将表达式与每个搜索值逐一进行比较 CASE 使用 Cloud SQL for MySQL CASE 语句实现类似功能
DUMP 返回一个 VARCHAR2 值,此值包含表达式的数据类型代码、长度(字节数)和内部表示 不适用 不支持
ORA_HASH 计算给定表达式的哈希值 MD5 or SHA 为 128 位校验和使用 MD5 函数,或者为 160 位校验和使用 SHA 函数

下表按名称和功能描述了 Oracle 和 Cloud SQL for MySQL 转换函数等效的场景,以及建议转换的场景。

Oracle 函数 Oracle 实现 Cloud SQL for MySQL 等效函数 Cloud SQL for MySQL 函数 Cloud SQL for MySQL 实现
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 Cloud SQL for MySQL CONVERT 函数需要对语法和参数进行一些调整:
CONVERT( 'Ä Ê Í A B C ' USING utf8) = Ä Ê Í A B C
TO_CHAR(字符串/数字) 该函数会将数字或日期转换为字符串:
TO_CHAR(22.73,'$99.9') = $22.7
FORMAT Cloud SQL for MySQL 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 使用 Cloud SQL for MySQL CAST 函数返回与 Oracle TO_NUMBER 函数相同的结果:
CAST('01234' as SIGNED) = 1234

下表按名称和功能描述了 Oracle 和 Cloud SQL for MySQL 条件式 SELECT 函数等效的场景,以及建议转换的场景。

Oracle 函数 Oracle 实现 Cloud SQL for MySQL 等效函数 Cloud SQL for MySQL 函数 Cloud SQL for MySQL 实现
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 和 Cloud SQL for MySQL null 函数等效的场景,以及建议转换的场景。

Oracle 函数 Oracle 实现 Cloud SQL for MySQL 等效函数 Cloud SQL for MySQL 函数 Cloud SQL for MySQL 实现
COALESCE 返回表达式列表中的第一个非 null 表达式:
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 根据表达式是 null 还是非 null 确定查询返回的值 CASE CASE 语句会从一系列条件中进行选择,并运行相应的语句:
CASE WHEN condition THEN result [WHEN ...] [ELSE result] END

下表按名称和功能描述了 Oracle 和 Cloud SQL for MySQL 环境和标识符函数等效的场景,以及建议转换的场景。

Oracle 函数 Oracle 实现 Cloud SQL for MySQL 等效函数 Cloud SQL for MySQL 函数 Cloud SQL for MySQL 实现
SYS_GUID 生成并返回由 16 个字节组成的全局唯一标识符(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 Cloud SQL for MySQL 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 Cloud SQL for MySQL 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 和 Cloud SQL for MySQL 聚合(分组)函数等效的场景,以及建议转换的场景。

Oracle 函数 Oracle 实现 Cloud SQL for MySQL 等效函数 Cloud SQL for MySQL 函数 Cloud SQL for MySQL 实现
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 使用 Cloud SQL for MySQL GROUP_CONCAT 函数返回等效结果:
SELECT GROUP_CONCAT( DEPARTMENT_NAME ORDER BY DEPARTMENT_NAME SEPARATOR ', ') DEPT FROM DEPARTMENTS; -- Single line results = Accounting, Administration, Benefits, Construction

下表按名称和功能描述了 Oracle 和 Cloud SQL for MySQL 的等效 FETCH 函数。

Oracle 函数 Oracle 实现 Cloud SQL for MySQL 等效函数 Cloud SQL for MySQL 函数 Cloud SQL for MySQL 实现
FETCH 从多行查询的结果集中检索指定的行数:
SELECT * FROM EMPLOYEES FETCH FIRST 10 ROWS ONLY;
LIMIT 使用 MySQL LIMIT 子句从查询中检索行:
SELECT * FROM EMPLOYEES LIMIT 10;

基本过滤、运算符和子查询

基本过滤、运算符函数和子查询转换起来相对简单,只需要额定工作量。大部分工作都围绕转换日期格式进行,因为 Oracle 和 Cloud SQL for MySQL 使用不同的默认日期格式:

  • Oracle SYSDATE 函数默认返回以下格式:01-AUG-19
  • Cloud SQL for MySQL SYSDATE() 函数默认返回以下格式:2019-08-01 12:04:05

如需设置日期和时间格式,请使用 MySQL DATE_FORMATSTR_TO_DATE 函数。

下表按名称和功能描述了 Oracle 和 Cloud SQL for MySQL 基本过滤、运算符和子查询函数等效的场景,以及建议转换的场景。

Oracle 函数 Oracle 实现 Cloud SQL for MySQL 等效函数 Cloud SQL for MySQL 函数
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 Cloud SQL for MySQL 支持在 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);
运算符 运算符 Cloud SQL for MySQL 支持所有基本运算符:
> | >= | < | <= | = | <> | !=

Cloud SQL for MySQL 查询的最佳做法

若要使 Cloud SQL for MySQL 和 Oracle 之间的性能水平相当,您可能需要优化查询。这些优化包括更改索引结构和调整数据库架构。本部分提供了一些指南,可帮助您在 Cloud SQL for MySQL 上实现水平相当的查询性能。

创建聚簇索引

使用 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;

调整查询参数

如需调整查询性能,您可能需要调整会话参数。Cloud SQL for MySQL 有一组标志,您可以针对此用途进行更改,包括以下标志:

  • InnoDB 相关参数
  • SORT 个参数
  • JOIN 个参数
  • 缓存处理参数

监控查询

运行缓慢的查询可能会导致系统停止响应或导致其他瓶颈,因此定期监控查询非常重要。

您可以通过以下几种方法诊断运行缓慢的 SQL 语句:

  • 使用 Cloud SQL for MySQL 信息中心,获取有关运行缓慢查询的实时和历史数据分析。
  • 使用 Cloud Monitoring 监控 Cloud SQL for MySQL 慢查询日志。
  • 使用 Cloud SQL for MySQL statement_analysis 视图查看有关 SQL 语句的运行时统计信息:

    mysql> SELECT * FROM sys.statement_analysis;
    

分析 Cloud SQL for MySQL 查询

Cloud SQL for MySQL 中的查询优化器会为 SELECTINSERTUPDATEDELETE 语句生成执行计划。当您调整运行缓慢的查询时,这些计划非常有用。请牢记一些注意事项:

  • 执行计划不是需要迁移的数据库对象;相反,它们是工具,用于分析 Oracle 和 Cloud SQL for MySQL 在针对相同数据集运行相同语句时的性能差异。
  • Cloud SQL for MySQL 不支持与 Oracle 相同的执行计划语法、功能或输出。

以下示例计划说明了 Oracle 执行计划与 Cloud SQL for MySQL 执行计划之间的区别:

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 不同,Cloud SQL for MySQL 存储过程和函数会在每次执行时进行解析。对存储过程和函数性能进行基准化分析的实用工具是 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 代码。

后续步骤