Oracle から Cloud SQL for MySQL への移行: クエリ、ストアド プロシージャ、関数、トリガー

このドキュメントは、Oracle® 11g/12c データベースから Cloud SQL for MySQL バージョン 5.7 の第 2 世代インスタンスへの移行を計画し、実施する際に必要な情報とガイダンスを提供するシリーズの一つです。このシリーズは次のパートから構成されています。

クエリ

Oracle と Cloud SQL for MySQL は ANSI SQL 標準に対応しています。一般に、SQL ステートメントの移行は難しくありません、基本的な構文要素を使用するだけで移行できます。スカラー関数やその他の Oracle 拡張機能を指定する必要はありません。以下では、一般的な Oracle クエリ要素とそれに対応する Cloud SQL for MySQL の要素について説明します。

基本的な SELECT 構文と FROM 構文

Oracle の機能名または構文名 Oracle の概要または実装 MySQL のサポート 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
OR
SELECT 1 FROM DUAL
列のエイリアス
SELECT COL1 AS C1
SELECT COL1 AS C1
OR
SELECT COL1 C1
テーブル名での
大文字と小文字の区別
大文字と小文字は区別しない
(例: テーブル名として ordersORDERS のどちらも使用可能)
× 定義済みのテーブル名に応じて大文字と小文字を区別する(例: テーブル名として ordersORDERS のいずれかを使用可能)。

MySQL の SELECT構文の詳細をご覧ください。

  • インライン ビュー
    • インラインビュー(別名、派生テーブル)は FROM 句に指定される SELECT ステートメントで、サブクエリとして使用されます。
    • インライン ビューを利用して複合計算を削除したり、結合オペレーションを排除したりすると、複雑なクエリをシンプルにできます。また、複数の個別クエリを 1 つの簡素化されたクエリに要約することもできます。
    • 変換に関する注記: Oracle ではインライン ビューにエイリアスを使用する必要はありませんが、MySQL ではインライン ビューごとに固有のエイリアスが必要です。

次の表に、Oracle から MySQL へのインライン ビューの変換例を記載します。

Oracle 11g / 12c
SQL> SELECT FIRST_NAME,
            DEPARTMENT_ID,
            SALARY,
            DATE_COL
     FROM EMPLOYEES,
          (SELECT SYSDATE AS DATE_COL FROM DUAL);

次のような出力になります。
FIRST_NAME           DEPARTMENT_ID     SALARY DATE_COL
-------------------- ------------- ---------- ---------
Steven                          90      24000 30-JUL-19
Neena                           90      17000 30-JUL-19
Lex                             90      17000 30-JUL-19
Cloud SQL for MySQL 5.7
インライン ビューにエイリアスを使用しない場合:
mysql> SELECT FIRST_NAME,
              DEPARTMENT_ID,
              SALARY,
              DATE_COL
       FROM EMPLOYEES, (SELECT SYSDATE() AS DATE_COL FROM DUAL);

ERROR 1248 (42000): Every derived table must have its own alias

インライン ビューにエイリアスを追加した場合:
mysql> SELECT FIRST_NAME,
              DEPARTMENT_ID,
              SALARY,
              DATE_COL
       FROM EMPLOYEES, (SELECT SYSDATE() AS DATE_COL FROM DUAL) AS A1;

次のような出力になります。
+-------------+---------------+----------+---------------------+
| FIRST_NAME  | DEPARTMENT_ID | SALARY   | DATE_COL            |
+-------------+---------------+----------+---------------------+
| Steven      |            90 | 23996.00 | 2019-07-30 09:28:00 |
| Neena       |            90 | 22627.00 | 2019-07-30 09:28:00 |
| Lex         |            90 | 22627.00 | 2019-07-30 09:28:00 |

JOIN ステートメント

Oracle の JOIN ステートメントは、FULL JOIN 句を除き、MySQL の JOIN ステートメントでサポートされています。さらに、MySQL の JOIN ステートメントは代替構文の使用をサポートしています。たとえば、ON 句や JOIN ステートメントで SUBQUERY を使用する代わりに、USING 句や WHERE 句を使用できます。

次の表に、JOIN の変換例を示します。

Oracle の JOIN の種類 MySQL でのサポート 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
× 代わりに、LEFT ステートメントと RIGHT JOIN ステートメントで UNION を使用することを検討してください。
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;

UNION、UNION ALL、INTERSECT、MINUS

MySQL では、UNION 関数と UNION ALL 関数を除き、Oracle の INTERSECT 関数と MINUS 関数はサポートされていません。

  • UNION: 2 つ以上の SELECT ステートメントの結果セットを結合しますが、重複するレコードは除外します。
  • UNION ALL: 重複レコードを除外せずに、複数の SELECT ステートメントの結果セットを結合します。
  • INTERSECT: レコードが両方のデータセットに存在する場合にのみ、2 つ以上の SELECT ステートメントの結果で共通する部分を返します。
  • MINUS: 2 つ以上の SELECT ステートメントを比較し、最初のクエリで他のステートメントから返されない個別の行のみを返します。

変換に関する注記

Oracle の INTERSECT 関数と MINUS 関数を MySQL に変換する場合は、代わりに JOIN ステートメントで INEXISTS を使用します。

Oracle の関数 Oracle の実装 MySQL のサポート 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

スカラー(単一行)関数とグループ化関数

MySQL にはさまざまなスカラー(単一行)関数と集計関数が用意されています。一部の MySQL 関数は、対応する Oracle 関数に非常によく似ています(名前と機能が似ている関数や、名前は異なっていても機能が似ている関数などがあります)。MySQL と Oracle で同じ関数名が使用されていても、機能がそれぞれ異なる場合もあります。

次の表に、Oracle と MySQL の間で名前と機能が同じ関数(「○」で示しています)と、変換が推奨される関数(「○」以外)を示します。

文字関数
Oracle の関数 Oracle 関数の仕様または実装 MySQL の同等関数 MySQL の対応関数 MySQL 関数の仕様または実装
CONCAT(str1,str2)
str1 と str2 を連結した文字列を返します。
CONCAT('A', 1) = A1
CONCAT
Oracle と同等:
CONCAT('A', 1) = A1
LOWER/UPPER
すべての文字を小文字または大文字にして返します。
LOWER('SQL') = sql
LOWER/UPPER
Oracle と同等:
LOWER('SQL') = sql
LPAD/RPAD(expr1,n,expr2)
expr1 の左側または右側に、expr2 で指定した文字を連続的に埋め込んで n 桁にして返します。
LPAD('A',3,'*') = **A
LPAD/RPAD
Oracle と同等:
LPAD('A',3,'*') = **A
SUBSTR(char,p,n)
char の一部分(文字位置 p から n 文字の長さのサブ文字列)を返します。
SUBSTR('MySQL', 3, 3)
= SQL
SUBSTR(char,p,n)
Oracle と同等:
SUBSTR('MySQL', 3, 3)
= SQL
INSTR(index,str)
文字列 str の位置(index)を返します。
INSTR('MySQL', 'y')
= 2
INSTR
Oracle と同等:
INSTR('MySQL', 'y')
= 2
REPLACE(char,str1,str2)
検索文字列を置換文字列で置き換えた char を返します。
REPLACE('ORADB', 'ORA', 'MySQL')

= MySQLDB
REPLACE(char,str1,str2)
Oracle と同等:
REPLACE('ORADB', 'ORA', 'MySQL')

= MySQLDB
TRIM(str)
文字列から先頭または末尾(あるいは両方)の文字を削除します。
TRIM(both '-' FROM '-MySQL-')
= MySQL

TRIM(' MySQL ') = MySQL
TRIM(str)
Oracle と同等:
TRIM(both '-' FROM '-MySQL-')
= MySQL

TRIM(' MySQL ') = MySQL
LTRIM/RTRIM(str)
文字列の左端または右端から、検索で指定されたすべての文字を削除します。
LTRIM('   MySQL', ' ')
= MySQL
部分的
LTRIM/RTRIM(str)
Oracle の R/LTRIM 関数。ただし、パラメータ(空白または文字列)の削除は除きます。MySQL の R/LTRIM は空白文字のみを削除し、入力文字列のみを受け入れます。
LTRIM('   MySQL')
= MySQL
ASCII(char)
データベースの文字セットに含まれる char の最初の文字の 10 進表現を返します。
ASCII('A') = 65
ASCII(char)
Oracle と同等:
ASCII('A') = 65
CHR(char)
ASCII コード値(0~225 の数値)に対応する文字を返します。
CHR(65) = A
関数名の一部が異なる
CHAR(char)
MySQL では同じ機能に CHAR 関数を使用するため、関数名を変更する必要があります。
CHAR(65) = A
LENGTH(str)
指定した文字列の長さを返します。

LENGTH ('MySQL') = 5
LENGTH(str)
Oracle と同等:
LENGTH('MySQL') = 5
REGEXP_REPLACE(str1,expr,str2)
正規表現パターンに一致する文字列を検索します。
REGEXP_REPLACE('John', '[hn].', '1') = Jo1
× なし MySQL バージョン 8 以降でのみサポートされます。可能であれば代わりに REPLACE 関数を使用するか、アプリケーション層に変換してください。
REGEXP_SUBSTR(str,expr)
正規表現パターンに一致する文字列を検索できるように、SUBSTR 関数の機能を拡張したものです。
REGEXP_SUBSTR('https://console.cloud.google.com/sql/instances','https://([[:alnum:]]+\.?){3,4}/?')
= https://console.cloud.google.com/
× なし MySQL バージョン 8 以降でのみサポートされます。可能であれば、代わりに SUBSTR 関数を使用するか、この機能をアプリケーション層に変換してください。
REGEXP_COUNT(str,expr)
ソース文字列でのパターン出現回数を返します。 × なし 代わりに、この機能をアプリケーション層に変換してください。
REGEXP_INSTR(index,expr)
正規表現パターンに一致する文字列の位置(インデックス)を検索します。 × なし MySQL バージョン 8 以降でのみサポートされます。
REVERSE(str)
文字列を反転させて返します。
REVERSE('MySQL')
= LQSyM
REVERSE
Oracle と同等:
REVERSE('MySQL')
= LQSyM
数値関数
Oracle の関数 Oracle 関数の仕様または実装 MySQL の同等関数 MySQL の対応関数 MySQL 関数の仕様または実装
ABS(n)
n の絶対値を返します。
ABS(-4.6) = 4.6
ABS
Oracle と同等:
ABS(-4.6) = 4.6
CEIL(n)
n 以上の最小の整数を返します。
CEIL(21.4) = 22
CEIL
Oracle と同等:
CEIL(21.4) = 22
FLOOR(n)
n 以下の最大の整数を返します。
FLOOR(-23.7) = -24
FLOOR
Oracle と同等:
FLOOR(-23.7) = -24
MOD(m,n)
m を n で割った余りを返します。
MOD(10, 3) = 1
MOD(m,n)
Oracle と同等:
MOD(10,3) = 1
ROUND(m,n)
小数点以下 n 桁に丸めた m を返します。
ROUND(1.39,1) = 1.4
ROUND
Oracle と同等:
ROUND(1.39,1) = 1.4
TRUNC(n1, n2)
n1 を小数点以下 n2 桁に切り捨てた数値を返します。
TRUNC(99.999) = 99
TRUNC(99.999,0) = 99
関数名の一部が異なる
TRUNCATE(n1, n2)
MySQL の TRUNCATE 関数は入力値と、小数点以下の桁数を指定する整数値を受け入れる必要があります。
TRUNCATE(99.999,0) = 99
日付と時刻の関数
Oracle の関数 Oracle 関数の仕様または実装 MySQL の同等関数 MySQL の対応関数 MySQL 関数の仕様または実装
SYSDATE
データベース サーバーが存在するオペレーティング システムに設定されている現在の日付と時刻を返します。
SELECT SYSDATE
FROM DUAL
= 31-JUL-2019
部分的
SYSDATE()
MySQL の SYSDATE() には括弧を含める必要があります。この関数は、Oracle の SYSDATE 関数とは異なる形式の日時を返します。
SELECT SYSDATE()
FROM DUAL;
= 2019-01-31 10:01:01.0

日時形式はセッション レベルで変更できます。
SYSTIMESTAMP
小数点以下の秒とタイムゾーンを含む、システムの日付を返します。
SELECT SYSTIMESTAMP FROM DUAL
= 01-JAN-19 07.37.11.622187000 AM +00:00
関数名の一部が異なる
CURRENT_TIMESTAMP
MySQL は、Oracle とは異なる日付 / 時刻形式を返します。日付の形式は元の日付 / 時刻形式と一致させる必要があります(または別の日付関数を使用する必要があります)。
SELECT CURRENT_TIMESTAMP
FROM DUAL
= 2019-01-31 06:55:07
LOCAL_TIMESTAMP
セッションのタイムゾーンでの現在の日付と時刻を TIMESTAMP データ型の値で返します。
SELECT LOCAL_TIMESTAMP
FROM DUAL
= 01-JAN-19 10.01.10.123456 PM
部分的(日時形式が異なる)
LOCAL_TIMESTAMP
MySQL は、Oracle とは異なる日付 / 時刻形式を返します。日付と時刻の形式は、元の日付と時刻の形式と一致する必要があります(または別の日付関数を使用する必要があります)。
SELECT LOCAL_TIMESTAMP
FROM DUAL
= 2019-01-01 10:01:01.0
CURRENT_DATE
セッションのタイムゾーンでの現在の日付を返します。
SELECT CURRENT_DATE
FROM DUAL
= 31-JAN-19
部分的(日時形式が異なる)
CURRENT_DATE
MySQL は、Oracle とは異なる日付 / 時刻形式を返します。日付と時刻の形式は、元の日付と時刻の形式と一致する必要があります(または別の日付関数を使用する必要があります)。
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
部分的(日時形式が異なる)
CURRENT_TIMESTAMP
MySQL は、Oracle とは異なる日付 / 時刻形式を返します。日付と時刻の形式は、元の日付と時刻の形式と一致する必要があります(または別の日付関数を使用する必要があります)。
SELECT CURRENT_TIMESTAMP
FROM DUAL
= 2019-01-31 06:55:07
ADD_MONTHS
日付と、月を表す整数を返します。
ADD_MONTHS(SYSDATE, 1)
= 31-JAN-19
関数名の一部が異なる
ADDDATE
同じ機能を実現するには、MySQL で ADDDATE 関数を使用します。
ADDDATE(SYSDATE(), 1)
= 2019-08-01 06:42:49.0

デフォルトでは、MySQL は Oracle とは異なる日付 / 時刻と範囲 / 形式を返します。日付と時刻の形式は、元の日付と時刻の形式と一致する必要があります(または別の日付関数を使用する必要があります)。
EXTRACT(date part 日付 / 時刻または期間の式から、指定された日付 / 時刻フィールドの値を返します。
EXTRACT(YEAR FROM DATE '2019-01-31')
= 2019
EXTRACT(date part) Oracle と同等:
EXTRACT(YEAR FROM DATE '2019-01-31')
= 2019
LAST_DAY
月の最終日の日付を返します。
LAST_DAY('01-JAN-2019')
= 31-JAN-19
部分的(日時形式が異なる)
LAST_DAY
MySQL は、Oracle とは異なる日付 / 時刻形式を返します。日付と時刻の形式は、元の日付と時刻の形式と一致する必要があります(または別の日付関数を使用する必要があります)。
LAST_DAY('2019-01-01')
= 2019-01-31
MONTH_BETWEEN
date1 と date2 の間の月数を返します。
MONTHS_BETWEEN(
SYSDATE, SYSDATE-60)
= 1.96
関数名の一部が異なる
PERIOD_DIFF(date1,date2)
MySQL の PERIOD_DIFF 関数は、2 つの月(YYMM または YYYYMM 形式)の差を整数として返します。
PERIOD_DIFF(
'201903', '201901')
= 2

Oracle の MONTH_BETWEEN 関数と同じ値を取得するには、より具体的な変換が必要になります。
TO_CHAR(date/time) 日付形式で指定された形式で、日付 / 時刻またはタイムスタンプのデータ型を VARCHAR2 データ型の値に変換します。
TO_CHAR(
SYSDATE,'DD-MM-YYYY HH24:MI:SS')
= 01-01-2019 10:01:01
関数名の一部が異なる
DATE_FORMAT
MySQL の DATE_FORMAT 関数は、日付を日付形式の定義で指定した形式に設定します。
DATE_FORMAT(
SYSDATE(),'%d-%m-%Y %H:%i:%s')
= 01-01-2019 10:01:01
エンコーディング関数とデコーディング関数
Oracle の関数 Oracle 関数の仕様または実装 MySQL の同等関数 MySQL の対応関数 MySQL 関数の仕様または実装
DECODE
IF-THEN-ELSE ステートメントの機能を使用して、各検索値を 1 つずつ式と比較します。 ×
CASE
同等の機能を実現するには、MySQL CASE ステートメントを使用します。
DUMP
データ型コード、長さ(バイト数)、指定された式の内部表現を含む VARCHAR2 値を返します。 × なし サポートされていません。
ORA_HASH
指定した式のハッシュ値を計算します。 ×
MD5/SHA
ハッシュ値を生成するには、MySQL の MD5 関数(128 ビットのチェックサムの場合)または SHA 関数(160 ビットのチェックサムの場合)を使用します。
変換関数
Oracle の関数 Oracle 関数の仕様または実装 MySQL の同等関数 MySQL の対応関数 MySQL 関数の仕様または実装
CAST
1 つの組み込みデータ型または集合型の値を別の組み込みデータ型または集合型の値に変換します。
CAST('1' as int) + 1
= 2
部分的
CAST
MySQL の CAST 関数は Oracle の関数と同様ですが、必要な変換が明示的か暗黙的かによって調整が必要になることがあります。
CAST('1' AS SIGNED) + 1
= 2
CONVERT
文字列をある文字セットから別の文字セットに変換します。
CONVERT('Ä Ê Í Õ Ø A B C D E ', 'US7ASCII', 'WE8ISO8859P1')
= ?? ?? ?? A B C
部分的
CONVERT
MySQL の CONVERT 関数で Oracle と同じ結果を取得するには、関数の構文とパラメータを調整する必要があります。
CONVERT('Ä Ê Í A B C ' USING utf8)
= Ä Ê Í A B C
TO_CHAR
(string/numeric)
この関数は数値または日付を文字列に変換します。
TO_CHAR(22.73,'$99.9')
= $22.7
×
FORMAT
MySQL の FORMAT 関数は、数値を '#,###.##' の形式に設定して特定の小数点以下の桁数に丸めてから、その結果を文字列として返します。したがって、Oracle とは異なる機能です。
CONCAT('$',
FORMAT(22.73, 1))
= $22.7
TO_DATE
Oracle の TO_DATE 関数は、文字列をソース固有の日付 / 時刻形式の日付に変換します。
TO_DATE(
'2019/01/01', 'yyyy-mm-dd')
= 01-JAN-2019
部分的(関数名と日付 / 時刻形式が異なる)
STR_TO_DATE
MySQL の STR_TO_DATE 関数は文字列を取り、日付 / 時刻形式で指定された日付を返します。
STR_TO_DATE(
'2019/01/01', '%Y/%m/%d')
= 2019-01-01
TO_NUMBER
式を NUMBER データ型の値に変換します。
TO_NUMBER('01234')
= 1234
×
CAST
Oracle の TO_NUMBER と同じ結果を返すには、代わりに MySQL の CAST 関数を使用します。
CAST('01234' as SIGNED)
= 1234
条件付き SELECT 関数
Oracle の関数 Oracle 関数の仕様または実装 MySQL の同等関数 MySQL の対応関数 MySQL 関数の仕様または実装
CASE
CASE ステートメントは、一連の条件に基づいて対応するステートメントを選択して実行します。構文は次のとおりです。
CASE WHEN condition THEN result
[WHEN ...] [ELSE result]
END
CASE
MySQL では、CASE 関数だけでなく、SELECT ステートメント内で IF/ELSE を使用して条件付きの処理を行うこともできます。
CASE WHEN condition THEN result
[WHEN ...] [ELSE result]
END
null 関数
Oracle の関数 Oracle 関数の仕様または実装 MySQL の同等関数 MySQL の対応関数 MySQL 関数の仕様または実装
COALESCE
式のリスト内の最初の null 以外の式を返します。
COALESCE(
null, '1', 'a')
= a
COALESCE
Oracle と同等:
COALESCE(
null, '1', 'a')
= 1
NULLIF
expr1 と expr2m を比較します。同じ式である場合は null を返します。同じ式ではない場合は、expr1 を返します。
NULLIF('1', '2')
= a
NULLIF
Oracle と同等:
NULLIF('1', '2')
= a
NVL
null(空白として返される)をクエリ結果に含まれる文字列で置き換えます。
NVL(null, 'a')
= a
×
IFNULL
MySQL の同等の関数は IFNULL 関数です。これは、null 値を指定された文字列で置き換えます。
IFNULL(null, 'a')
= a
NVL2
指定された式が null または null 以外であるかどうかによって、クエリで返す値を決定します。
×
CASE

CASE ステートメントは、一連の条件を基に対応するステートメントを選択して実行します。
CASE WHEN condition THEN result
[WHEN ...] [ELSE result]
END
環境関数と ID 関数
Oracle の関数 Oracle 関数の仕様または実装 MySQL の同等関数 MySQL の対応関数 MySQL 関数の仕様または実装
SYS_GUID
最大 16 バイトからなる、グローバルに一意の ID(RAW 値)を生成して返します。
SELECT SYS_GUID()
FROM DUAL
= 8EFA4A31468B4C6DE05011AC0200009E
× REPLACEUUID Oracle の SYS_GUID 関数をシミュレートするには、代わりに MySQL の REPLACE 関数と UUID 関数を使用します。
REPLACE(
UUID(), '-', '')
UID
セッション ユーザー(ログオンしているユーザー)を
一意に識別する整数を返します。
SELECT UID FROM DUAL
= 43
× なし なし
USER
現在のセッション ユーザー名を返します。
SELECT USER FROM DUAL
= UserName
部分的
USER + INSTR + SUBSTR
MySQL の USER 関数は、ユーザー名と接続サーバー(root@IP)を返します。ユーザー名のみを取得するには、サポート関数を追加で使用します。
SELECT
SUBSTR(USER(), 1, INSTR(USER(), '@') -1) FROM DUAL
= root
USERENV
現在のユーザー セッションに関する情報と現在のパラメータ構成を返します。
SELECT USERENV('LANGUAGE')
FROM DUAL
= ENGLISH_AMERICA.AL32UTF8
×
SHOW SESSION
VARIABLES
MySQL の SHOW SESSION
VARIABLES ステートメントを使用して、現在のセッションの設定を表示します。
SHOW SESSION VARIABLES LIKE '%collation%';
= utf8_general_ci
ROWID
Oracle サーバーは各テーブルの行ごとに、テーブル内でその行を識別する一意の ROWID を割り当てます。ROWID は行のアドレスであり、データ オブジェクト番号、行のデータブロック、行の位置、データファイルからなります。 × なし 可能であれば、他の MySQL 関数を使用して同じ機能をエミュレートするようにしてください。
ROWNUM
Oracle によりテーブルまたは結合テーブルから行が選択される順番を表す数値を返します。 × なし 可能であれば、他の MySQL 関数またはセッション変数を使用して同じ機能をエミュレートするようにしてください。
集計(グループ化)関数
Oracle の関数 Oracle 関数の仕様または
実装
MySQL の
同等関数
MySQL の対応関数 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
Oracle と同様の結果を取得するには、MySQL の GROUP_CONCAT 関数を使用します。ただし、構文が異なる場合があります。
SELECT GROUP_CONCAT(
DEPARTMENT_NAME ORDER BY DEPARTMENT_NAME SEPARATOR ', ') DEPT
FROM DEPARTMENTS;

-- Single line results
= Accounting, Administration, Benefits, Construction
Oracle 12c でのフェッチ
Oracle の関数 Oracle 関数の仕様または
実装
MySQL の
同等関数
MySQL の対応関数 MySQL 関数の仕様または実装
FETCH
複数行クエリの結果セットからデータ行を取得します。
SELECT * FROM
EMPLOYEES
FETCH FIRST 10 ROWS ONLY;
× LIMIT 特定のレコードセットのみを取得するには、MySQL の LIMIT 句を使用します。
SELECT * FROM
EMPLOYEES
LIMIT 10;

基本的なフィルタリング、演算子、サブクエリ

基本的なフィルタリング、演算子関数、サブクエリは、最小限の追加の作業を行うだけで比較的簡単に変換できます。

変換に関する注記

デフォルトで返される日付形式は Oracle と MySQL で異なるため、日付の形式を確認し、必要な処置を行う必要があります。

  • Oracle の SYSDATE 関数はデフォルトで 01-AUG-19 を返します。
  • MySQL の SYSDATE() 関数はデフォルトで 2019-08-01 12:04:05 を返します。
  • 日付 / 時刻形式を設定するには、MySQL の [DATE_FORMAT](https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_date-format) 関数または [STR_TO_DATE](https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_str-to-date) 関数を使用します。
Oracle の関数またはサブクエリ MySQL の同等関数 MySQL の対応関数またはサブクエリ 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
MySQL では、JOIN ステートメントを使用する場合と WHERE/AND 句でフィルタリングを行う場合に、SELECT レベルでサブクエリを使用できます。
-- 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 は、基本的な演算子をすべてサポートしています。
> | >= | < | <= | = | <> | !=

分析関数(またはウィンドウ関数とランキング関数)

Oracle の分析関数では、標準 SQL の分析関数の機能を拡張して、行のグループに基づいて集約値を計算できます。これらの関数は、単一のクエリ式の範囲内で論理的に分割された結果セットに適用できます。これらの関数は一般にビジネス インテリジェンス レポートおよびアナリティクスと組み合わせて使用されます。分析的ではない複雑な SQL コードの代わりに分析関数を使用すると、クエリのパフォーマンスが向上する可能性があります。

変換に関する注記

  • MySQL バージョン 5.7 には、単純な SQL ステートメントの変換をサポートする分析関数はありません。ただし、この機能は部分的に MySQL バージョン 8 に追加されているため、分析関数の変換に留意する必要があります。移行プロセス全体で手動による追加作業が必要となる可能性があるためです。
  • オプションのソリューションとして、分析関数を使用しないようにコードを作成し直して従来の SQL コード ソリューションに戻すことができます。また、このロジックをアプリケーション層に移すこともできます。

次の表に、Oracle の一般的な分析関数を示します。

関数ファミリー 関連する関数 MySQL 5.7 での対応状況
分析とランキング
RANK
AVERAGE_RANK
DENSE_RANK
RANK ROW_NUMBER
PERCENT_RANK
CUME_DIST
NTILE
FIRST_VALUE
LAST_VALUE
OVER (PARTITION BY...)
×
階層表示
CONNECT BY
HIER_ANCESTOR
HIER_CHILD_COUNT
HIER_DEPTH
HIER_LEVEL
HIER_ORDER
HIER_PARENT
HIER_TOP
×
ラグ
LAG
LAG_VARIANCE LAG_VARIANCE_PERCENT LEAD
LEAD_VARIANCE LEAD_VARIANCE_PERCENT
×

共通テーブル式(CTE)

CTE では、一連のコードのロジックを実装して、複雑すぎるか何度も使用するには効率的ではないと考えられる SQL コードを再利用できます。CTE に名前を付けた後、SQL ステートメントのさまざまな部分で WITH 句を使って CTE を複数回使用できます。

変換に関する注記

  • CTE は MySQL バージョン 5.7 ではサポートされませんが、MySQL バージョン 8 ではサポートされます。
  • 代わりに、派生テーブル / サブクエリを使用するか、CTE 機能を使用しないように SQL ステートメントを作成し直してください。

Oracle
WITH DEPT_COUNT
(DEPARTMENT_ID, DEPT_COUNT) AS
(SELECT DEPARTMENT_ID,
        COUNT(*)
 FROM EMPLOYEES
 GROUP BY DEPARTMENT_ID)

SELECT E.FIRST_NAME ||' '|| E.LAST_NAME AS EMP_NAME, D.DEPT_COUNT AS EMP_DEPT_COUNT FROM EMPLOYEES E JOIN DEPT_COUNT D USING (DEPARTMENT_ID) ORDER BY 2 DESC;
MySQL
SELECT * FROM (
SELECT CONCAT(E.FIRST_NAME, ' ', E.LAST_NAME) AS EMP_NAME,
       (SELECT COUNT(*)
        FROM EMPLOYEES D
        WHERE E.DEPARTMENT_ID = D.DEPARTMENT_ID
        GROUP BY DEPARTMENT_ID) AS EMP_DEPT_COUNT
FROM EMPLOYEES E
ORDER BY 2 DESC) TBL
WHERE EMP_DEPT_COUNT IS NOT NULL;

MERGE ステートメント

MERGE(または UPSERT)ステートメントを使用すると、1 つの DML オペレーションを別個に実行するのではなく、1 つの MERGE オペレーションの中で条件付きで複数の DML オペレーションを実行する単一の SQL ステートメントを指定できます。ソーステーブルからレコードを選択した後、論理構造を指定することによって、ターゲット テーブルに対して複数の DML オペレーションを自動的に実行します。この機能を使用すると、挿入、更新、削除を何度も行う必要がありません。MERGE は確定的なステートメントです。つまり、MERGE ステートメントで行が処理された後、その行を同じ MERGE ステートメントで再処理することはできません。

変換に関する注記

Oracle とは異なり、MySQL バージョン 5.7 では MERGE 機能をサポートしていません。MERGE 機能を部分的にシミュレートするために、MySQL には REPLACE ステートメントと INSERT… ON DUPLICATE KEY UPDATE ステートメントが用意されています。

  • REPLACE: INSERT ステートメントと同じように機能しますが、テーブル内の古い行に新しい行の PRIMARY KEY または UNIQUE インデックスと同じ値が設定されている場合、古い行が削除されてから新しい行が挿入されます。

  • INSERT… ON DUPLICATE KEY UPDATE: 挿入された行によって PRIMARY KEY または UNIQUE インデックスの値が重複する場合、重複するキーによる例外が発生しないよう、古い行の UPDATE が行われます。次に例を示します。

    INSERT INTO tbl (a,b,c) VALUES (1,2,3)
      ON DUPLICATE KEY UPDATE c=c+1;
    
    UPDATE tbl SET c=c+1 WHERE a=1;
    

代わりに、MERGE 機能をストアド プロシージャに変換し、例外 / 重複の処理で INSERTUPDATEDELETE コマンドを使用して DML オペレーションを管理することもできます。

SQL ステートメントのヒント

Oracle では、クエリ実行計画の効率化を目的に、ユーザーが豊富な SQL クエリヒントを使用してオプティマイザーの動作と決定に影響を与えられるようになっています。Oracle のデータベースでは 60 種類を超えるヒントがサポートされていますが、MySQL で使用できるクエリヒントの数は限られています。

MySQL バージョン 5.7 では通常、OPTIMIZER HINTSINDEX HINTS の 2 種類のクエリヒントをサポートしています。MySQL オプティマイザーのヒントを使用すると、個々の SQL ステートメントでオプティマイザーの動作を制御できます。例:

SELECT /*+ NO_RANGE_OPTIMIZATION(tbl PRIMARY, col1_idx) */ col1 FROM tbl;

MySQL バージョン 5.7 で使用できるオプティマイザー ヒント

ヒント名 ヒントの概要 適用範囲
BKA, NO_BKA
BKA(Batched Key Access)結合処理に影響を与えます。 クエリブロック、テーブル
BNL, NO_BNL
BNL(Block Nested-Loop)結合処理に影響を与えます。 クエリブロック、テーブル
MAX_EXECUTION_TIME
ステートメントの実行時間を制限します。 グローバル
MRR, NO_MRR
MRR(Multi-Range Read)最適化に影響を与えます。 テーブル、インデックス
NO_ICP
ICP(Index Condition Pushdown)最適化に影響を与えます。 テーブル、インデックス
NO_RANGE_OPTIMIZATION
範囲最適化に影響を与えます。 テーブル、インデックス
QB_NAME
クエリブロックに名前を割り当てます。 クエリブロック
SEMIJOIN, NO_SEMIJOIN
準結合戦略に影響を与えます。 クエリブロック
SUBQUERY
マテリアル化、INEXISTS のサブクエリ戦略に影響を与えまます。 クエリブロック

MySQL のインデックス ヒントは、クエリ処理中のインデックスの選択方法に関する情報をオプティマイザーに提供します。USEFORCE、または IGNORE キーワードを使用して、オプティマイザーによるインデックスの使用プロセスを制御します。次に例を示します。

SELECT * FROM tbl USE INDEX (col1_index, col2_index);
-- OR
SELECT * FROM tbl IGNORE INDEX (col1_index, col2_index);

変換に関する注記

Oracle と MySQL オプティマイザーは根本的に異なり、Oracle と MySQL のクエリヒントの重複部分は限られているため、クエリヒントが指定されていないすべての Oracle SQL ステートメントをターゲット MySQL データベースに変換することをおすすめします。

MySQL のツール(リアルタイム パフォーマンス ダッシュボードの MySQL Workbench など)と機能を使用して、MySQL のパフォーマンスを調整してください。たとえば、実行計画を使ってクエリを検査し、ユースケースに応じてインスタンス / セッション パラメータを調整します。

実行計画

実行計画の主な目的は、クエリ オプティマイザーがデータベースのデータにアクセスする際に行った選択についての詳細を提供することです。クエリ オプティマイザーによって、データベース ユーザー用に SELECTINSERTUPDATEDELETE の各ステートメントの実行計画が生成されます。また、管理者は特定のクエリや DML オペレーションについての理解を深めることができます。これは、クエリのパフォーマンスを調整する場合に非常に有効です。たとえば、インデックスのパフォーマンスを確認する場合や、作成する必要があるインデックスがあるかどうかを判断する場合に役立ちます。

実行計画は、データ量、データ統計、インスタンス パラメータ(グローバル パラメータまたはセッション パラメータ)によって影響される可能性があります。

変換に関する考慮事項

実行計画は移行の必要があるデータベース オブジェクトではありません。これは、同じデータセットに対して同じステートメントを実行する場合に、Oracle と MySQL の間のパフォーマンスの違いを分析するためのツールです。

MySQL でサポートしている実行計画の構文、機能、出力は、Oracle とは異なります。

Oracle の実行計画
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 の実行計画
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 | +----+-------------+-----------+------------+-------+---------------+---------+---------+-------+------+----------+-------+

ストアド プロシージャ、関数、トリガー

PL/SQL は Oracle の拡張プロシージャ言語であり、データベース内でコードベースのソリューションを作成、保管、適用するために使用されます。通常、データベースのストアド プロシージャと関数は、Oracle の PL/SQL、PostgreSQL の PL/pgSQL、MySQL の MySQL プロシージャ言語などの ANSI SQL および SQL 拡張プロシーシャ言語からなるコード要素です。MySQL では固有の拡張プロシージャ言語にデータベースと同じ名前を使用しています。

これらのストアド プロシージャと関数の目的は、パフォーマンス、互換性、セキュリティなどの要件に応じて、アプリケーションからではなくデータベース内から実行するのに適したソリューションを提供することです。ストアド プロシージャと関数では両方とも PL/SQL を使用しますが、ストアド プロシージャは主に DDL/DML オペレーションを実行するために使用されます。関数は主に、計算を行って特定の結果を返すために使用されます。

PL/SQL から MySQL へのプロシージャ言語の移行

Oracle PL/SQL から MySQL へのコードの移行という観点では、MySQL プロシージャの実装は Oracle の実装と異なります。したがって、コードを移行するには Oracle の PL/SQL 機能を MySQL のストアド プロシージャと関数に変換する必要があります。さらに、Oracle のパッケージとパッケージ本体は MySQL ではサポートされていないため、コードを変換する際は、これらの要素を個々の MySQL コードに変換(または解析)する必要があります。MySQL のストアド プロシージャと関数はルーティンとも呼ばれます。

コード オブジェクトのオーナー

Oracle では、ストアド プロシージャまたは関数のオーナーは特定のユーザーですが、MySQL ではデータベース ユーザーがデータベースで作成した特定のスキーマです。

コード オブジェクトの権限とセキュリティ

Oracle でストアド プロシージャまたは関数を作成するには、CREATE PROCEDURE システム権限が必要です(他のユーザーでプロシージャまたは関数を作成するには、CREATE ANY PROCEDURE 権限が必要です)。ストアド プロシージャまたは関数を実行するには、データベース ユーザーに EXECUTE 権限が必要です。

MySQL では、コード要素を作成するには CREATE ROUTINE 権限が必要です。また、実行するには EXECUTE 権限が必要です。MYSQL の DEFINER 句は、ユーザーをコード オブジェクトの作成者として定義しますが、そのユーザーには CREATE ROUTINE 権限などの適切な権限が必要です。

MySQL のストアド プロシージャと関数の構文

次の例は、MySQL のストアド プロシージャと関数の構文を示しています。

CREATE
    [DEFINER = user]
    PROCEDURE sp_name ([proc_parameter[,...]])
    [characteristic ...] routine_body

CREATE
    [DEFINER = user]
    FUNCTION sp_name ([func_parameter[,...]])
    RETURNS type
    [characteristic ...] routine_body

proc_parameter:
    [ IN | OUT | INOUT ] param_name type

func_parameter:
    param_name type

type:
    Any valid MySQL data type

characteristic:
    COMMENT 'string'
  | LANGUAGE SQL
  | [NOT] DETERMINISTIC
  | { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA }
  | SQL SECURITY { DEFINER | INVOKER }

routine_body:
    Valid SQL routine statement