将 Oracle 转换为 Amazon RDS for MySQL 或 Amazon Aurora MySQL - AWS Schema Conversion Tool

本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。

将 Oracle 转换为 Amazon RDS for MySQL 或 Amazon Aurora MySQL

要在转换后的 MySQL 代码中模拟 Oracle 数据库函数,请使用 AWS SCT 中的 Oracle 到 MySQL 扩展包。有关扩展包的更多信息,请参阅使用 AWS SCT 扩展包

将 MySQL 用作目标数据库的权限

下面列出了将 MySQL 用作目标所需的权限:

  • CREATE ON *.*

  • ALTER ON *.*

  • DROP ON *.*

  • INDEX ON *.*

  • REFERENCES ON *.*

  • SELECT ON *.*

  • CREATE VIEW ON *.*

  • SHOW VIEW ON *.*

  • TRIGGER ON *.*

  • CREATE ROUTINE ON *.*

  • ALTER ROUTINE ON *.*

  • EXECUTE ON *.*

  • CREATE TEMPORARY TABLES ON *.*

  • AWS_LAMBDA_ACCESS

  • INSERT, UPDATE ON AWS_ORACLE_EXT.*

  • INSERT, UPDATE, DELETE ON AWS_ORACLE_EXT_DATA.*

如果使用版本 5.7 或更低版本的 MySQL 数据库作为目标,请授予 INVOKE LAMBDA *.* 权限,而不是 AWS_LAMBDA_ACCESS。对于 8.0 及更高版本的 MySQL 数据库,请授予 AWS_LAMBDA_ACCESS 权限。

您可以使用以下代码示例创建数据库用户并授予权限。

CREATE USER 'user_name' IDENTIFIED BY 'your_password'; GRANT CREATE ON *.* TO 'user_name'; GRANT ALTER ON *.* TO 'user_name'; GRANT DROP ON *.* TO 'user_name'; GRANT INDEX ON *.* TO 'user_name'; GRANT REFERENCES ON *.* TO 'user_name'; GRANT SELECT ON *.* TO 'user_name'; GRANT CREATE VIEW ON *.* TO 'user_name'; GRANT SHOW VIEW ON *.* TO 'user_name'; GRANT TRIGGER ON *.* TO 'user_name'; GRANT CREATE ROUTINE ON *.* TO 'user_name'; GRANT ALTER ROUTINE ON *.* TO 'user_name'; GRANT EXECUTE ON *.* TO 'user_name'; GRANT CREATE TEMPORARY TABLES ON *.* TO 'user_name'; GRANT AWS_LAMBDA_ACCESS TO 'user_name'; GRANT INSERT, UPDATE ON AWS_ORACLE_EXT.* TO 'user_name'; GRANT INSERT, UPDATE, DELETE ON AWS_ORACLE_EXT_DATA.* TO 'user_name';

在前面的示例中,将 user_name 替换为用户名。然后,将 your_password 替换为安全密码。

如果使用版本 5.7 或更低版本的 MySQL 数据库作为目标,请使用 GRANT INVOKE LAMBDA ON *.* TO 'user_name',而非 GRANT AWS_LAMBDA_ACCESS TO 'user_name'

要使用 Amazon RDS for MySQL 或 Aurora MySQL 作为目标,请将 lower_case_table_names 参数设置为 1。此值意味着 MySQL 服务器在处理表、索引、触发器和数据库等对象名称的标识符时不区分大小写。如果目标实例中已开启二进制日志记录,请将 log_bin_trust_function_creators 参数设置为 1。在这种情况下,您无需使用 DETERMINISTICREADS SQL DATANO SQL 特性创建存储函数。要配置这些参数,请创建新的数据库参数组或修改现有数据库参数组。

Oracle 到 MySQL 的转换设置

要编辑 Oracle 到 MySQL 的转换设置,请选择 AWS SCT 中的设置,然后选择转换设置。从上面的列表中选择 Oracle,然后选择 Oracle – MySQL。AWS SCT 显示了 Oracle 到 MySQL 转换的所有可用设置。

AWS SCT 中的 Oracle 到 MySQL 转换设置包括以下各项的选项:

  • 限制转换后的代码中操作项的注释数量。

    对于在转换后的代码中,为所选严重性级别及更高级别的操作项添加注释,请选择操作项的严重性。AWS SCT 会在转换后的代码中为所选严重性级别及更高级别的操作项添加注释。

    例如,要最大限度地减少转换后的代码中的注释数量,请选择仅错误。要在转换后的代码中包含所有操作项的注释,请选择所有消息

  • 为了解决源 Oracle 数据库可以使用 ROWID 伪列但是 MySQL 不支持类似功能的问题,AWS SCT 可以在转换后的代码中模拟 ROWID 伪列。为此,请为生成行 ID?选择生成为身份

    如果源 Oracle 代码不使用 ROWID 伪列,请为生成行 ID?选择不生成。在这种情况下,转换后的代码运行更快。

  • 在 Oracle 源代码中包含带有 MySQL 不支持的参数的 TO_CHARTO_DATETO_NUMBER 函数时使用该代码。默认情况下,AWS SCT 会在转换后的代码中模拟这些参数的用法。

    当源 Oracle 代码仅包含 PostgreSQL 支持的参数时,您可以使用原生 MySQL TO_CHARTO_DATETO_NUMBER 函数。在这种情况下,转换后的代码运行更快。要仅包含这些参数,请选择以下值:

    • 函数 TO_CHAR() 不使用 Oracle 特定的格式化字符串

    • 函数 TO_DATE() 不使用 Oracle 特定的格式化字符串

    • 函数 TO_NUMBER() 不使用 Oracle 特定的格式化字符串

  • 解决数据库和应用程序在不同的时区运行的问题。默认情况下,AWS SCT 在转换后的代码中模拟时区。但是,当数据库和应用程序使用相同的时区时,您不需要这种模拟。在这种情况下,选择客户端时区与服务器端时区相匹配

迁移注意事项

将 Oracle 转换为 RDS for MySQL 或 Aurora MySQL 时,要更改语句的运行顺序,您可以使用 GOTO 语句和标签。将跳过 GOTO 语句后的任何 PL/SQL 语句并从标签位置继续处理。可在过程、批处理或语句块中的任意位置使用 GOTO 语句和标签。GOTO 语句也可以嵌套。

MySQL 不使用 GOTO 语句。当 AWS SCT 转换包含 GOTO 语句的代码时,它将转换此语句以使用 BEGIN…ENDLOOP…END LOOP 语句。

在下表中可以找到有关 AWS SCT 如何转换 GOTO 语句的示例。

Oracle 语句 MySQL 语句
BEGIN .... statement1; .... GOTO label1; statement2; .... label1: Statement3; .... END
BEGIN label1: BEGIN .... statement1; .... LEAVE label1; statement2; .... END; Statement3; .... END
BEGIN .... statement1; .... label1: statement2; .... GOTO label1; statement3; .... statement4; .... END
BEGIN .... statement1; .... label1: LOOP statement2; .... ITERATE label1; LEAVE label1; END LOOP; statement3; .... statement4; .... END
BEGIN .... statement1; .... label1: statement2; .... statement3; .... statement4; .... END
BEGIN .... statement1; .... label1: BEGIN statement2; .... statement3; .... statement4; .... END; END

将 Oracle 中的 WITH 语句转换为 RDS for MySQL 或 Amazon Aurora MySQL

您可使用 Oracle 中的 WITH 子句(subquery_factoring)将名称(query_name)分配到子查询块。然后,您可以通过指定查询名称来引用查询中的子查询块多位置。如果子查询块不包含链接或参数(本地、过程、函数、包),则 AWS SCT 会将子句转换为视图或临时表。

将子句转换为临时表的好处是,对子查询的重复引用可能更有效。效率之所以更高,是因为可以轻松地从临时表中检索数据,而不是每次引用都需要数据。您可以通过使用额外的视图或临时表模拟。视图名称使用格式 <procedure_name>$<subselect_alias>

您可以在下表中找到示例。

Oracle 语句 MySQL 语句
CREATE PROCEDURE TEST_ORA_PG.P_WITH_SELECT_VARIABLE_01 (p_state IN NUMBER) AS l_dept_id NUMBER := 1; BEGIN FOR cur IN (WITH dept_empl(id, name, surname, lastname, state, dept_id) AS ( SELECT id, name, surname, lastname, state, dept_id FROM test_ora_pg.dept_employees WHERE state = p_state AND dept_id = l_dept_id) SELECT id,state FROM dept_empl ORDER BY id) LOOP NULL; END LOOP;
CREATE PROCEDURE test_ora_pg.P_WITH_SELECT_VARIABLE_01(IN par_P_STATE DOUBLE) BEGIN DECLARE var_l_dept_id DOUBLE DEFAULT 1; DECLARE var$id VARCHAR (8000); DECLARE var$state VARCHAR (8000); DECLARE done INT DEFAULT FALSE; DECLARE cur CURSOR FOR SELECT ID, STATE FROM (SELECT ID, NAME, SURNAME, LASTNAME, STATE, DEPT_ID FROM TEST_ORA_PG.DEPT_EMPLOYEES WHERE STATE = par_p_state AND DEPT_ID = var_l_dept_id) AS dept_empl ORDER BY ID; DECLARE CONTINUE HANDLER FOR NOT FOUND SET done := TRUE; OPEN cur; read_label: LOOP FETCH cur INTO var$id, var$state; IF done THEN LEAVE read_label; END IF; BEGIN END; END LOOP; CLOSE cur; END;
CREATE PROCEDURE TEST_ORA_PG.P_WITH_SELECT_REGULAR_MULT_01 AS BEGIN FOR cur IN ( WITH dept_empl AS ( SELECT id, name, surname, lastname, state, dept_id FROM test_ora_pg.dept_employees WHERE state = 1), dept AS (SELECT id deptid, parent_id, name deptname FROM test_ora_pg.department ) SELECT dept_empl.*,dept.* FROM dept_empl, dept WHERE dept_empl.dept_id = dept.deptid ) LOOP NULL; END LOOP;
CREATE VIEW TEST_ORA_PG.`P_WITH_SELECT_REGULAR_MULT_01$dept_empl `(id, name, surname, lastname, state, dept_id) AS (SELECT id, name, surname, lastname, state, dept_id FROM test_ora_pg.dept_employees WHERE state = 1); CREATE VIEW TEST_ORA_PG.`P_WITH_SELECT_REGULAR_MULT_01$dept `(deptid, parent_id,deptname) AS (SELECT id deptid, parent_id, name deptname FROM test_ora_pg.department); CREATE PROCEDURE test_ora_pg.P_WITH_SELECT_REGULAR_MULT_01() BEGIN DECLARE var$ID DOUBLE; DECLARE var$NAME VARCHAR (30); DECLARE var$SURNAME VARCHAR (30); DECLARE var$LASTNAME VARCHAR (30); DECLARE var$STATE DOUBLE; DECLARE var$DEPT_ID DOUBLE; DECLARE var$deptid DOUBLE; DECLARE var$PARENT_ID DOUBLE; DECLARE var$deptname VARCHAR (200); DECLARE done INT DEFAULT FALSE; DECLARE cur CURSOR FOR SELECT dept_empl.*, dept.* FROM TEST_ORA_PG.`P_WITH_SELECT_REGULAR_MULT_01$dept_empl ` AS dept_empl, TEST_ORA_PG.`P_WITH_SELECT_REGULAR_MULT_01$dept ` AS dept WHERE dept_empl.DEPT_ID = dept.DEPTID; DECLARE CONTINUE HANDLER FOR NOT FOUND SET done := TRUE; OPEN cur; read_label: LOOP FETCH cur INTO var$ID, var$NAME, var$SURNAME, var$LASTNAME, var$STATE, var$DEPT_ID, var$deptid, var$PARENT_ID, var$deptname; IF done THEN LEAVE read_label; END IF; BEGIN END; END LOOP; CLOSE cur; END; call test_ora_pg.P_WITH_SELECT_REGULAR_MULT_01()
CREATE PROCEDURE TEST_ORA_PG.P_WITH_SELECT_VAR_CROSS_02(p_state IN NUMBER) AS l_dept_id NUMBER := 10; BEGIN FOR cur IN ( WITH emp AS (SELECT id, name, surname, lastname, state, dept_id FROM test_ora_pg.dept_employees WHERE dept_id > 10 ), active_emp AS ( SELECT id FROM emp WHERE emp.state = p_state ) SELECT * FROM active_emp ) LOOP NULL; END LOOP; END;
CREATE VIEW TEST_ORA_PG.`P_WITH_SELECT_VAR_CROSS_01$emp `(id, name, surname, lastname, state, dept_id) AS (SELECT id, name, surname, lastname, state, dept_id FROM TEST_ORA_PG.DEPT_EMPLOYEES WHERE DEPT_ID > 10); CREATE PROCEDURE test_ora_pg.P_WITH_SELECT_VAR_CROSS_02(IN par_P_STATE DOUBLE) BEGIN DECLARE var_l_dept_id DOUBLE DEFAULT 10; DECLARE var$ID DOUBLE; DECLARE done INT DEFAULT FALSE; DECLARE cur CURSOR FOR SELECT * FROM (SELECT ID FROM TEST_ORA_PG. `P_WITH_SELECT_VAR_CROSS_01$emp` AS emp WHERE emp.STATE = par_p_state) AS active_emp; DECLARE CONTINUE HANDLER FOR NOT FOUND SET done := TRUE; OPEN cur; read_label: LOOP FETCH cur INTO var$ID; IF done THEN LEAVE read_label; END IF; BEGIN END; END LOOP; CLOSE cur; END;