Dokumen ini adalah bagian dari rangkaian yang menyajikan informasi dan panduan penting terkait perencanaan dan 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 fungsi
- Memigrasikan pengguna Oracle ke Cloud SQL untuk MySQL: Jenis data, pengguna, dan tabel
- Memigrasikan pengguna Oracle ke Cloud SQL untuk MySQL: Kueri, prosedur tersimpan, fungsi, dan pemicu (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. Secara umum sangat mudah memigrasikan pernyataan SQL hanya dengan menggunakan elemen sintaksis dasar (misalnya, tidak menentukan fungsi skalar atau fitur diperluas Oracle lainnya). Bagian berikut membahas elemen kueri Oracle umum beserta ekuivalen Cloud SQL for MySQL yang sesuai.
Sintaksis SELECT dan FROM dasar
Nama fitur atau nama sintaksis Oracle | Ringkasan atau implementasi Oracle | Dukungan MySQL | Solusi alternatif atau yang sesuai 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 |
Nama tabel Kepekaan huruf besar/kecil |
Tidak ada kepekaan huruf besar/kecil (misalnya, nama tabel dapat berupa 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.
- Tampilan inline
- Tampilan inline (juga dikenal sebagai tabel turunan) adalah pernyataan
SELECT
, yang terletak di klausaFROM
dan digunakan sebagai subkueri. - Tampilan inline dapat membantu menyederhanakan kueri yang kompleks dengan menghapus penghitungan gabungan atau menghilangkan operasi join, sekaligus meringkas beberapa kueri terpisah menjadi satu kueri yang disederhanakan.
- Catatan konversi: Tampilan inline Oracle tidak memerlukan penggunaan alias, sedangkan MySQL memerlukan alias khusus untuk setiap tampilan inline.
- Tampilan inline (juga dikenal sebagai tabel turunan) adalah pernyataan
Tabel berikut menampilkan contoh konversi dari Oracle ke MySQL, sebagai tampilan 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 ini: 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 tampilan inline:mysql> SELECT FIRST_NAME, DEPARTMENT_ID, SALARY, DATE_COL FROM EMPLOYEES, (SELECT SYSDATE() AS DATE_COL FROM DUAL); Menambahkan alias ke tampilan 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 ini: +-------------+---------------+----------+---------------------+ | 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 MySQL
JOIN
, kecuali untuk klausa FULL JOIN
. Selain itu, pernyataan JOIN
MySQL mendukung penggunaan sintaksis alternatif, seperti klausa USING
, klausa WHERE
sebagai ganti klausa ON
, dan menggunakan SUBQUERY
di pernyataan
JOIN
.
Tabel berikut menampilkan contoh konversi JOIN.
Jenis JOIN Oracle | Didukung oleh MySQL | Sintaksis JOIN 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 untuk 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 yang berisi dua pernyataanSELECT
atau lebih dan menghapus data duplikat.UNION ALL
: Melampirkan kumpulan hasil dari dua pernyataanSELECT
atau lebih tanpa menghapus data duplikat.INTERSECT
: Menampilkan perpotongan dari dua pernyataanSELECT
atau lebih hanya jika data ada di kedua set data.MINUS
: Membandingkan dua atau beberapa pernyataanSELECT
, hanya menampilkan baris yang berbeda dari kueri pertama yang tidak ditampilkan oleh pernyataan lainnya.
Catatan konversi
Saat melakukan konversi dari fungsi INTERSECT
dan MINUS
Oracle ke MySQL, gunakan pernyataan JOIN
serta IN
dan EXISTS
sebagai solusi alternatif.
Contoh
Fungsi Oracle | Implementasi Oracle | Dukungan MySQL | Solusi alternatif atau yang sesuai 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 Oracle (berdasarkan nama dan fungsi, atau dengan nama yang berbeda tetapi dengan fungsi yang mirip). Meskipun fungsi MySQL dapat memiliki nama yang identik dengan fungsi Oracle, keduanya dapat menunjukkan fungsionalitas yang berbeda.
Tabel berikut menjelaskan di mana Oracle dan MySQL setara berdasarkan nama dan fungsionalitas (ditentukan oleh "Yes") dan tempat konversi direkomendasikan (semua kasus selain "Yes").
Fungsi karakter
Fungsi Oracle | Spesifikasi atau implementasi fungsi Oracle | Setara MySQL | Fungsi yang sesuai MySQL | Spesifikasi atau implementasi fungsi MySQL |
---|---|---|---|---|
CONCAT(str1,str2) |
Menampilkan str1 yang disambungkan dengan str2:CONCAT('A', 1) = A1 |
Ya | CONCAT |
Setara dengan Oracle:CONCAT('A', 1) = A1 |
LOWER/UPPER |
Menampilkan karakter, dengan semua huruf kecil atau huruf besar: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 panjang karakter n 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, dimulai pada posisi karakter p,
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 yang diganti dengan string pengganti: REPLACE('ORADB', 'ORA', 'MySQL') |
Ya | REPLACE(char,str1,str2) |
Setara dengan Oracle:REPLACE('ORADB', 'ORA', 'MySQL') |
TRIM(str) |
Pangkas karakter di awal atau 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 yang muncul dalam penelusuran dari ujung kiri atau kanan string:LTRIM(' MySQL', ' ') = MySQL |
Sebagian | LTRIM/RTRIM(str) |
Fungsi R/LTRIM Oracle kecuali pengganti parameter
(spasi kosong atau string). R/LTRIM MySQL hanya menghilangkan spasi kosong, sehingga 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, ke karakter:CHR(65) = A |
Sebagian dengan nama fungsi yang berbeda | CHAR(char) |
MySQL menggunakan fungsi CHAR untuk fungsi yang sama; oleh karena itu, 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) |
Telusuri string untuk pola ekspresi reguler:REGEXP_REPLACE('John', '[hn].', '1') = Jo1 |
Tidak | T/A | Hanya didukung dari MySQL versi 8. Sebagai solusinya, gunakan fungsi REPLACE jika memungkinkan atau konversikan ke lapisan aplikasi |
REGEXP_SUBSTR(str,expr) |
Memperluas fungsi dari fungsi SUBSTR dengan menelusuri
string untuk pola ekspresi reguler:REGEXP_SUBSTR('https://console.cloud.google.com/sql/instances','https://([[:alnum:]]+\.?){3,4}/?') = https://console.cloud.google.com/ |
Tidak | T/A | Hanya didukung dari MySQL versi 8. Sebagai solusinya, gunakan
fungsi SUBSTR jika memungkinkan, atau konversikan fungsi tersebut ke
lapisan aplikasi. |
REGEXP_COUNT(str,expr) |
Menampilkan frekuensi terjadinya pola di string sumber. | Tidak | T/A | Untuk solusi alternatif, konversikan fungsi ke lapisan aplikasi. |
REGEXP_INSTR(index,expr) |
Telusuri posisi string (indeks) untuk pola ekspresi reguler. | Tidak | T/A | Hanya didukung dari MySQL versi 8. |
REVERSE(str) |
Tampilkan string terbalikREVERSE('MySQL') = LQSyM |
Ya | REVERSE |
Setara dengan Oracle:REVERSE('MySQL') = LQSyM |
Fungsi numerik
Fungsi Oracle | Spesifikasi atau implementasi fungsi Oracle | Setara MySQL | Fungsi yang sesuai MySQL | Spesifikasi atau implementasi fungsi 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 dari 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 kurang dari n: FLOOR(-23.7) = -24 |
Ya | FLOOR |
Setara dengan Oracle:FLOOR(-23.7) = -24 |
MOD(m,n) |
Menampilkan sisa m yang 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 sebelah kanan
titik desimal:ROUND(1.39,1) = 1.4 |
Ya | ROUND |
Setara dengan Oracle:ROUND(1.39,1) = 1.4 |
TRUNC(n1, n2) |
Menampilkan n1 yang dipotong menjadi n2 di belakang koma:TRUNC(99.999) = 99 TRUNC(99.999,0) = 99 |
Sebagian dengan nama fungsi yang berbeda | TRUNCATE(n1, n2) |
Fungsi TRUNCATE MySQL harus menerima angka input dan bilangan bulat untuk menentukan jumlah akurasi di sebelah kanan titik desimal:TRUNCATE(99.999,0) = 99 |
Fungsi tanggal dan waktu
Fungsi Oracle | Spesifikasi atau implementasi fungsi Oracle | Setara MySQL | Fungsi yang sesuai MySQL | Spesifikasi atau implementasi fungsi MySQL |
---|---|---|---|---|
SYSDATE |
Menampilkan tanggal dan waktu yang ditetapkan saat ini untuk sistem operasi tempat server database berada:SELECT SYSDATE FROM DUAL = 31-JUL-2019 |
Sebagian | SYSDATE() |
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 pada 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 yang berbeda | CURRENT_TIMESTAMP |
MySQL menampilkan format tanggal/waktu yang berbeda dari Oracle. Format tanggal diperlukan (atau fungsi tanggal yang berbeda) agar cocok dengan format tanggal/waktu asli:SELECT CURRENT_TIMESTAMP FROM DUAL = 2019-01-31 06:55:07 |
LOCAL_TIMESTAMP |
Menampilkan tanggal dan waktu saat ini dalam zona waktu sesi dalam nilai jenis data TIMESTAMP :SELECT LOCAL_TIMESTAMP FROM DUAL = 01-JAN-19 10.01.10.123456 PM |
Sebagian dengan format tanggal/waktu yang berbeda. | LOCAL_TIMESTAMP |
MySQL menampilkan format tanggal/waktu yang berbeda dari Oracle. Format tanggal/waktu diperlukan (atau fungsi tanggal yang berbeda) agar cocok 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 yang berbeda | CURRENT_DATE |
MySQL menampilkan format tanggal/waktu yang berbeda dari Oracle. Format tanggal/waktu diperlukan (atau fungsi tanggal yang berbeda) agar cocok 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 yang berbeda | CURRENT_TIMESTAMP |
MySQL menampilkan format tanggal/waktu yang berbeda dari Oracle. Format tanggal/waktu diperlukan (atau menggunakan fungsi tanggal yang berbeda) agar cocok dengan format tanggal/waktu asli:SELECT CURRENT_TIMESTAMP FROM DUAL = 2019-01-31 06:55:07 |
ADD_MONTHS |
Menampilkan tanggal plus bulan bilangan bulat:ADD_MONTHS(SYSDATE, 1) = 31-JAN-19 |
Sebagian dengan nama fungsi yang berbeda | ADDDATE |
Untuk mencapai fungsi 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 dari Oracle. Pemformatan tanggal/waktu diperlukan (atau fungsi tanggal yang berbeda) agar cocok dengan format tanggal/waktu asli. |
EXTRACT (bagian tanggal) |
Menampilkan nilai kolom tanggal/waktu yang ditentukan dari tanggal/waktu atau ekspresi 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 dalam sebulan:LAST_DAY('01-JAN-2019') = 31-JAN-19 |
Sebagian dengan format tanggal/waktu yang berbeda | LAST_DAY |
MySQL menampilkan format tanggal/waktu yang berbeda dari Oracle. Format tanggal/waktu diperlukan (atau fungsi tanggal yang berbeda) agar cocok 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 yang berbeda | PERIOD_DIFF(date1,date2) |
Fungsi PERIOD_DIFF MySQL menampilkan perbedaan dalam bulan sebagai bilangan bulat antara dua titik (diformat sebagai YYMM atau YYYYMM ):PERIOD_DIFF( '201903', '201901') = 2 Untuk mencapai 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 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 yang 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 Oracle | Spesifikasi atau implementasi fungsi Oracle | Setara MySQL | Fungsi yang sesuai MySQL | Spesifikasi atau implementasi fungsi MySQL |
---|---|---|---|---|
DECODE |
Membandingkan ekspresi dengan setiap nilai penelusuran satu per satu menggunakan fungsi
pernyataan IF-THEN-ELSE |
Tidak | CASE |
Gunakan pernyataan CASE MySQL untuk mendapatkan fungsi yang 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 |
Menggunakan MD5 MySQL untuk checksum 128-bit atau fungsi SHA
untuk checksum 160-bit guna menghasilkan nilai hash |
Fungsi konversi
Fungsi Oracle | Spesifikasi atau implementasi fungsi Oracle | Setara MySQL | Fungsi yang sesuai MySQL | Spesifikasi atau implementasi fungsi 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 fungsi Oracle, tetapi dalam kasus tertentu, fungsi tersebut 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 pada sintaksis dan parameter untuk menampilkan hasil yang tepat sebagai 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 angka, membulatkannya ke sejumlah desimal tertentu, lalu menampilkan hasilnya sebagai string, memiliki fungsi yang berbeda dengan
Oracle:CONCAT('$', FORMAT(22.73, 1)) = $22.7 |
TO_DATE |
Fungsi TO_DATE Oracle mengonversi string menjadi tanggal berdasarkan format tanggal/waktu tertentu sumber:TO_DATE( '2019/01/01', 'yyyy-mm-dd') = 01-JAN-2019 |
Sebagian dengan nama fungsi dan format tanggal/waktu yang 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 ke nilai jenis data NUMBER :TO_NUMBER('01234') = 1234 |
Tidak | CAST |
Sebagai alternatif, gunakan fungsi CAST MySQL untuk menampilkan hasil yang sama dengan Oracle TO_NUMBER :CAST('01234' as SIGNED) = 1234 |
Fungsi SELECT bersyarat
Fungsi Oracle | Spesifikasi atau implementasi fungsi Oracle | Setara MySQL | Fungsi yang sesuai MySQL | Spesifikasi atau implementasi fungsi MySQL |
---|---|---|---|---|
CASE |
Pernyataan CASE memilih dari urutan kondisi dan menjalankan pernyataan yang sesuai dengan sintaksis berikut:CASE WHEN condition THEN result [WHEN ...] [ELSE result] END |
Ya | CASE |
Selain fungsi CASE , MySQL juga mendukung penggunaan penanganan kondisional IF/ELSE dalam pernyataan SELECT :CASE WHEN condition THEN result [WHEN ...] [ELSE result] END |
Fungsi null
Fungsi Oracle | Spesifikasi atau implementasi fungsi Oracle | Setara MySQL | Fungsi yang sesuai MySQL | Spesifikasi atau implementasi fungsi MySQL |
---|---|---|---|---|
COALESCE |
Menampilkan ekspresi non-null pertama dalam daftar ekspresi: COALESCE( null, '1', 'a') = a |
Ya | COALESCE |
Setara dengan Oracle:COALESCE( null, '1', 'a') = 1 |
NULLIF |
Bandingkan expr1 dan expr2m. Jika keduanya sama, fungsi
akan menampilkan null. Jika kedua nilai tersebut tidak sama, fungsi akan menampilkan expr1: NULLIF('1', '2') = a |
Ya | NULLIF |
Setara dengan Oracle:NULLIF('1', '2') = a |
NVL |
Ganti null (ditampilkan sebagai kosong) dengan string dalam hasil kueri:
NVL(null, 'a') = a |
Tidak | IFNULL |
Fungsi yang setara di MySQL adalah fungsi IFNULL , yang menggantikan nilai null dengan string tertentu:IFNULL(null, 'a') = a |
NVL2 |
Tentukan nilai yang ditampilkan oleh kueri berdasarkan nilai ekspresi tertentu, yakni null atau bukan null. |
Tidak | CASE |
Pernyataan CASE memilih dari urutan kondisi dan menjalankan pernyataan yang sesuai: CASE WHEN condition THEN result [WHEN ...] [ELSE result] END |
Fungsi lingkungan dan ID
Fungsi Oracle | Spesifikasi atau implementasi fungsi Oracle | Setara MySQL | Fungsi yang sesuai MySQL | Spesifikasi atau implementasi fungsi MySQL |
---|---|---|---|---|
SYS_GUID |
Menghasilkan dan menampilkan ID unik global (nilai RAW) yang terdiri dari
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 SESSION VARIABLES MySQL untuk melihat setelan untuk sesi
saat ini:SHOW SESSION VARIABLES LIKE '%collation%'; = utf8_general_ci |
ROWID |
Server Oracle menetapkan ROWID unik pada setiap baris di setiap tabel untuk mengidentifikasi baris 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 fungsi yang sama dengan fungsi MySQL lainnya. |
ROWNUM |
Menampilkan angka yang mewakili urutan baris yang dipilih oleh Oracle dari tabel atau tabel gabungan. | Tidak | T/A | Jika memungkinkan, coba emulasikan fungsi yang sama dengan fungsi MySQL atau variabel sesi lainnya. |
Fungsi agregat (grup)
Fungsi Oracle | Spesifikasi fungsi atau implementasi Oracle |
MySQL setara |
Fungsi yang sesuai MySQL | Spesifikasi atau implementasi fungsi 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
ukuran: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 serupa dengan Oracle,
antisipasi perbedaan sintaksis dengan kasus tertentu:SELECT GROUP_CONCAT( DEPARTMENT_NAME ORDER BY DEPARTMENT_NAME SEPARATOR ', ') DEPT FROM DEPARTMENTS; |
Pengambilan Oracle 12c
Fungsi Oracle | Spesifikasi fungsi atau implementasi Oracle |
MySQL setara |
Fungsi yang sesuai MySQL | Spesifikasi atau implementasi fungsi 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 kumpulan data tertentu saja:SELECT * FROM EMPLOYEES LIMIT 10; |
Pemfilteran dasar, operator, dan subkueri
Selama konversi, pemfilteran dasar, fungsi operator, dan subkueri relatif mudah, sehingga hanya memerlukan sedikit atau tanpa upaya tambahan.
Catatan konversi
Periksa dan tangani format tanggal karena format Oracle dan MySQL menampilkan hasil default yang berbeda:
- Fungsi
SYSDATE
Oracle secara default menampilkan01-AUG-19
. - Fungsi
SYSDATE()
MySQL secara default menampilkan2019-08-01 12:04:05
. - Format tanggal dan waktu dapat disetel 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 Oracle | Setara MySQL | Fungsi atau sub-kueri yang sesuai MySQL | Spesifikasi atau implementasi fungsi 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 analisis (atau fungsi jendela dan peringkat)
Fungsi analisis Oracle memperluas fungsionalitas dari fungsi analisis SQL standar dengan menyediakan kemampuan untuk menghitung nilai agregat berdasarkan sekumpulan baris. Fungsi ini dapat diterapkan ke kumpulan hasil yang dipartisi secara logis dalam cakupan ekspresi kueri tunggal. Fungsi ini biasanya digunakan bersama dengan analisis dan laporan business intelligence, dengan potensi untuk meningkatkan performa kueri sebagai alternatif untuk mencapai hasil yang sama menggunakan kode SQL non-analitis yang lebih kompleks.
Catatan konversi
- MySQL versi 5.7 tidak menyediakan fungsi analisis untuk mendukung konversi pernyataan SQL yang mudah. Namun, fungsi ini ditambahkan sebagian di MySQL versi 8, sehingga menjadikan konversi fungsi analisis sebagai hal yang perlu dipertimbangkan, yang mungkin memerlukan upaya manual dalam proses migrasi.
- Solusi opsional adalah menulis ulang kode untuk menghapus penggunaan fungsi analisis, kembali ke solusi kode SQL yang lebih tradisional, atau memindahkan logika ini ke lapisan aplikasi.
Tabel berikut mencantumkan fungsi analisis umum Oracle.
Kelompok fungsi | Fungsi terkait | Didukung oleh MySQL 5.7 |
---|---|---|
Analitis 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 |
Jeda | LAG LAG_VARIANCE LAG_VARIANCE_PERCENT LEAD LEAD_VARIANCE LEAD_VARIANCE_PERCENT |
Tidak |
Ekspresi tabel umum (CTE)
CTE menyediakan cara untuk menerapkan logika kode berurutan untuk menggunakan kembali kode SQL
yang mungkin terlalu kompleks atau tidak efisien untuk beberapa penggunaan. CTE dapat diberi nama
dan digunakan beberapa kali di berbagai bagian pernyataan SQL menggunakan
klausul WITH
.
Catatan konversi
- MySQL versi 5.7 tidak mendukung CTE, tetapi MySQL versi 8 mendukungnya.
- Untuk 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 satu pernyataan SQL
yang secara kondisional melakukan operasi DML dalam satu operasi MERGE
,
bukan satu operasi DML, yang berjalan terpisah. Fungsi ini memilih data
dari tabel sumber, kemudian, dengan menentukan struktur logis, secara otomatis
melakukan beberapa operasi DML pada tabel target. Fitur ini membantu Anda
menghindari penggunaan beberapa penyisipan, update, atau penghapusan. Perhatikan bahwa MERGE
adalah pernyataan deterministik, yang berarti bahwa setelah sebuah baris diproses oleh pernyataan MERGE
, baris tersebut tidak dapat diproses lagi menggunakan pernyataan MERGE
yang sama.
Catatan konversi
MySQL versi 5.7 tidak mendukung fungsi MERGE
, tidak seperti Oracle. Untuk
menyimulasikan sebagian fungsi MERGE
, MySQL menyediakan pernyataan
REPLACE
dan
INSERT… ON DUPLICATE KEY UPDATE
:
REPLACE
: Berfungsi dengan cara yang sama seperti pernyataanINSERT
, kecuali jika baris lama dalam tabel memiliki nilai yang sama dengan baris baru untukPRIMARY KEY
atauUNIQUE
, baris lama akan dihapus sebelum baris baru disisipkan.INSERT… ON DUPLICATE KEY UPDATE
: Jika baris yang disisipkan akan menyebabkan nilai duplikat dalam indeksPRIMARY KEY
atauUNIQUE
,UPDATE
dari baris 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 fungsi 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 Optimizer dan pengambilan keputusannya, yang bertujuan untuk menghasilkan rencana eksekusi kueri yang lebih efisien. Oracle mendukung lebih dari 60 petunjuk database yang berbeda. MySQL menyediakan kumpulan petunjuk kueri yang terbatas.
Secara umum, MySQL versi 5.7 mendukung dua jenis petunjuk kueri: OPTIMIZER
HINTS
dan INDEX HINTS
.
Petunjuk MySQL Optimizer
memberikan kemampuan untuk mengontrol perilaku Pengoptimal dalam setiap pernyataan
SQL—misalnya:
SELECT /*+ NO_RANGE_OPTIMIZATION(tbl PRIMARY, col1_idx) */ col1 FROM tbl;
Petunjuk Pengoptimal Tersedia pada MySQL versi 5.7
Nama petunjuk | Ringkasan petunjuk | Cakupan yang berlaku |
---|---|---|
BKA, NO_BKA |
Memengaruhi pemrosesan penggabungan 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 operasi baca multi-rentang | [TABLE] INDEX |
NO_ICP |
Memengaruhi pengoptimalan bentang bawah kondisi indeks | [TABLE] INDEX |
NO_RANGE_OPTIMIZATION |
Memengaruhi pengoptimalan rentang | [TABLE] INDEX |
QB_NAME |
Menetapkan nama ke blok kueri | Pemblokiran kueri |
SEMIJOIN, NO_SEMIJOIN |
Memengaruhi strategi semi-gabungan | Pemblokiran kueri |
SUBQUERY |
Memengaruhi strategi SubQuery IN -ke-EXISTS
materialisasi. |
Pemblokiran kueri |
Petunjuk Indeks MySQL
memberi Pengoptimal informasi tentang cara memilih indeks selama pemrosesan
kueri. Kata kunci USE
, FORCE
, atau IGNORE
digunakan untuk mengontrol proses penggunaan indeks Optimizer—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 Oracle dan MySQL Optimizer, dan karena ada tumpang-tindih terbatas atau tidak sama sekali antara petunjuk kueri Oracle dan MySQL, sebaiknya Anda mengonversi pernyataan Oracle SQL yang menyimpan petunjuk kueri yang tidak ditentukan ke target database MySQL.
Lakukan penyesuaian performa MySQL melalui alat MySQL (misalnya, Workbench MySQL untuk dasbor performa real-time) dan fitur seperti memeriksa kueri menggunakan rencana eksekusi dan menyesuaikan instance atau parameter sesi sesuai dengan kasus penggunaan.
Rencana eksekusi
Tujuan utama dari rencana eksekusi adalah untuk memberikan gambaran mendalam mengenai pilihan
yang dibuat oleh pengoptimal kueri untuk mengakses data database. Pengoptimal kueri
akan menghasilkan rencana eksekusi untuk pernyataan SELECT
, INSERT
, UPDATE
, dan DELETE
untuk pengguna database, yang juga memungkinkan administrator memiliki pandangan
yang lebih baik mengenai kueri tertentu dan operasi DML. Parameter ini sangat berguna saat Anda perlu melakukan penyesuaian performa kueri—misalnya, untuk menentukan performa indeks atau untuk 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 yang identik.
MySQL tidak mendukung sintaksis, fungsi, atau output rencana eksekusi yang sama dengan Oracle.
Contoh
Rencana eksekusi Oracle |
---|
SQL> EXPLAIN PLAN FOR SELECT * FROM EMPLOYEES WHERE EMPLOYEE_ID = 105; |
Rencana eksekusi MySQL |
mysql> EXPLAIN SELECT * FROM EMPLOYEES WHERE EMPLOYEE_ID = 105; |
Prosedur, fungsi, dan pemicu yang tersimpan
PL/SQL adalah bahasa prosedural Oracle yang diperluas yang digunakan untuk membuat, menyimpan, dan menerapkan solusi berbasis kode dalam database. Secara umum, prosedur dan fungsi database yang tersimpan adalah elemen kode yang terdiri dari bahasa prosedural ANSI SQL dan SQL yang diperluas—misalnya, PL/SQL untuk Oracle, PL/pgSQL untuk PostgreSQL, dan bahasa prosedur MySQL untuk MySQL. MySQL menggunakan nama yang sama dengan database untuk bahasa prosedurnya sendiri yang diperluas.
Tujuan dari prosedur dan fungsi yang tersimpan ini adalah memberikan solusi untuk persyaratan yang lebih cocok dijalankan dari dalam database, bukan dari aplikasi (misalnya, performa, kompatibilitas, dan keamanan). Meskipun prosedur dan fungsi tersimpan menggunakan PL/SQL, prosedur tersimpan digunakan terutama untuk melakukan operasi DDL/DML, dan fungsi terutama digunakan untuk melakukan penghitungan guna menampilkan hasil tertentu.
Bahasa prosedur PL/SQL ke MySQL
Dari perspektif migrasi kode Oracle PL/SQL ke MySQL, implementasi prosedural MySQL berbeda dengan Oracle. Oleh karena itu, migrasi kode diperlukan untuk mengonversi fungsi PL/SQL dari Oracle ke prosedur dan fungsi yang tersimpan di MySQL. Selain itu, Paket Oracle dan Isi Paket tidak didukung oleh MySQL, jadi saat Anda melakukan konversi kode, konversikan elemen ini (atau uraikan elemen tersebut) menjadi satu unit kode MySQL. Perhatikan bahwa prosedur dan fungsi yang tersimpan di MySQL jua disebut sebagai rutin.g
Pemilik objek kode
Di Oracle, pemilik prosedur atau fungsi yang tersimpan adalah pengguna tertentu. Di MySQL, pemilik adalah skema spesifik (yang dibuat dalam 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 di bawah pengguna lain, pengguna database harus memiliki hak istimewa CREATE
ANY PROCEDURE
). Untuk menjalankan prosedur tersimpan atau fungsi, pengguna database harus memiliki hak istimewa EXECUTE
.
Di MySQL, untuk membuat elemen kode, pengguna harus memiliki hak istimewa CREATE
ROUTINE
dan hak istimewa EXECUTE
agar dapat berjalan. Klausa DEFINER
MySQL menentukan pembuat pengguna untuk objek kode, dan pengguna harus memiliki hak istimewa yang sesuai seperti CREATE ROUTINE
.
Prosedur dan sintaksis fungsi tersimpan MySQL
Contoh berikut menunjukkan prosedur dan fungsi yang tersimpan di MySQL sintaksis .
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