Converting and optimizing queries from Oracle Database to Cloud SQL for MySQL

This document discusses the basic query differences between Oracle® andCloud SQL for MySQL, and how features in Oracle map to features inCloud SQL for MySQL. It also outlines performance considerations forCloud SQL for MySQL and ways to analyze and optimize query performance onGoogle Cloud. While this document touches on techniques to optimize storedprocedures and triggers for Cloud SQL for MySQL, it does not cover how totranslate PL/SQL code to MySQL stored procedures and functions.

When converting queries from Oracle Database to Cloud SQL for MySQL, thereare certain SQL dialect differences to consider. There are also several built-infunctions that are different or incompatible between the two database platforms.

Basic query differences

While both Oracle and Cloud SQL for MySQL support ANSI SQL, there areseveral fundamental differences when querying data, mainly around the use ofsystem functions.

The following table highlights the differences in theSELECT andFROMsyntax for Oracle and Cloud SQL for MySQL.

Oracle feature nameOracle implementationCloud SQL for MySQL supportCloud SQL for MySQL equivalent
SQL basic syntax for data retrievalSELECT
FROM
WHERE
GROUP BY
HAVING
ORDER BY
YesSELECT
FROM
WHERE
GROUP BY
HAVING
ORDER BY
SELECTfor output printSELECT 1 FROM DUALYesSELECT 1
or
SELECT 1 FROM DUAL
Column aliasesSELECT COL1 AS C1YesSELECT COL1 AS C1
or
SELECT COL1 C1
Table name case sensitivityNo case sensitivity(for example, table name can beorders andORDERS)NoCase sensitive according to the defined table name (for example table namecan be onlyorders orORDERS)

Inline views

Inline views (also known as derived tables) areSELECT statements, located in theFROMclause, and used as a subquery. Inline views can help make complex queriessimpler by removing compound calculations or eliminating join operations, whilecondensing multiple separate queries into a single, simplified query.

Note: Oracle's inline views do not require the use of aliases, whileCloud SQL for MySQL inline views require a specific alias for each one.

The following example outlines a conversion example from Oracle 11g/12c toCloud SQL for MySQL for an inline view.

An inline view in Oracle 11g/12c:

SELECTFIRST_NAME,DEPARTMENT_ID,SALARY,DATE_COLFROMEMPLOYEES,(SELECTSYSDATEASDATE_COLFROMDUAL);

A working view in Cloud SQL for MySQL 5.7 with an alias:

SELECTFIRST_NAME,DEPARTMENT_ID,SALARY,DATE_COLFROMEMPLOYEES,(SELECTSYSDATE()ASDATE_COLFROMDUAL)ASA1;

Joins

Oracle'sjoin types are supported by Cloud SQL for MySQL except for theFULL JOIN.Cloud SQL for MySQL joins support the use of alternate syntax, such as theUSING clause, theWHERE clause instead of theON clause, and theSUBQUERY in theJOIN statement.

The following table shows aJOIN conversion example.

OracleJOIN typeCloud SQL for MySQL supportCloud SQL for MySQLJOIN syntax
INNER JOINYesSELECT E.FIRST_NAME, D.DEPARTMENT_NAMEFROM EMPLOYEES E JOIN DEPARTMENTS DON E.DEPARTMENT_ID = D.DEPARTMENT_ID;
CROSS JOINYesSELECT E.FIRST_NAME, D.DEPARTMENT_NAMEFROM EMPLOYEES E CROSS JOIN DEPARTMENTS D
FULL JOINNoConsider usingUNION withLEFT andRIGHT JOINS:
SELECT E.FIRST_NAME, D.DEPARTMENT_NAMEFROM EMPLOYEES E LEFT JOIN DEPARTMENTS DON E.DEPARTMENT_ID = D.DEPARTMENT_IDUNIONSELECT E.FIRST_NAME, D.DEPARTMENT_NAMEFROM EMPLOYEES E RIGHT JOIN DEPARTMENTS DON E.DEPARTMENT_ID = D.DEPARTMENT_ID;
LEFT JOIN[ OUTER ]YesSELECT E.FIRST_NAME, D.DEPARTMENT_NAMEFROM EMPLOYEES E LEFT JOIN DEPARTMENTS DON E.DEPARTMENT_ID = D.DEPARTMENT_ID;
RIGHT JOIN[ OUTER ]YesSELECT E.FIRST_NAME, D.DEPARTMENT_NAMEFROM EMPLOYEES E RIGHT JOIN DEPARTMENTS DON E.DEPARTMENT_ID = D.DEPARTMENT_ID;
SUBQUERYYesSELECT E.FIRST_NAME, D.DEPARTMENT_NAMEFROM EMPLOYEES E JOIN (SELECT * FROM DEPARTMENTS)DON E.DEPARTMENT_ID = D.DEPARTMENT_ID;

While Cloud SQL for MySQL supports bothUNION andUNION ALL functions,it does not support Oracle'sINTERSECT andMINUS functions:

  • UNION attaches the result sets of twoSELECT statements aftereliminating duplicate records.
  • UNION ALL attaches the result sets of twoSELECT statements withouteliminating duplicate records.
  • INTERSECT returns the intersection of twoSELECT statements, only ifa record exists in the result sets from both queries.
  • MINUS compares two or moreSELECT statements, returning onlydistinct rows from the first query that are not returned by the second query.
Note: UseJOIN,IN,orEXISTS functions as a workaround whenconverting from the OracleINTERSECT orMINUS functions toCloud SQL for MySQL.

The following table shows some Oracle to Cloud SQL for MySQL conversionexamples.

Oracle functionOracle implementationCloud SQL for MySQL supportCloud SQL for MySQL equivalent
UNIONSELECT COL1 FROM TBL1
UNION
SELECT COL1 FROM TBL2
YesSELECT COL1 FROM TBL1
UNION
SELECT COL1 FROM TBL2
UNION ALLSELECT COL1 FROM TBL1
UNION ALL
SELECT COL1 FROM TBL2
YesSELECT COL1 FROM TBL1
UNION ALL
SELECT COL1 FROM TBL2
INTERSECTSELECT COL1 FROM TBL1
INTERSECT
SELECT COL1 FROM TBL2
NoSELECT COL1 FROM TBL1
WHERE COL1 IN
(SELECT COL1 FROM TBL2)
MINUSSELECT COL1 FROM TBL1
MINUS
SELECT COL1 FROM TBL2
NoSELECT A.COL1FROM TBL1 A LEFT JOIN TBL2 BON USING(COL1)WHERE B.COL1 IS NULL

Scalar and group functions

Cloud SQL for MySQL provides an extensive list of scalar (single-row) andaggregation functions. Some of Cloud SQL for MySQL functions are similarto their Oracle counterparts (by name and functionality, or under a differentname but with similar functionality). While some Cloud SQL for MySQLfunctions can have identical names to their Oracle counterparts, they can alsoexhibit different functionalities.

The following table describes where Oracle and Cloud SQL for MySQLcharacter functions are equivalent by name and functionality, and where aconversion is recommended.

Oracle functionOracle implementationCloud SQL for MySQL equivalentCloud SQL for MySQL functionCloud SQL for MySQL implementation
CONCATReturns the first string concatenated with the second string:
CONCAT('A', 1) = A1
YesCONCATCONCAT('A', 1) = A1
CONCAT USING PIPEFNAME |' '| LNAMENoCONCATCONCAT(FNAME, ' ', LNAME)
LOWER orUPPERReturns the string, with all letters in lowercase or in uppercase:
LOWER('SQL') = sql
YesLOWER orUPPERLOWER('SQL') = sql
LPAD/RPADReturnsexpression1, left or right padded tolengthn characters with the sequence of characters inexpression2:
LPAD('A',3,'*') = **A
YesLPAD orRPADLPAD('A',3,'*') = **A
SUBSTRReturns a portion of the string, starting at positionx (in this case 3), with a length ofy. The first position in the string is 1.
SUBSTR('MySQL', 3, 3)= SQL
YesSUBSTRSUBSTR('MySQL', 3, 3)= SQL
INSTRReturns the position (index) of a string from a given string:
INSTR('MySQL', 'y')= 2
YesINSTRINSTR('MySQL', 'y')= 2
REPLACEReturns a string with every occurrence of a search string replaced with areplacement string:
REPLACE('ORADB', 'ORA', 'MySQL')= MySQLDB
YesREPLACEREPLACE('ORADB', 'ORA', 'MySQL')= MySQLDB
TRIMTrim leading or trailing characters (or both) from a string:
TRIM(both '-' FROM '-MySQL-')= MySQLTRIM(' MySQL ')= MySQL
YesTRIMTRIM(both '-' FROM '-MySQL-')= MySQLTRIM(' MySQL ')= MySQL
LTRIM/RTRIMRemoves from the left side or right side of a string all characters thatappear in the search:
LTRIM(' MySQL', ' ')= MySQL
PartiallyLTRIM or RTRIMOracleLTRIM andRTRIM functions take a secondparameter that specifies which leading or trailing characters to removefrom the string. Cloud SQL for MySQL functions only remove leading andtrailing whitespaces from the given string:
LTRIM(' MySQL')= MySQL
ASCIITakes a single character and returns its numeric ASCII code:
ASCII('A') = 65
YesASCIIASCII('A') = 65
CHRReturns the ASCII code value, which is a numeric value from 0 through 225,to a character:
CHR(65) = A
Requires a different function nameCHARCloud SQL for MySQL uses theCHAR function for the samefunctionality, so you need to change the function name:
CHAR(65) = A
LENGTHReturns the length of a given string:
LENGTH('MySQL') = 5
YesLENGTHLENGTH('MySQL') = 5
REGEXP_REPLACESearches a string for a regular expression pattern:
REGEXP_REPLACE('John', '[hn].', 'e') = Joe
NoN/ASupported as of MySQL version 8. As a workaround, use theREPLACE function if possible or move the logic to theapplication layer.
REGEXP_SUBSTRExtends the functionality of theSUBSTR function by searchinga string for a regular expression pattern:
REGEXP_SUBSTR('https://console.cloud.google.com/sql/instances','https://([[:alnum:]]+\.?){3,4}/?')=https://console.cloud.google.com/
NoN/ASupported as of MySQL version 8. As a workaround, use theSUBSTR function if possible or move the logic to theapplication layer
REGEXP_COUNTReturns the number of times a pattern occurs in a source stringNoN/ANo equivalent function available for Cloud SQL for MySQL. Move thislogic to the application layer.
REGEXP_INSTRSearch a string position (index) for a regular expression patternNoN/ASupported as of MySQL version 8. If on an older version, move this logic tothe application layer.
REVERSEReturns the reversed string for a given string:
REVERSE('MySQL')= LQSyM
YesREVERSEREVERSE('MySQL')= LQSyM

The following table describes where Oracle and Cloud SQL for MySQL numericfunctions are equivalent by name and functionality, and where a conversion isrecommended.

Oracle functionOracle implementationCloud SQL for MySQL equivalentCloud SQL for MySQL functionCloud SQL for MySQL implementation
ABSReturns the absolute value of a given number:
ABS(-4.6) = 4.6
YesABSABS(-4.6) = 4.6
CEILReturns the smallest integer that is greater than or equal to the givennumber:
CEIL(21.4) = 22
YesCEILCEIL(21.4) = 22
FLOORReturns the largest integer equal to or less than the given number:
FLOOR(-23.7) = -24
YesFLOORFLOOR(-23.7) = -24
MODReturns the remainder ofm divided byn:
MOD(10, 3) = 1
YesMODMOD(10, 3) = 1
ROUNDReturnsn rounded to integer places to the right ofthe decimal point:
ROUND(1.39, 1) = 1.4
YesROUNDROUND(1.39, 1) = 1.4
TRUNC(number)Returnsn1 truncated ton2decimal places. The second parameter is optional.
TRUNC(99.999) = 99TRUNC(99.999, 0) = 99
Requires a different function nameTRUNCATE(number)Cloud SQL for MySQL function has a different name and the secondparameter is mandatory.
TRUNCATE(99.999, 0) = 99

The following table describes where Oracle and Cloud SQL for MySQLdatetime functions are equivalent by name and functionality, and where aconversion is recommended.

Oracle functionOracle implementationCloud SQL for MySQL equivalentCloud SQL for MySQL functionCloud SQL for MySQL implementation
SYSDATEReturns the current date and time set for the operating system on which thedatabase server resides:
SELECT SYSDATEFROM DUAL;= 31-JUL-2019
YesSYSDATE()

Cloud SQL for MySQLSYSDATE() mustinclude parentheses and returns by default a differentdatetime format than the OracleSYSDATEfunction:

SELECT SYSDATE()FROM DUAL;= 2019-01-31 10:01:01.0

You can change thedatetime format at the session level

SYSTIMESTAMPReturns the system date, including fractional seconds and time zone:
SELECT SYSTIMESTAMP FROM DUAL= 01-JAN-19 07.37.11.622187000 AM +00:00
Requires a different function nameCURRENT_TIMESTAMPCloud SQL for MySQL function returns a differentdatetimeformat by default. To reformat the output, use theDATE_FORMAT() function.
SELECT CURRENT_TIMESTAMPFROM DUAL;= 2019-01-31 06:55:07
LOCAL_TIMESTAMPReturns the current date and time as aTIMESTAMP type:
SELECT LOCALTIMESTAMPFROM DUAL= 01-JAN-19 10.01.10.123456 PM
Returns a differentdatetime formatLOCAL_TIMESTAMPCloud SQL for MySQL function returns a differentdatetimeformat than the default format for Oracle. To reformat the output, use theDATE_FORMAT() function.
SELECT LOCAL_TIMESTAMPFROM DUAL= 2019-01-01 10:01:01.0
CURRENT_DATEReturns the current date:
SELECT CURRENT_DATEFROM DUAL= 31-JAN-19
Returns a differentdatetime formatCURRENT_DATECloud SQL for MySQL function returns a differentdatetimeformat. To reformat the output, use theDATE_FORMAT()function.
SELECT CURRENT_DATEFROM DUAL= 2019-01-31
CURRENT_TIMESTAMPReturns the current date and time:
SELECT CURRENT_TIMESTAMPFROM DUAL= 31-JAN-19 06.54.35.543146 AM +00:00
Returns a differentdatetime formatCURRENT_TIMESTAMPCloud SQL for MySQL function returns a differentdatetimeformat. To reformat the output, use theDATE_FORMAT()function.
SELECT CURRENT_TIMESTAMPFROM DUAL= 2019-01-31 06:55:07
ADD_MONTHSReturns the date plus integer months:
ADD_MONTHS(SYSDATE, 1)= 31-JAN-19
Requires a different function nameADDDATECloud SQL for MySQL function returns a differentdatetimeformat. To reformat the output, use theDATE_FORMAT()function.
ADDDATE(SYSDATE(), 1)= 2019-08-01 06:42:49.0
EXTRACT (date part)Returns the value of adatetime field based on an intervalexpression:
EXTRACT(YEAR FROM DATE '2019-01-31')= 2019
YesEXTRACT (date part)EXTRACT(YEAR FROM DATE '2019-01-31')= 2019
LAST_DAYReturns the last day of the month for a given date:
LAST_DAY('01-JAN-2019')= 31-JAN-19
PartiallyLAST_DAYCloud SQL for MySQL function returns a differentdatetimeformat than default format for Oracle. To reformat the output, use theDATE_FORMAT() function.
LAST_DAY('2019-01-01')= 2019-01-31
MONTH_BETWEENReturns the number of months between the given datesdate1 anddate2:
MONTHS_BETWEEN(SYSDATE, SYSDATE-60)= 1.96
PartiallyPERIOD_DIFFCloud SQL for MySQLPERIOD_DIFF function returns thedifference in months as an integer number between two periods (formatted asYYMM orYYYYMM):
PERIOD_DIFF('201903', '201901')= 2
TO_CHAR (Datetime)Converts a number,datetime, or timestamp type to astring type
TO_CHAR(SYSDATE,'DD-MM-YYYY HH24:MI:SS')= 01-01-2019 10:01:01
Requires a different function nameDATE_FORMATCloud SQL for MySQLDATE_FORMAT function formats a datevalue according to a format string:
DATE_FORMAT(SYSDATE(),'%d-%m-%Y %H:%i:%s')01-01-2019 10:01:01

The following table describes where Oracle and Cloud SQL for MySQLencoding and decoding functions are equivalent by name and functionality, andwhere a conversion is recommended.

Oracle functionOracle implementationCloud SQL for MySQL equivalentCloud SQL for MySQL functionCloud SQL for MySQL implementation
DECODECompares the expression to each search value one by one using thefunctionality of anIF-THEN-ELSE statementNoCASEUse Cloud SQL for MySQLCASE statement for similarfunctionality
DUMPReturns aVARCHAR2 value containing the data type code, lengthin bytes, and internal representation of expressionNoN/ANot supported
ORA_HASHComputes a hash value for a given expressionNoMD5 or SHAUse theMD5 function for 128-bit checksums or theSHA function for 160-bit checksums

The following table describes where Oracle and Cloud SQL for MySQLconversion functions are equivalent by name and functionality, and where aconversion is recommended.

Oracle functionOracle implementationCloud SQL for MySQL equivalentCloud SQL for MySQL functionCloud SQL for MySQL implementation
CASTConverts one built-in data type or collection-typed value into anotherbuilt-in data type or collection-typed value:
CAST('1' as int) + 1= 2
PartiallyCASTAdjust depending on if an explicit or implicit conversion is required:
CAST('1' AS SIGNED) + 1= 2
CONVERTConverts a character string from one-character set to another:
CONVERT ('Ä Ê Í Õ Ø A B C D E ', 'US7ASCII', 'WE8ISO8859P1')= ?? ?? ?? A B C
PartiallyCONVERTCloud SQL for MySQLCONVERT function requires someadjustments to the syntax and parameters:
CONVERT('Ä Ê Í A B C ' USING utf8)= Ä Ê Í A B C
TO_CHAR (string/numeric)The function converts a number or date to a string:
TO_CHAR(22.73,'$99.9')= $22.7
NoFORMATCloud SQL for MySQLFORMAT function converts a number toa format like#,###,###.##, rounding it to a decimal place andthen returning the result as a string:
CONCAT('$',FORMAT(22.73, 1))= $22.7
TO_DATEOracle'sTO_DATE function converts a string to a date based onadatetimecode format:
TO_DATE('2019/01/01', 'yyyy-mm-dd')= 01-JAN-2019
Requires a different function nameSTR_TO_DATECloud SQL for MySQLSTR_TO_DATE function takes a string andreturns a date based on adatetime format:
STR_TO_DATE('2019/01/01', '%Y/%m/%d')= 2019-01-01
TO_NUMBERConverts expression to a value of aNUMBER data type:
TO_NUMBER('01234')= 1234
Requires a different function nameCASTUse the Cloud SQL for MySQLCAST function to return the sameresult as the OracleTO_NUMBER function:
CAST('01234' as SIGNED)= 1234

The following table describes where Oracle and Cloud SQL for MySQLconditionalSELECT functions are equivalent by name and functionality, andwhere a conversion is recommended.

Oracle functionOracle implementationCloud SQL for MySQL equivalentCloud SQL for MySQL functionCloud SQL for MySQL implementation
CASETheCASE statement chooses from a sequence of conditions andruns a corresponding statement with the following syntax:
CASE WHEN condition THEN result[WHEN ...] [ELSE result]END
YesCASEIn addition to theCASE function, Cloud SQL for MySQLsupports the use ofIF/ELSEconditional handling inside theSELECT statement:
CASE WHEN condition THEN result[WHEN ...] [ELSE result]END

The following table describes where Oracle and Cloud SQL for MySQL nullfunctions are equivalent by name and functionality, and where a conversion isrecommended.

Oracle functionOracle implementationCloud SQL for MySQL equivalentCloud SQL for MySQL functionCloud SQL for MySQL implementation
COALESCEReturns the first non-null expression in the expression list:
COALESCE(null, '1', 'a')= a
YesCOALESCECOALESCE(null, '1', 'a')= 1
NULLIFPerforms a comparison betweenexpression1 andexpression2. If they areequal, the function returnsnull.If they are not equal, the function returnsexpression1:
NULLIF('1', '2')= a
YesNULLIFNULLIF('1', '2')= a
NVLReplaces anull value with a string in the results of a query:
NVL(null, 'a')= a
NoIFNULLIFNULL(null, 'a')= a
NVL2Determines the value returned by a query based on whether anexpression is null or not nullNoCASETheCASE statementchooses from a sequence of conditions and runs a correspondingstatement:
CASE WHEN condition THEN result[WHEN ...] [ELSE result]END

The following table describes where Oracle and Cloud SQL for MySQLenvironment and identifier functions are equivalent by name and functionality,and where a conversion is recommended.

Oracle functionOracle implementationCloud SQL for MySQL equivalentCloud SQL for MySQL functionCloud SQL for MySQL implementation
SYS_GUIDGenerates and returns a globally unique identifier (RAW value)made up of 16 bytes:
SELECT SYS_GUID()FROM DUAL=8EFA4A31468B4C6DE05011AC0200009E
NoREPLACE andUUIDAs a workaround, use theREPLACE andUUID functions to simulate theSYS_GUIDfunction:
REPLACE(UUID(), '-', '')
UIDReturns an integer that uniquely identifies the session user (the user whois logged on):
SELECT UID FROM DUAL= 43
NoN/AN/A
USERReturns the username of the user who is connected to the currentsession:
SELECT USER FROM DUAL=username
YesUSER + INSTR + SUBSTRThe Cloud SQL for MySQLUSER function returns theusername and host name (root@IP_ADDRESS) for theconnection. To retrieve only the username, use the additional supportingfunctions:
SELECTSUBSTR(USER(), 1, INSTR(USER(), '@') -1) FROM DUAL= root
USERENVReturns information about the current Oracle session, such as the languageof the session:
SELECT USERENV('LANGUAGE')FROM DUAL= ENGLISH_AMERICA.AL32UTF8
NoSHOW SESSIONVARIABLESCloud SQL for MySQLSHOW SESSION VARIABLES statementreturns the settings for the current session:
SHOW SESSION VARIABLES LIKE '%collation%';= utf8_general_ci
ROWIDOracle assigns each row of a table a uniqueROWID to identifythe row in the table. TheROWID is the address of the rowthat contains the data object number, the data block of the row, the rowposition, and the data file.PartiallyN/AROW_NUMBER() is available starting in MySQL8.0. If you're using an earlier version, emulate the same functionality by usinga session variable@row_number.
ROWNUMReturns a number that represents the order that a row is returned by anOracle tablePartiallyN/AROW_NUMBER() is available starting in MySQL 8.0. If you'reusing an earlier version, emulate the same functionality by using asession variable@row_number.

The following table describes where Oracle and Cloud SQL for MySQLaggregate (group) functions are equivalent by name and functionality, and wherea conversion is recommended.

Oracle functionOracle implementationCloud SQL for MySQL equivalentCloud SQL for MySQL functionCloud SQL for MySQL implementation
AVGReturns the average value of a column or expressionYesAVGEquivalent to Oracle
COUNTReturns the number of rows returned by a queryYesCOUNTEquivalent to Oracle
COUNT(DISTINCT)Returns the number of unique values in the column or expressionYesCOUNT(DISTINCT)Equivalent to Oracle
MAXReturns the maximum value of a column or expressionYesMAXEquivalent to Oracle
MINReturns the minimum value of a column or expressionYesMINEquivalent to Oracle
SUMReturns the sum of a value of a column or expressionYesSUMEquivalent to Oracle
LISTAGGOrders the data within each group specified in theORDER BYclause and concatenates the values of the measure column:
SELECT LISTAGG(DEPARTMENT_NAME, ', ')WITHIN GROUP(ORDER BY DEPARTMENT_NAME) DEPTFROM DEPARTMENTS;-- Single line results= Accounting, Administration, Benefits, Construction
Requires a different function name and syntaxGROUP_CONCATUse Cloud SQL for MySQLGROUP_CONCAT function to returnequivalent results:
SELECT GROUP_CONCAT(DEPARTMENT_NAME ORDER BY DEPARTMENT_NAME SEPARATOR ', ') DEPTFROM DEPARTMENTS;-- Single line results= Accounting, Administration, Benefits, Construction

The following table describes where Oracle and Cloud SQL for MySQLFETCHfunction is equivalent by name and functionality.

Oracle functionOracle implementationCloud SQL for MySQL equivalentCloud SQL for MySQL functionCloud SQL for MySQL implementation
FETCHRetrieves a specified number of rows from the result set of a multi-rowquery:
SELECT * FROMEMPLOYEESFETCH FIRST 10 ROWS ONLY;
YesLIMITUse the MySQLLIMITclause to retrieve rows from a query:
SELECT * FROMEMPLOYEESLIMIT 10;

Basic filtering, operators, and subqueries

Basic filtering, operator functions, and subqueries are relativelystraightforward to convert, with nominal effort required. Most of the effortrevolves around converting date formats because Oracle andCloud SQL for MySQL use different default date formats:

  • The OracleSYSDATE function returns this format by default:01-AUG-19.
  • The Cloud SQL for MySQLSYSDATE() function returns this format bydefault:2019-08-01 12:04:05.

To set date and time formats, use the MySQLDATE_FORMAT or theSTR_TO_DATE functions.

The following table describes where Oracle and Cloud SQL for MySQL basicfiltering, operators, and subquery functions are equivalent by name andfunctionality, and where a conversion is recommended.

Oracle functionOracle implementationCloud SQL for MySQL equivalentCloud SQL for MySQL function
EXISTS/NOT EXISTSYesEXISTS/NOT EXISTSSELECT * FROM DEPARTMENTS DWHERE EXISTS (SELECT 1 FROM EMPLOYEES E WHERE E.DEPARTMENT_ID = D.DEPARTMENT_ID);
IN/NOT INYesIN/NOT INSELECT * FROM DEPARTMENTS DWHERE DEPARTMENT_ID IN (SELECT DEPARTMENT_ID FROM EMPLOYEES E);-- ORSELECT * FROM EMPLOYEESWHERE (EMPLOYEE_ID, DEPARTMENT_ID)IN((100, 90));
LIKE/NOT LIKEYesLIKE/NOT LIKESELECT * FROM EMPLOYEESWHERE FIRST_NAME LIKE '_e_n%';
BETWEEN/NOT BETWEENYesBETWEEN/NOT BETWEENSELECT * FROM EMPLOYEESWHERE EXTRACT(YEAR FROM HIRE_DATE)NOT BETWEEN 2001 and 2004;
AND/ORYesAND/ORSELECT * FROM EMPLOYEESWHERE DEPARTMENT_ID IN(100, 101)AND (SALARY >= 1000 OR HIRE_DATE <= '2006-02-05');
SubQueryYesSubQueryCloud SQL for MySQL supports subqueries in theSELECTclause, in theJOIN clause, and for filtering in theWHERE/AND clauses:
-- SELECT SubquerySELECT D.DEPARTMENT_NAME, (SELECT AVG(SALARY) AS AVG_SAL FROM EMPLOYEES E WHERE E.DEPARTMENT_ID = D.DEPARTMENT_ID) AVG_SALFROM DEPARTMENTS D;-- JOIN SubquerySELECT FIRST_NAME, LAST_NAME, SALARYFROM EMPLOYEES E JOIN (SELECT * FROM DEPARTMENTS WHERE LOCATION_ID = 2700) DON E.DEPARTMENT_ID = D.DEPARTMENT_ID;-- Filtering SubquerySELECT FIRST_NAME, LAST_NAME, SALARYFROM EMPLOYEESWHERE SALARY < (SELECT AVG(SALARY) FROM EMPLOYEES);
OperatorsYesOperatorsCloud SQL for MySQL supports all basic operators:
> | >= | < | <= | = | <> | !=

Best practices for Cloud SQL for MySQL queries

To maintain comparable levels of performance between Cloud SQL for MySQLand Oracle, you might need to optimize your queries. These optimizations includechanging the index structures and adjusting the database schema. This sectionprovides a few guidelines to help you achieve comparable query performance onCloud SQL for MySQL.

Create a clustered index

When using theInnoDB storage engine,a best practice is to define a table with a primary key, because this keycreates a clustered index on that table. In addition to improving queryperformance, this approach also lets you create additional secondaryindexes. You want to avoid creating too many indexes, though. Having redundantindexes does not improve performance and can slow downDML execution. This best practice leads to a second best practice: regularly monitorfor redundant indexes, and if you have any redundant ones, drop them from thedatabase.

Use the following query to identify tables with no primary keys so that you cancreate primary keys for them:

mysql>SELECTt.table_schema,t.table_nameFROMinformation_schema.tablestLEFTJOINinformation_schema.statisticssONt.table_schema=s.table_schemaANDt.table_name=s.table_nameANDs.non_unique=0WHEREs.table_nameISNULLANDt.table_schemaNOTIN('sys','information_schema','mysql','performance_schema')ANDt.`TABLE_TYPE` <>'VIEW';

Use the following query to find tables which have no indexes so you can createindexes for them:

mysql>SELECTt.table_schema,t.table_nameFROMINFORMATION_SCHEMA.tablestWHEREtable_nameNOTIN(SELECTtable_nameFROM(SELECTtable_name,index_nameFROMinformation_schema.statisticsGROUPBYtable_name,index_name)tab_ind_colsGROUPBYtable_name)ANDtable_schemaNOTIN('sys','information_schema','mysql','performance_schema')ANDTABLE_TYPE <>'VIEW';

Use the following query to check for redundant indexes so that you can removethe redundancies:

mysql>SELECT*FROMsys.schema_redundant_indexes;

Adjust query parameters

To tune query performance, you might need to adjust session parameters.Cloud SQL for MySQL has a set offlags that you can alter for this purpose, including the following flags:

  • InnoDB-related parameters
  • SORT parameters
  • JOIN parameters
  • Cache-handling parameters

Monitor queries

Slow-running queries can cause the system to stop responding or lead to otherbottlenecks, so it's important to regularly monitor queries.

There are several ways to diagnose slow-running SQL statements:

  • Use theCloud SQL for MySQL dashboard for real-time and historical insights on slow-running queries.
  • UseCloud Monitoring to monitor the Cloud SQL for MySQL slow query log.
  • Use the Cloud SQL for MySQLstatement_analysis view to see theruntime statistics about a SQL statement:

    mysql>SELECT*FROMsys.statement_analysis;

Analyze Cloud SQL for MySQL queries

The query optimizer in Cloud SQL for MySQL generates an execution plan forSELECT,INSERT,UPDATE, andDELETE statements. These plans are usefulwhen you adjust a slow-running query. There are a few considerations to keep inmind:

  • Execution plans are not database objects that need to be migrated;rather, they are a tool for analyzing performance differences betweenOracle and Cloud SQL for MySQL running the same statement on identicaldatasets.
  • Cloud SQL for MySQL does not support the same execution plansyntax, functionality, or output as Oracle.

Here is an example plan to illustrate the differences between an Oracleexecution plan and a Cloud SQL for MySQL execution plan:

SQL>EXPLAINPLANFORSELECT*FROMEMPLOYEESWHEREEMPLOYEE_ID=105;SQL>SELECT*FROMTABLE(DBMS_XPLAN.DISPLAY(FORMAT=>'ALL +OUTLINE'));Planhashvalue:1833546154---------------------------------------------------------------------------------------------|Id|Operation|Name|Rows|Bytes|Cost(%CPU)|Time|---------------------------------------------------------------------------------------------|0|SELECTSTATEMENT||1|69|1(0)|00:00:01||1|TABLEACCESSBYINDEXROWID|EMPLOYEES|1|69|1(0)|00:00:01||*2|INDEXUNIQUESCAN|EMP_EMP_ID_PK|1||0(0)|00:00:01|---------------------------------------------------------------------------------------------mysql>EXPLAINSELECT*FROMEMPLOYEESWHEREEMPLOYEE_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|+----+-------------+-----------+------------+-------+---------------+---------+---------+-------+------+----------+-------+

Optimize stored procedures and triggers

In contrast to Oracle, Cloud SQL for MySQL stored procedures andfunctions are parsed at each execution. A helpful tool for benchmarking storedprocedure and function performance is theMySQLBENCHMARK() utility.This tool takes two parameters, an iteration count and an expression, andestimates the runtime of the given expression (for example, stored procedure,function, andSELECT statement). The output represents the approximate totalruntime over all iterations.

The following is an example to illustrate theBENCHMARK() utility:

-- SELECT Expression Examplemysql>selectbenchmark(10000000,'select sysdate()');+-----------------------------------------+|benchmark(10000000,'select sysdate()')|+-----------------------------------------+|0|+-----------------------------------------+1rowinset(0.12sec)-- Result: Run time of 0.12 sec for 1,0000,000 iterations-- FUNCTION Examplemysql>selectbenchmark(1000000,func1());+-----------------------------+|benchmark(1000000,func1())|+-----------------------------+|0|+-----------------------------+1rowinset(2.54sec)-- Result: Run time of 2.54 sec for 1,000,000 iterations

If you notice a performance regression during the conversion, use theMySQLEXPLAINcommand to identify possible factors contributing to the regression. One commonsolution for slow performance is to alter a table index structure to accommodatethe MySQL optimizer. Another common practice is to optimize a converted PL/SQLcode by reducing unnecessary data retrieval or by using temporary tables withinthe procedural MySQL code.

What's next

  • Explore more aboutMySQL user accounts.
  • Explore reference architectures, diagrams, and best practices about Google Cloud.Take a look at ourCloud Architecture Center.

Except as otherwise noted, the content of this page is licensed under theCreative Commons Attribution 4.0 License, and code samples are licensed under theApache 2.0 License. For details, see theGoogle Developers Site Policies. Java is a registered trademark of Oracle and/or its affiliates.

Last updated 2025-11-24 UTC.