Dokumen ini adalah bagian dari sebuah seri yang menyajikan informasi dan panduan penting terkait perencanaan dan pelaksanaan migrasi database Oracle® 11g/12c ke Cloud SQL untuk MySQL versi 5.7, instance generasi kedua. Seri ini mencakup bagian-bagian berikut:
- Memigrasikan pengguna Oracle ke Cloud SQL untuk MySQL: Terminologi dan fungsionalitas
- Memigrasikan pengguna Oracle ke Cloud SQL untuk MySQL: Jenis data, pengguna, dan tabel
- Memigrasikan pengguna Oracle ke Cloud SQL untuk MySQL: Kueri, serta prosedur, fungsi, dan pemicu tersimpan (dokumen ini)
- Memigrasikan pengguna Oracle ke Cloud SQL untuk MySQL: Keamanan, operasi, pemantauan, dan logging
Kueri
Oracle dan Cloud SQL untuk MySQL mendukung standar ANSI SQL. Migrasi pernyataan SQL secara umum sangat mudah dilakukan, cukup dengan menggunakan elemen sintaksis dasar (tanpa perlu menentukan fungsi skalar atau fitur tambahan Oracle lainnya). Bagian berikut membahas elemen kueri yang umum di Oracle dan padanan terkaitnya di Cloud SQL untuk MySQL.
Sintaksis SELECT dan FROM dasar
| Nama fitur atau nama sintaksis di Oracle | Ringkasan atau implementasi di Oracle | Dukungan di MySQL | Solusi terkait atau alternatif di MySQL |
|---|---|---|---|
| Sintaksis dasar SQL untuk pengambilan data | SELECT FROM WHERE GROUP BY HAVING ORDER BY |
Ya | SELECT FROM WHERE GROUP BY HAVING ORDER BY |
SELECT untuk cetak output |
SELECT 1 FROM DUAL |
Ya | SELECT 1 OR SELECT 1 FROM DUAL |
| Alias kolom | SELECT COL1 AS C1 |
Ya | SELECT COL1 AS C1 OR SELECT COL1 C1 |
| Kepekaan huruf besar/kecil nama tabel |
Tidak peka huruf besar/kecil (misalnya, nama tabel dapat orders dan/atau ORDERS). |
Tidak | Peka huruf besar/kecil sesuai dengan nama tabel yang ditentukan (misalnya, nama tabel hanya boleh orders atau ORDERS). |
Anda dapat membaca detail selengkapnya tentang sintaksis SELECT MySQL.
- Tabel virtual inline
- Tabel virtual inline (disebut juga tabel turunan) adalah pernyataan
SELECT, yang terletak di klausaFROMdan digunakan sebagai subkueri. - Tabel virtual inline dapat membantu menyederhanakan kueri yang kompleks dengan menghapus penghitungan gabungan atau menghilangkan operasi join, sekaligus menggabungkan beberapa kueri terpisah menjadi satu kueri yang disederhanakan.
- Catatan konversi: Tabel virtual inline di Oracle tidak memerlukan penggunaan alias, sedangkan MySQL mengharuskan alias spesifik untuk setiap tabel virtual inline.
- Tabel virtual inline (disebut juga tabel turunan) adalah pernyataan
Tabel berikut menampilkan contoh konversi dari Oracle ke MySQL, sebagai tabel virtual inline.
| Oracle 11g/12c |
|---|
SQL> SELECT FIRST_NAME,
DEPARTMENT_ID,
SALARY,
DATE_COL
FROM EMPLOYEES,
(SELECT SYSDATE AS DATE_COL FROM DUAL);
Outputnya mirip dengan berikut: 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 untuk MySQL 5.7 |
Tanpa alias untuk tabel virtual inline:
mysql> SELECT FIRST_NAME,
DEPARTMENT_ID,
SALARY,
DATE_COL
FROM EMPLOYEES, (SELECT SYSDATE() AS DATE_COL FROM DUAL);
Menambahkan alias ke tabel virtual inline:
mysql> SELECT FIRST_NAME,
DEPARTMENT_ID,
SALARY,
DATE_COL
FROM EMPLOYEES, (SELECT SYSDATE() AS DATE_COL FROM DUAL) AS A1;
Outputnya mirip dengan berikut: +-------------+---------------+----------+---------------------+ | 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 | |
Pernyataan JOIN
Pernyataan JOIN Oracle didukung oleh pernyataan JOIN MySQL, kecuali untuk klausa FULL JOIN. Selain itu, pernyataan JOIN MySQL mendukung penggunaan sintaksis alternatif, seperti klausa USING, klausa WHERE alih-alih klausa ON, dan penggunaan SUBQUERY dalam pernyataan JOIN.
Tabel berikut menampilkan contoh konversi JOIN.
| Jenis JOIN di Oracle | Dukungan di MySQL | Sintaksis JOIN di MySQL |
|---|---|---|
INNER JOIN |
Ya | SELECT E.FIRST_NAME, D.DEPARTMENT_NAME FROM EMPLOYEES E JOIN DEPARTMENTS D ON E.DEPARTMENT_ID = D.DEPARTMENT_ID; |
CROSS JOIN |
Ya | SELECT E.FIRST_NAME, D.DEPARTMENT_NAME FROM EMPLOYEES E CROSS JOIN DEPARTMENTS D |
FULL JOIN |
Tidak | Sebagai solusi, pertimbangkan menggunakan UNION dengan pernyataan LEFT dan RIGHT JOIN. |
LEFT JOIN [ OUTER ] |
Ya | SELECT E.FIRST_NAME, D.DEPARTMENT_NAME FROM EMPLOYEES E LEFT JOIN DEPARTMENTS D ON E.DEPARTMENT_ID = D.DEPARTMENT_ID; |
RIGHT JOIN [ OUTER ] |
Ya | SELECT E.FIRST_NAME, D.DEPARTMENT_NAME FROM EMPLOYEES E RIGHT JOIN DEPARTMENTS D ON E.DEPARTMENT_ID = D.DEPARTMENT_ID; |
SUBQUERY |
Ya | 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, dan MINUS
MySQL tidak mendukung fungsi INTERSECT dan MINUS Oracle kecuali untuk fungsi UNION dan UNION ALL:
UNION: Melampirkan kumpulan hasil dari dua pernyataanSELECTatau lebih dan menghapus data duplikat.UNION ALL: Melampirkan kumpulan hasil dari dua pernyataanSELECTatau lebih tanpa menghapus data duplikat.INTERSECT: Menampilkan irisan dari dua pernyataanSELECTatau lebih hanya jika data ada di kedua set data.MINUS: Membandingkan dua pernyataanSELECTatau lebih, dengan hanya menampilkan baris berbeda dari kueri pertama yang tidak ditampilkan oleh pernyataan lainnya.
Catatan konversi
Saat mengonversi fungsi INTERSECT dan MINUS dari Oracle ke MySQL, gunakan pernyataan JOIN serta IN dan EXISTS sebagai solusi alternatif.
Contoh
| Fungsi di Oracle | Implementasi di Oracle | Dukungan di MySQL | Solusi terkait atau alternatif di MySQL |
|---|---|---|---|
UNION |
SELECT COL1 FROM TBL1 UNION SELECT COL1 FROM TBL2 |
Ya | SELECT COL1 FROM TBL1 UNION SELECT COL1 FROM TBL2 |
UNION ALL |
SELECT COL1 FROM TBL1 UNION ALL SELECT COL1 FROM TBL2 |
Ya | SELECT COL1 FROM TBL1 UNION ALL SELECT COL1 FROM TBL2 |
INTERSECT |
SELECT COL1 FROM TBL1 INTERSECT SELECT COL1 FROM TBL2 |
Tidak | SELECT COL1 FROM TBL1 WHERE COL1 IN (SELECT COL1 FROM TBL2) |
MINUS |
SELECT COL1 FROM TBL1 MINUS SELECT COL1 FROM TBL2 |
Tidak | SELECT A.COL1 FROM TBL1 A LEFT JOIN TBL2 B ON USING(COL1) WHERE B.COL1 IS NULL |
Fungsi skalar (baris tunggal) dan grup
MySQL menyediakan daftar lengkap fungsi skalar (baris tunggal) dan agregasi. Beberapa fungsi MySQL mirip dengan fungsi padanannya di Oracle (berdasarkan nama dan fungsionalitas, atau dengan nama berbeda tetapi fungsionalitas sama). Meskipun beberapa fungsi MySQL dapat memiliki nama yang sama dengan fungsi padanannya di Oracle, terkadang fungsionalitasnya berbeda.
Tabel berikut menjelaskan elemen di Oracle dan Cloud SQL untuk PostgreSQL yang setara berdasarkan nama dan fungsionalitas (ditentukan oleh "Ya") dan elemen yang perlu dikonversi (semua kasus selain "Ya")
Fungsi karakter
| Fungsi di Oracle | Spesifikasi atau implementasi fungsi di Oracle | Setara dengan MySQL | Fungsi terkait di MySQL | Spesifikasi atau implementasi fungsi di MySQL |
|---|---|---|---|---|
CONCAT(str1,str2) |
Menampilkan str1 yang disambung dengan str2:
CONCAT('A', 1) = A1
|
Ya | CONCAT |
Setara dengan Oracle:
CONCAT('A', 1) = A1
|
LOWER/UPPER |
Menampilkan karakter, dengan huruf kecil atau huruf besar semua:
LOWER('SQL') = sql
|
Ya | LOWER/UPPER |
Setara dengan Oracle:
LOWER('SQL') = sql
|
LPAD/RPAD(expr1,n,expr2) |
Menampilkan expr1, dengan padding kiri atau kanan hingga sepanjang n karakter dengan urutan karakter dalam expr2:
LPAD('A',3,'*') = **A
|
Ya | LPAD/RPAD |
Setara dengan Oracle:
LPAD('A',3,'*') = **A
|
SUBSTR(char,p,n) |
Menampilkan sebagian char, mulai dari posisi karakter p, dengan panjang substring n karakter:
SUBSTR('MySQL', 3, 3)
= SQL
|
Ya | SUBSTR(char,p,n) |
Setara dengan Oracle:
SUBSTR('MySQL', 3, 3)
= SQL
|
INSTR(index,str) |
Menampilkan posisi (indeks) string str:
INSTR('MySQL', 'y')
= 2
|
Ya | INSTR |
Setara dengan Oracle:
INSTR('MySQL', 'y')
= 2
|
REPLACE(char,str1,str2) |
Menampilkan karakter dengan setiap kemunculan string penelusuran diganti dengan string pengganti:
REPLACE('ORADB', 'ORA', 'MySQL')
|
Ya | REPLACE(char,str1,str2) |
Setara dengan Oracle:
REPLACE('ORADB', 'ORA', 'MySQL')
|
TRIM(str) |
Memangkas karakter di awal atau di akhir (atau keduanya) dari string:TRIM(both '-' FROM '-MySQL-') = MySQL |
Ya | TRIM(str) |
Setara dengan Oracle:TRIM(both '-' FROM '-MySQL-') = MySQL |
LTRIM/RTRIM(str) |
Menghapus semua karakter dari ujung kiri atau kanan string yang muncul dalam penelusuran:
LTRIM(' MySQL', ' ')
= MySQL
|
Sebagian | LTRIM/RTRIM(str) |
Fungsi R/LTRIM Oracle kecuali penggantian ke parameter (spasi kosong atau string). Fungsi R/LTRIM MySQL hanya menghilangkan spasi kosong; hanya menerima string input:
LTRIM(' MySQL')
= MySQL
|
ASCII(char) |
Menampilkan representasi desimal dalam himpunan karakter database dari karakter pertama char:
ASCII('A') = 65
|
Ya | ASCII(char) |
Setara dengan Oracle:
ASCII('A') = 65
|
CHR(char) |
Menampilkan nilai kode ASCII, yang merupakan nilai numerik antara 0 dan 225, hingga sebuah karakter:CHR(65) = A |
Sebagian dengan nama fungsi berbeda | CHAR(char) |
MySQL menggunakan fungsi CHAR untuk fungsionalitas yang sama, sehingga Anda harus mengubah nama fungsi:CHAR(65) = A |
LENGTH(str) |
Menampilkan panjang string tertentu:
LENGTH ('MySQL') = 5
|
Ya | LENGTH(str) |
Setara dengan Oracle:
LENGTH('MySQL') = 5
|
REGEXP_REPLACE(str1,expr,str2) |
Mencari pola ekspresi reguler dalam string:
REGEXP_REPLACE('John', '[hn].', '1') = Jo1
|
Tidak | T/A | Hanya didukung mulai dari MySQL versi 8. Sebagai solusinya, gunakan fungsi REPLACE jika memungkinkan, atau konversi fungsionalitas ini menjadi lapisan aplikasi |
REGEXP_SUBSTR(str,expr) |
Memperluas fungsionalitas fungsi SUBSTR dengan mencari pola ekspresi reguler dalam string:
REGEXP_SUBSTR('https://console.cloud.google.com/sql/instances','https://([[:alnum:]]+\.?){3,4}/?')
= https://console.cloud.google.com/
|
Tidak | T/A | Hanya didukung mulai dari MySQL versi 8. Sebagai solusinya, gunakan fungsi SUBSTR jika memungkinkan, atau konversi fungsionalitas ini menjadi lapisan aplikasi. |
REGEXP_COUNT(str,expr) |
Menampilkan frekuensi kemunculan pola di sebuah string sumber. | Tidak | T/A | Sebagai solusi alternatif, konversi fungsionalitas ini menjadi lapisan aplikasi. |
REGEXP_INSTR(index,expr) |
Mencari pola ekspresi reguler dalam posisi string (indeks). | Tidak | T/A | Hanya didukung mulai dari MySQL versi 8. |
REVERSE(str) |
Menampilkan string terbalik
REVERSE('MySQL')
= LQSyM
|
Ya | REVERSE |
Setara dengan Oracle:
REVERSE('MySQL')
= LQSyM
|
Fungsi numerik
| Fungsi di Oracle | Spesifikasi atau implementasi fungsi di Oracle | Setara dengan MySQL | Fungsi terkait di MySQL | Spesifikasi atau implementasi fungsi di MySQL |
|---|---|---|---|---|
ABS(n) |
Nilai absolut n: ABS(-4.6) = 4.6 |
Ya | ABS |
Setara dengan Oracle:ABS(-4.6) = 4.6 |
CEIL(n) |
Menampilkan bilangan bulat terkecil yang lebih besar daripada atau sama dengan n:CEIL(21.4) = 22 |
Ya | CEIL |
Setara dengan Oracle:CEIL(21.4) = 22 |
FLOOR(n) |
Menampilkan bilangan bulat terbesar yang sama dengan atau lebih kecil daripada n: FLOOR(-23.7) = -24 |
Ya | FLOOR |
Setara dengan Oracle:FLOOR(-23.7) = -24 |
MOD(m,n) |
Menampilkan sisa m dibagi n:MOD(10, 3) = 1 |
Ya | MOD(m,n) |
Setara dengan Oracle:MOD(10,3) = 1 |
ROUND(m,n) |
Menampilkan m yang dibulatkan ke n tempat bilangan bulat di kanan tanda desimal:ROUND(1.39,1) = 1.4 |
Ya | ROUND |
Setara dengan Oracle:ROUND(1.39,1) = 1.4 |
TRUNC(n1, n2) |
Menampilkan n1 yang dipangkas hingga n2 di belakang koma:TRUNC(99.999) = 99 TRUNC(99.999,0) = 99 |
Sebagian dengan nama fungsi berbeda | TRUNCATE(n1, n2) |
Fungsi TRUNCATE MySQL harus menerima angka input dan bilangan bulat untuk menentukan tingkat akurasi di kanan tanda desimal:TRUNCATE(99.999,0) = 99 |
Fungsi tanggal dan waktu
| Fungsi di Oracle | Spesifikasi atau implementasi fungsi di Oracle | Setara dengan MySQL | Fungsi terkait di MySQL | Spesifikasi atau implementasi fungsi di MySQL |
|---|---|---|---|---|
SYSDATE |
Menampilkan tanggal dan waktu saat ini yang ditetapkan untuk sistem operasi tempat server database berada:SELECT SYSDATE FROM DUAL = 31-JUL-2019 |
Sebagian | SYSDATE() |
Fungsi SYSDATE() MySQL harus menyertakan tanda kurung dan menampilkan format tanggal/waktu yang berbeda dari fungsi SYSDATE Oracle:SELECT SYSDATE() FROM DUAL; = 2019-01-31 10:01:01.0 Perhatikan bahwa format tanggal/waktu dapat diubah di tingkat sesi |
SYSTIMESTAMP |
Menampilkan tanggal sistem, termasuk detik pecahan dan zona waktu:SELECT SYSTIMESTAMP FROM DUAL = 01-JAN-19 07.37.11.622187000 AM +00:00 |
Sebagian dengan nama fungsi berbeda | CURRENT_TIMESTAMP |
MySQL menampilkan format tanggal/waktu yang berbeda dengan Oracle. Pemformatan tanggal diperlukan (atau fungsi tanggal yang berbeda) agar sama dengan format tanggal/waktu asli:SELECT CURRENT_TIMESTAMP FROM DUAL = 2019-01-31 06:55:07 |
LOCAL_TIMESTAMP |
Menampilkan tanggal dan waktu saat ini di zona waktu sesi dalam nilai dengan jenis data TIMESTAMP:SELECT LOCAL_TIMESTAMP FROM DUAL = 01-JAN-19 10.01.10.123456 PM |
Sebagian dengan format tanggal/waktu berbeda. | LOCAL_TIMESTAMP |
MySQL menampilkan format tanggal/waktu yang berbeda dengan Oracle. Pemformatan tanggal/waktu diperlukan (atau fungsi tanggal yang berbeda) agar sama dengan format tanggal/waktu asli:SELECT LOCAL_TIMESTAMP FROM DUAL = 2019-01-01 10:01:01.0 |
CURRENT_DATE |
Menampilkan tanggal saat ini dalam zona waktu sesi:SELECT CURRENT_DATE FROM DUAL = 31-JAN-19 |
Sebagian dengan format tanggal/waktu berbeda | CURRENT_DATE |
MySQL menampilkan format tanggal/waktu yang berbeda dengan Oracle. Pemformatan tanggal/waktu diperlukan (atau fungsi tanggal yang berbeda) agar sama dengan format tanggal/waktu asli:SELECT CURRENT_DATE FROM DUAL = 2019-01-31 |
CURRENT_TIMESTAMP |
Menampilkan tanggal dan waktu saat ini dalam zona waktu sesi:SELECT CURRENT_TIMESTAMP FROM DUAL = 31-JAN-19 06.54.35.543146 AM +00:00 |
Sebagian dengan format tanggal/waktu berbeda | CURRENT_TIMESTAMP |
MySQL menampilkan format tanggal/waktu yang berbeda dengan Oracle. Pemformatan tanggal/waktu diperlukan (atau fungsi tanggal yang berbeda) agar sama dengan format tanggal/waktu asli:SELECT CURRENT_TIMESTAMP FROM DUAL = 2019-01-31 06:55:07 |
ADD_MONTHS |
Menampilkan tanggal plus bulan dalam bilangan bulat:ADD_MONTHS(SYSDATE, 1) = 31-JAN-19 |
Sebagian dengan nama fungsi berbeda | ADDDATE |
Untuk mendapatkan fungsionalitas yang sama, MySQL menggunakan fungsi ADDDATE:ADDDATE(SYSDATE(), 1) = 2019-08-01 06:42:49.0 Secara default, MySQL menampilkan tanggal/waktu dan rentang/format yang berbeda dengan Oracle. Pemformatan tanggal/waktu diperlukan (atau fungsi tanggal yang berbeda) agar sama dengan format tanggal/waktu asli. |
EXTRACT(bagian tanggal) |
Menampilkan nilai kolom tanggal/waktu tertentu dari ekspresi tanggal/waktu atau interval: EXTRACT(YEAR FROM DATE '2019-01-31') = 2019 |
Ya | EXTRACT (bagian tanggal) |
Setara dengan Oracle:EXTRACT(YEAR FROM DATE '2019-01-31') = 2019 |
LAST_DAY |
Menampilkan tanggal dari hari terakhir bulan itu:
LAST_DAY('01-JAN-2019')
= 31-JAN-19
|
Sebagian dengan format tanggal/waktu berbeda | LAST_DAY |
MySQL menampilkan format tanggal/waktu yang berbeda dengan Oracle. Pemformatan tanggal/waktu diperlukan (atau fungsi tanggal yang berbeda) agar sama dengan format tanggal/waktu asli:
LAST_DAY('2019-01-01')
= 2019-01-31
|
MONTH_BETWEEN |
Menampilkan jumlah bulan antara tanggal date1 dan date2:MONTHS_BETWEEN( SYSDATE, SYSDATE-60) = 1.96 |
Sebagian dengan nama fungsi berbeda | PERIOD_DIFF(date1,date2) |
Fungsi PERIOD_DIFF MySQL menampilkan selisih bulan sebagai bilangan bulat antara dua periode (diformat sebagai YYMM atau YYYYMM):PERIOD_DIFF( '201903', '201901') = 2 Untuk mendapatkan nilai yang sama dengan fungsi MONTH_BETWEEN Oracle, diperlukan konversi yang lebih spesifik |
TO_CHAR (tanggal/waktu) |
Mengonversi jenis data tanggal/waktu atau stempel waktu menjadi nilai dengan jenis data VARCHAR2 dalam format yang ditentukan oleh format tanggal:TO_CHAR( SYSDATE,'DD-MM-YYYY HH24:MI:SS') = 01-01-2019 10:01:01 |
Sebagian dengan nama fungsi berbeda | DATE_FORMAT |
Fungsi DATE_FORMAT MySQL memformat tanggal seperti yang ditentukan oleh definisi format tanggal:DATE_FORMAT( SYSDATE(),'%d-%m-%Y %H:%i:%s') = 01-01-2019 10:01:01 |
Fungsi encoding dan decoding
| Fungsi di Oracle | Spesifikasi atau implementasi fungsi di Oracle | Setara dengan MySQL | Fungsi terkait di MySQL | Spesifikasi atau implementasi fungsi di MySQL |
|---|---|---|---|---|
DECODE |
Membandingkan ekspresi dengan setiap nilai penelusuran satu per satu menggunakan fungsionalitas pernyataan IF-THEN-ELSE |
Tidak | CASE |
Gunakan pernyataan CASE MySQL untuk mendapatkan fungsionalitas serupa. |
DUMP |
Menampilkan nilai VARCHAR2 yang berisi kode jenis data, panjang dalam byte, dan representasi internal untuk ekspresi tertentu. |
Tidak | T/A | Tidak didukung. |
ORA_HASH |
Menghitung nilai hash untuk ekspresi tertentu. | Tidak | MD5/SHA |
Gunakan fungsi MD5 MySQL untuk checksum 128-bit, atau fungsi SHA untuk checksum 160-bit, guna menghasilkan nilai hash. |
Fungsi konversi
| Fungsi di Oracle | Spesifikasi atau implementasi fungsi di Oracle | Setara dengan MySQL | Fungsi terkait di MySQL | Spesifikasi atau implementasi fungsi di MySQL |
|---|---|---|---|---|
CAST |
Mengonversi satu jenis data bawaan atau nilai berjenis koleksi menjadi jenis data bawaan atau nilai berjenis koleksi lainnya:
CAST('1' as int) + 1
= 2
|
Sebagian | CAST |
Fungsi CAST MySQL mirip dengan fungsionalitas Oracle, tetapi dalam kasus tertentu, fungsi ini harus disesuaikan bergantung pada apakah konversi eksplisit atau implisit diperlukan:
CAST('1' AS SIGNED) + 1
= 2
|
CONVERT |
Mengonversi string karakter dari satu himpunan karakter ke himpunan karakter lainnya:
CONVERT('Ä Ê Í Õ Ø A B C D E ', 'US7ASCII', 'WE8ISO8859P1')
= ?? ?? ?? A B C
|
Sebagian | CONVERT |
Fungsi CONVERT MySQL memerlukan beberapa penyesuaian sintaksis dan parameter agar dapat menampilkan hasil yang sama persis dengan yang di Oracle:
CONVERT('Ä Ê Í A B C ' USING utf8)
= Ä Ê Í A B C
|
TO_CHAR (string/numerik) |
Fungsi ini mengonversi angka atau tanggal menjadi string: TO_CHAR(22.73,'$99.9') = $22.7 |
Tidak | FORMAT |
Fungsi FORMAT MySQL menjalankan format '#,###.##' dari suatu angka, membulatkannya ke sejumlah desimal tertentu, lalu menampilkan hasilnya sebagai string. Fungsi ini memiliki fungsionalitas yang berbeda dengan yang di Oracle:
CONCAT('$',
FORMAT(22.73, 1))
= $22.7
|
TO_DATE |
Fungsi TO_DATE Oracle mengonversi string menjadi tanggal berdasarkan format tanggal/waktu spesifik sumbernya:TO_DATE( '2019/01/01', 'yyyy-mm-dd') = 01-JAN-2019 |
Sebagian dengan nama fungsi dan format tanggal/waktu berbeda | STR_TO_DATE |
Fungsi STR_TO_DATE MySQL mengambil string dan menampilkan tanggal yang ditentukan oleh format tanggal/waktu:STR_TO_DATE( '2019/01/01', '%Y/%m/%d') = 2019-01-01 |
TO_NUMBER |
Mengonversi ekspresi menjadi nilai dengan jenis data NUMBER:
TO_NUMBER('01234')
= 1234
|
Tidak | CAST |
Sebagai alternatif, gunakan fungsi CAST MySQL untuk menampilkan hasil yang sama dengan fungsi TO_NUMBER Oracle:
CAST('01234' as SIGNED)
= 1234
|
Fungsi SELECT bersyarat
| Fungsi di Oracle | Spesifikasi atau implementasi fungsi di Oracle | Setara dengan MySQL | Fungsi terkait di MySQL | Spesifikasi atau implementasi fungsi di MySQL |
|---|---|---|---|---|
CASE |
Pernyataan CASE memilih dari serangkaian kondisi dan menjalankan pernyataan yang terkait dengan sintaksis berikut:CASE WHEN condition THEN result [WHEN ...] [ELSE result] END |
Ya | CASE |
Selain fungsi CASE, MySQL juga mendukung penggunaan penanganan bersyarat IF/ELSE di dalam pernyataan SELECT:CASE WHEN condition THEN result [WHEN ...] [ELSE result] END |
Fungsi null
| Fungsi di Oracle | Spesifikasi atau implementasi fungsi di Oracle | Setara dengan MySQL | Fungsi terkait di MySQL | Spesifikasi atau implementasi fungsi di MySQL |
|---|---|---|---|---|
COALESCE |
Menampilkan ekspresi bukan null pertama dalam daftar ekspresi: COALESCE( null, '1', 'a') = a |
Ya | COALESCE |
Setara dengan Oracle:COALESCE( null, '1', 'a') = 1 |
NULLIF |
Membandingkan expr1 dan expr2m. Jika keduanya sama, fungsi akan menampilkan null. Jika keduanya tidak sama, fungsi akan menampilkan expr1:
NULLIF('1', '2')
= a
|
Ya | NULLIF |
Setara dengan Oracle:
NULLIF('1', '2')
= a
|
NVL |
Mengganti null (ditampilkan sebagai kosong) dengan string dalam hasil kueri:
NVL(null, 'a') = a |
Tidak | IFNULL |
Fungsi yang setara di MySQL adalah fungsi IFNULL , yang mengganti nilai null dengan string tertentu:IFNULL(null, 'a') = a |
NVL2 |
Menentukan nilai yang ditampilkan oleh kueri berdasarkan nilai ekspresi tertentu, yakni null atau bukan null. |
Tidak | CASE |
Pernyataan CASE memilih dari serangkaian kondisi dan menjalankan pernyataan yang terkait: CASE WHEN condition THEN result [WHEN ...] [ELSE result] END |
Fungsi lingkungan dan ID
| Fungsi di Oracle | Spesifikasi atau implementasi fungsi di Oracle | Setara dengan MySQL | Fungsi terkait di MySQL | Spesifikasi atau implementasi fungsi di MySQL |
|---|---|---|---|---|
SYS_GUID |
Menghasilkan dan menampilkan ID unik global (nilai RAW) yang terdiri atas 16 byte:SELECT SYS_GUID() FROM DUAL = 8EFA4A31468B4C6DE05011AC0200009E |
Tidak | REPLACE dan UUID |
Sebagai solusinya, gunakan fungsi REPLACE dan UUID MySQL untuk menyimulasikan fungsi SYS_GUID Oracle:REPLACE( UUID(), '-', '') |
UID |
Menampilkan bilangan bulat yang mengidentifikasi pengguna sesi secara unik (pengguna yang login): SELECT UID FROM DUAL = 43 |
Tidak | T/A | T/A |
USER |
Menampilkan nama dari nama pengguna sesi saat ini:SELECT USER FROM DUAL = UserName |
Sebagian | USER + INSTR + SUBSTR |
Fungsi USER MySQL menampilkan nama pengguna beserta server koneksi (root@IP). Untuk mengambil nama pengguna saja, gunakan fungsi pendukung tambahan:SELECT SUBSTR(USER(), 1, INSTR(USER(), '@') -1) FROM DUAL = root |
USERENV |
Menampilkan informasi tentang sesi pengguna saat ini dengan konfigurasi parameter saat ini:
SELECT USERENV('LANGUAGE')
FROM DUAL
= ENGLISH_AMERICA.AL32UTF8
|
Tidak | SHOW SESSION VARIABLES |
Gunakan pernyataan SHOW SESSIONVARIABLES MySQL untuk melihat setelan sesi saat ini:SHOW SESSION VARIABLES LIKE '%collation%'; = utf8_general_ci |
ROWID |
Server Oracle menetapkan ROWID unik ke setiap baris di setiap tabel untuk mengidentifikasi baris tersebut dalam tabel. ROWID adalah alamat baris yang berisi nomor objek data, blok data baris, posisi baris, dan file data. |
Tidak | T/A | Jika memungkinkan, coba emulasikan fungsionalitas yang sama dengan fungsi MySQL lainnya. |
ROWNUM |
Menampilkan angka yang menunjukkan urutan pemilihan baris oleh Oracle dari tabel atau tabel gabungan. | Tidak | T/A | Jika memungkinkan, coba emulasikan fungsionalitas yang sama dengan fungsi atau variabel sesi MySQL lainnya. |
Fungsi agregat (grup)
| Fungsi di Oracle | Spesifikasi atau implementasi fungsi di Oracle |
Setara dengan MySQL |
Fungsi terkait di MySQL | Spesifikasi atau implementasi fungsi di MySQL |
|---|---|---|---|---|
AVG |
Menampilkan nilai rata-rata kolom atau ekspresi. | Ya | AVG |
Setara dengan Oracle |
COUNT |
Menampilkan jumlah baris yang ditampilkan oleh kueri. | Ya | COUNT |
Setara dengan Oracle |
COUNT (DISTINCT) |
Menampilkan jumlah nilai unik dalam kolom atau ekspresi. | Ya | COUNT (DISTINCT) |
Setara dengan Oracle |
MAX |
Menampilkan nilai maksimum kolom atau ekspresi. | Ya | MAX |
Setara dengan Oracle |
MIN |
Menampilkan nilai minimum kolom atau ekspresi. | Ya | MIN |
Setara dengan Oracle |
SUM |
Menampilkan jumlah nilai kolom atau ekspresi. | Ya | SUM |
Setara dengan Oracle |
LISTAGG |
Menampilkan data dalam setiap grup berdasarkan satu baris yang ditentukan dalam klausa ORDER BY dengan menggabungkan nilai kolom pengukuran:SELECT LISTAGG( DEPARTMENT_NAME, ', ') WITHIN GROUP (ORDER BY DEPARTMENT_NAME) DEPT FROM DEPARTMENTS; |
Tidak | GROUP_CONCAT |
Gunakan fungsi GROUP_CONCAT MySQL untuk menampilkan hasil yang mirip dengan yang di Oracle, antisipasi perbedaan sintaksis pada kasus tertentu:SELECT GROUP_CONCAT( DEPARTMENT_NAME ORDER BY DEPARTMENT_NAME SEPARATOR ', ') DEPT FROM DEPARTMENTS; |
Fetch di Oracle 12c
| Fungsi di Oracle | Spesifikasi atau implementasi fungsi di Oracle |
Setara dengan MySQL |
Fungsi terkait di MySQL | Spesifikasi atau implementasi fungsi di MySQL |
|---|---|---|---|---|
FETCH |
Mengambil baris data dari kumpulan hasil kueri multi-baris: SELECT * FROM EMPLOYEES FETCH FIRST 10 ROWS ONLY; |
Tidak | LIMIT | Gunakan klausa LIMIT MySQL untuk mengambil sekumpulan data tertentu saja:SELECT * FROM EMPLOYEES LIMIT 10; |
Pemfilteran, operator, dan subkueri dasar
Selama konversi, pemfilteran, fungsi operator, dan subkueri dasar relatif mudah ditangani, hanya perlu sedikit atau bahkan tanpa upaya tambahan.
Catatan konversi
Periksa dan tangani format tanggal karena format Oracle dan MySQL menampilkan hasil default yang berbeda:
- Fungsi
SYSDATEOracle secara default menampilkan01-AUG-19. - Fungsi
SYSDATE()MySQL secara default menampilkan2019-08-01 12:04:05. - Format tanggal dan waktu dapat ditetapkan menggunakan fungsi
[DATE_FORMAT](https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_date-format)atau[STR_TO_DATE](https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_str-to-date)MySQL.
| Fungsi atau subkueri di Oracle | Setara dengan MySQL | Fungsi atau subkueri terkait di MySQL | Spesifikasi atau implementasi fungsi di MySQL |
|---|---|---|---|
EXISTS/NOT EXISTS |
Ya | EXISTS/NOT EXISTS |
SELECT * FROM DEPARTMENTS D
WHERE EXISTS (SELECT 1
FROM EMPLOYEES E
WHERE
E.DEPARTMENT_ID =
D.DEPARTMENT_ID);
|
IN/NOT IN |
Ya | IN/NOT IN |
SELECT * FROM DEPARTMENTS D
WHERE DEPARTMENT_ID IN
(SELECT DEPARTMENT_ID
FROM EMPLOYEES E);
|
LIKE/NOT LIKE |
Ya | LIKE/NOT LIKE |
SELECT * FROM EMPLOYEES WHERE FIRST_NAME LIKE '_e_n%'; |
BETWEEN/NOT BETWEEN |
Ya | BETWEEN/NOT BETWEEN |
SELECT * FROM EMPLOYEES WHERE EXTRACT(YEAR FROM HIRE_DATE) NOT BETWEEN 2001 and 2004; |
AND/OR |
Ya | AND/OR |
SELECT * FROM EMPLOYEES WHERE DEPARTMENT_ID IN(100, 101) AND (SALARY >= 1000 OR HIRE_DATE <= '2006-02-05'); |
SubQuery |
Ya | SubQuery |
MySQL mendukung subkueri di level SELECT, untuk pernyataan JOIN dan untuk pemfilteran dalam klausa 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;
|
| Operator | Ya | Operator | MySQL mendukung semua operator dasar:> | >= | < | <= | = | <> | != |
Fungsi analitik (atau fungsi jendela dan peringkat)
Fungsi analitik Oracle memperluas fungsionalitas fungsi analitik SQL standar dengan menyediakan kapabilitas untuk menghitung nilai agregat berdasarkan sekumpulan baris. Fungsi ini dapat diterapkan ke kumpulan hasil yang dipartisi secara logis dalam lingkup ekspresi kueri tunggal. Biasanya, fungsi ini digunakan bersama dengan analisis dan laporan business intelligence untuk meningkatkan performa kueri, sebagai alternatif dari penggunaan kode SQL non-analitis yang lebih kompleks.
Catatan konversi
- MySQL versi 5.7 tidak menyediakan fungsi analitik untuk mendukung konversi pernyataan SQL secara langsung. Namun, fungsionalitas ini ditambahkan sebagian di MySQL versi 8, yang menjadikan konversi fungsi analitik sebagai hal yang perlu dipertimbangkan, yang mungkin memerlukan upaya manual dalam proses migrasi.
- Salah satu solusi opsionalnya adalah menulis ulang kode untuk menghapus penggunaan fungsi analitik, kembali ke solusi kode SQL yang lebih tradisional, atau memindahkan logika ini ke lapisan aplikasi.
Tabel berikut mencantumkan daftar fungsi analitik yang umum di Oracle.
| Kelompok fungsi | Fungsi terkait | Didukung di MySQL 5.7 |
|---|---|---|
| Analitik dan peringkat | RANK AVERAGE_RANK DENSE_RANK RANK ROW_NUMBER PERCENT_RANK CUME_DIST NTILE FIRST_VALUE LAST_VALUE OVER (PARTITION BY...) |
Tidak |
| Hierarkis | CONNECT BY HIER_ANCESTOR HIER_CHILD_COUNT HIER_DEPTH HIER_LEVEL HIER_ORDER HIER_PARENT HIER_TOP |
Tidak |
| Lag | LAG LAG_VARIANCE LAG_VARIANCE_PERCENT LEAD LEAD_VARIANCE LEAD_VARIANCE_PERCENT |
Tidak |
Ekspresi tabel umum (CTE)
CTE menyediakan cara mengimplementasikan logika kode berurutan untuk menggunakan kembali kode SQL yang mungkin terlalu kompleks atau tidak efisien untuk banyak penggunaan. CTE dapat dinamai dan kemudian digunakan berkali-kali di berbagai bagian pernyataan SQL menggunakan klausa WITH.
Catatan konversi
- MySQL versi 5.7 tidak mendukung CTE, tetapi MySQL versi 8 mendukungnya.
- Sebagai solusi alternatif, gunakan tabel turunan atau subkueri, atau tulis ulang pernyataan SQL untuk menghilangkan fungsi CTE.
Contoh
| Oracle |
|---|
WITH DEPT_COUNT
(DEPARTMENT_ID, DEPT_COUNT) AS
(SELECT DEPARTMENT_ID,
COUNT(*)
FROM EMPLOYEES
GROUP BY DEPARTMENT_ID)
|
| 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;
|
Pernyataan MERGE
Pernyataan MERGE (atau UPSERT) menyediakan cara untuk menentukan pernyataan SQL tunggal yang melakukan operasi DML secara bersyarat dalam satu operasi MERGE, bukan satu operasi DML, yang berjalan terpisah. Pernyataan ini memilih data dari tabel sumber, kemudian melakukan beberapa operasi DML pada tabel target secara otomatis dengan menentukan struktur logika. Fitur ini membantu Anda menghindari penggunaan pernyataan insert, update, atau delete secara berulang-ulang. Perhatikan bahwa MERGE adalah pernyataan deterministik. Artinya, baris yang telah diproses oleh pernyataan MERGE tidak dapat diproses lagi menggunakan pernyataan MERGE yang sama.
Catatan konversi
Tidak seperti Oracle, MySQL versi 5.7 tidak mendukung fungsi MERGE. Untuk menyimulasikan sebagian fungsionalitas MERGE, MySQL menyediakan REPLACE dan pernyataan INSERT… ON DUPLICATE KEY UPDATE:
REPLACE: Fungsinya sama dengan pernyataanINSERT, kecuali bahwa jika baris lama dalam tabel memiliki nilai yang sama dengan baris baru untuk indeksPRIMARY KEYatauUNIQUE, baris lama akan dihapus sebelum baris baru disisipkan.INSERT… ON DUPLICATE KEY UPDATE: Jika baris yang disisipkan menyebabkan nilai duplikat dalam indeksPRIMARY KEYatauUNIQUE,UPDATEbaris lama akan muncul untuk menghilangkan pengecualian kunci duplikat, misalnya: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;
Solusi lainnya adalah mengonversi fungsionalitas MERGE menjadi prosedur tersimpan untuk mengelola operasi DML, menggunakan perintah INSERT, UPDATE, dan DELETE dengan penanganan pengecualian serta duplikasi.
Petunjuk pernyataan SQL
Oracle menyediakan banyak petunjuk kueri SQL yang memungkinkan pengguna memengaruhi perilaku Pengoptimal dan pengambilan keputusannya, dengan tujuan menghasilkan rencana eksekusi kueri yang lebih efisien. Oracle mendukung lebih dari 60 petunjuk database. MySQL menyediakan petunjuk kueri yang lebih sedikit.
Secara umum, MySQL versi 5.7 mendukung dua jenis petunjuk kueri: OPTIMIZER
HINTS dan INDEX HINTS.
Optimizer Hint MySQL memberikan kemampuan untuk mengontrol perilaku Pengoptimal dalam pernyataan SQL individual—misalnya:
SELECT /*+ NO_RANGE_OPTIMIZATION(tbl PRIMARY, col1_idx) */ col1 FROM tbl;
Optimizer Hint yang Tersedia di MySQL 5.7
| Nama petunjuk | Ringkasan petunjuk | Cakupan yang berlaku |
|---|---|---|
BKA, NO_BKA |
Memengaruhi pemrosesan join akses kunci batch | Blok kueri, tabel |
BNL, NO_BNL |
Memengaruhi pemrosesan join loop bertingkat blok | Blok kueri, tabel |
MAX_EXECUTION_TIME |
Membatasi waktu eksekusi pernyataan | Global |
MRR, NO_MRR |
Memengaruhi pengoptimalan baca multi-rentang | Tabel, indeks |
NO_ICP |
Memengaruhi pengoptimalan pushdown kondisi indeks | Tabel, indeks |
NO_RANGE_OPTIMIZATION |
Memengaruhi pengoptimalan rentang | Tabel, indeks |
QB_NAME |
Menetapkan nama ke blok kueri | Blok kueri |
SEMIJOIN, NO_SEMIJOIN |
Memengaruhi strategi semi-join | Blok kueri |
SUBQUERY |
Memengaruhi materialisasi, strategi subkueri IN ke EXISTS. |
Blok kueri |
Index Hint MySQL memberi Pengoptimal informasi tentang cara memilih indeks selama pemrosesan kueri. Kata kunci USE, FORCE, atau IGNORE digunakan untuk mengontrol proses penggunaan indeks Pengoptimal—misalnya:
SELECT * FROM tbl USE INDEX (col1_index, col2_index);
-- OR
SELECT * FROM tbl IGNORE INDEX (col1_index, col2_index);
Catatan konversi
Karena ada perbedaan mendasar antara Pengoptimal Oracle dan MySQL, dan karena tumpang-tindih antara petunjuk kueri Oracle dan MySQL sangat terbatas atau tidak ada sama sekali, sebaiknya Anda mengonversi pernyataan SQL Oracle yang berisi petunjuk kueri yang tidak ditentukan menjadi database MySQL target.
Lakukan penyesuaian performa MySQL melalui alat (misalnya MySQL Workbench untuk dasbor performa real-time) dan fitur MySQL seperti memeriksa kueri menggunakan rencana eksekusi dan menyesuaikan parameter instance atau sesi sesuai dengan kasus penggunaannya.
Rencana eksekusi
Kegunaan utama rencana eksekusi adalah untuk memberikan gambaran mendalam mengenai pilihan yang dibuat oleh pengoptimal kueri untuk mengakses data database. Pengoptimal kueri menghasilkan rencana eksekusi untuk pernyataan SELECT, INSERT, UPDATE, dan DELETE bagi pengguna database, serta memungkinkan administrator lebih memahami kueri dan operasi DML tertentu. Rencana ini sangat berguna saat Anda perlu menyesuaikan performa kueri—misalnya, untuk mengetahui performa indeks atau menentukan apakah ada indeks yang hilang yang perlu dibuat.
Rencana eksekusi dapat dipengaruhi oleh volume data, statistik data, dan parameter instance (parameter global atau sesi).
Pertimbangan konversi
Rencana eksekusi bukanlah objek database yang perlu dimigrasikan, melainkan alat untuk menganalisis perbedaan performa antara Oracle dan MySQL yang menjalankan pernyataan yang sama pada set data identik.
MySQL tidak mendukung sintaksis, fungsionalitas, atau output rencana eksekusi yang sama dengan Oracle.
Contoh
| Rencana eksekusi di Oracle |
|---|
SQL> EXPLAIN PLAN FOR
SELECT * FROM EMPLOYEES WHERE EMPLOYEE_ID = 105;
|
| Rencana eksekusi di MySQL |
mysql> EXPLAIN SELECT * FROM EMPLOYEES WHERE EMPLOYEE_ID = 105; |
Prosedur, fungsi, dan pemicu tersimpan
PL/SQL adalah bahasa prosedural tambahan di Oracle yang digunakan untuk membuat, menyimpan, dan menerapkan solusi berbasis kode dalam database. Secara umum, prosedur dan fungsi tersimpan database adalah elemen kode yang terdiri atas bahasa prosedural tambahan ANSI SQL dan SQL—misalnya PL/SQL untuk Oracle, PL/pgSQL untuk PostgreSQL, dan bahasa prosedural MySQL untuk MySQL. MySQL menggunakan nama yang sama dengan database untuk bahasa prosedural tambahannya sendiri.
Prosedur dan fungsi tersimpan digunakan untuk memberikan solusi bagi persyaratan yang lebih cocok dijalankan dari dalam database, bukan dari aplikasi (misalnya performa, kompatibilitas, dan keamanan). Meskipun sama-sama menggunakan PL/SQL, prosedur tersimpan digunakan terutama untuk menjalankan operasi DDL/DML, sedangkan fungsi tersimpan biasa digunakan untuk melakukan penghitungan guna menampilkan hasil tertentu.
Bahasa prosedural PL/SQL ke MySQL
Dari perspektif migrasi kode PL/SQL Oracle ke MySQL, implementasi prosedural MySQL berbeda dengan Oracle. Oleh karena itu, migrasi kode diperlukan untuk mengonversi fungsionalitas PL/SQL dari Oracle menjadi prosedur dan fungsi tersimpan di MySQL. Selain itu, Oracle Package dan Package Body tidak didukung di MySQL. Jadi, saat Anda mengonversi kode, konversi (atau urai) elemen tersebut menjadi beberapa unit kode MySQL tunggal. Perhatikan bahwa prosedur dan fungsi tersimpan di MySQL disebut juga routine.
Pemilik objek kode
Di Oracle, pemilik prosedur atau fungsi tersimpan adalah pengguna tertentu. Di MySQL, pemiliknya adalah skema tertentu (yang dibuat di database oleh pengguna database).
Hak istimewa dan keamanan objek kode
Di Oracle, untuk membuat prosedur atau fungsi tersimpan, pengguna harus memiliki hak istimewa sistem CREATE PROCEDURE (untuk membuat prosedur atau fungsi pada pengguna lain, pengguna database harus memiliki hak istimewa CREATE
ANY PROCEDURE). Untuk menjalankan prosedur atau fungsi tersimpan, pengguna database harus memiliki hak istimewa EXECUTE.
Di MySQL, pengguna harus memiliki hak istimewa CREATE
ROUTINE untuk membuat elemen kode, dan hak istimewa EXECUTE untuk menjalankannya. Klausa DEFINER MySQL menentukan pembuat pengguna objek kode, dan pengguna ini harus memiliki hak istimewa yang sesuai seperti CREATE ROUTINE.
Sintaksis prosedur dan fungsi tersimpan di MySQL
Contoh berikut menunjukkan sintaksis prosedur dan fungsi tersimpan di 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