To show all Stored Procedures...
SHOW PROCEDURE STATUS;
To show all Stored Procedures for a specified database...
SHOW PROCEDURE STATUS WHERE db = 'database_name';
To show all Stored Procedures for a specified database by querying the data dictionary...
SELECT routine_name
FROM information_schema.routines
WHERE routine_type = 'PROCEDURE'
AND routine_schema = 'database_name';
To view the definition of a Stored Procedure...
SHOW CREATE PROCEDURE procedure_name\G;
If you haven't been granted the privileges to create procedures, functions and triggers already, then you need...
GRANT CREATE ROUTINE ON myDB.* TO myUser;
DELIMITER $$
CREATE DEFINER = 'myUser'
PROCEDURE IF NOT EXISTS myProc(myParam)
COMMENT 'Procedure to output a row to a text file'
LANGUAGE SQL
NOT DETERMINISTIC
SQL SECURITY DEFINER
BEGIN
SELECT myRow
FROM myTable
INTO OUTFILE 'myOutfile';
END$$
DELIMITER ;
Another example showing how to pass and use a date parameter to a stored procedure...
DELIMITER //
DROP PROCEDURE IF EXISTS myProc;
CREATE PROCEDURE myProc(IN myDate DATE)
BEGIN
SELECT @myCommand := CONCAT("SELECT 'myHeading1',
'myHeading2'
UNION (SELECT myCol1,
myCol2
INTO OUTFILE '/tmp/myOutput", DATE_FORMAT(myDate,'%d%m%Y'),'.csv',"'
FIELDS TERMINATED BY ','
LINES TERMINATED BY '\r\n'
FROM myDB.myTable
WHERE DATE_FORMAT(myCol2, '%d/%m/%Y') = DATE_FORMAT(?,'%d/%m/%Y')
ORDER BY myCol1)");
PREPARE stmt FROM @myCommand;
EXECUTE stmt USING @myDate;
DEALLOCATE PREPARE stmt;
END//
DELIMITER ;
If you have saved your DDL to a script file, you can run it using...
source myscript.sql
Make appropriate Grants...
GRANT EXECUTE ON PROCEDURE myDB.myProc TO aUser;
GRANT ALTER ROUTINE ON PROCEDURE myDB.myProc TO aUser;
GRANT EXECUTE, ALTER ROUTINE ON PROCEDURE myDB.myProc TO aUser;
GRANT ALL PRIVILEGES ON PROCEDURE myDB.myProc TO aUser;
aUser will be able to Execute myProc
aUser will be able to ALTER PROCEDURE myProc()
aUser will be able to Execute and Alter myProc
As above
CREATE FUNCTION myfunc()
RETURNS CHAR(36)
BEGIN
mycode;
END;
Mark functions as DETERMINISTIC if the result only depends on input arguments (e.g. pure math).
Specify SQL characteristics (NO SQL or READS SQL DATA) to make them safe for binary logging.
Avoid Non-deterministic functions (NOW(), UUID(), RAND()).
Avoid functions that read or write tables.
If an unsafe construct is unavoidable, consider moving the logic to application code.
If the divergence is not material (i.e. if the application will continue to give the correct results) then it may be sufficient to clearly document your choice.
To identify existing functions likely to cause replication safety concerns...
SELECT ROUTINE_SCHEMA,
ROUTINE_NAME,
ROUTINE_TYPE,
IS_DETERMINISTIC,
SQL_DATA_ACCESS
FROM information_schema.ROUTINES
WHERE ROUTINE_TYPE = 'FUNCTION';
Check for any functions marked as:
IS_DETERMINISTIC = NO
SQL_DATA_ACCESS = MODIFIES SQL DATA or CONTAINS SQL
This script gives a suggested rewrite for existing functions.
NOTE that any function that modifies SQL data will never be considered replication-safe even after making the suggested changes.i.e. Adding DETERMINISTIC to the function is just an assertion that the function is deterministic, it is up to you to make sure that is true.SELECT ROUTINE_SCHEMA,
ROUTINE_NAME,
IS_DETERMINISTIC,
SQL_DATA_ACCESS,
CONCAT('-- Suggested rewrite for ', ROUTINE_SCHEMA, '.', ROUTINE_NAME, ' :',
'\nCREATE FUNCTION ', ROUTINE_SCHEMA, '.', ROUTINE_NAME, ' (...)',
'\nRETURNS <datatype>',
CASE
WHEN IS_DETERMINISTIC = 'NO' THEN '\n-- Consider adding DETERMINISTIC if safe'
ELSE '\nDETERMINISTIC'
END,
CASE SQL_DATA_ACCESS
WHEN 'CONTAINS SQL' THEN '\n-- Consider declaring as NO SQL or READS SQL DATA'
WHEN 'MODIFIES SQL DATA' THEN '\n-- WARNING: function modifies data; unsafe for replication'
ELSE CONCAT('\n', SQL_DATA_ACCESS)
END,
'\nBEGIN',
'\n -- function body here',
'\nEND;'
) AS suggested_definition
FROM information_schema.ROUTINES
WHERE ROUTINE_TYPE = 'FUNCTION';
call myProc();
call myProc(myParamValue);
To dump just stored procedures, functions and triggers for a specific database...
mysqldump -h myhost -u myuser -p --no-create-db --no-data --no-create-info --routines --databases mydatabase > routines.sql
To load them into a new instance (with same usernames)...
For different usernames just edit the routines.sql script before running itmysql -u root -p
source routines.sql
DROP PROCEDURE myProc;
DROP PROCEDURE IF EXISTS myProc;
DELIMITER $$
CREATE DEFINER=`myuser`@`%`
PROCEDURE `myproc`(IN inid VARCHAR(5))
BEGIN
SELECT mycolumn
FROM mytable
WHERE myid = 'inid';
END$$
DELIMITER ;
call myProc(12345);
This executes in approximately 30 secondsSELECT mycolumn
FROM mytable
WHERE myid = '12345;
This executes in less than 1 secondUnfortunately it's not yet (January 2023) possible to EXPLAIN a stored procedure, so we can't see whether the same execution plan is being used.
However, a fair guess would be that an index is being used standalone but not by the procedure... but why?
Perhaps stackoverflow has the answer?
https://stackoverflow.com/questions/16752922/mysql-stored-procedure-is-slower-20-times-than-standard-queryThe accepted answer suggests a bug related to differences in database characterset versus table characterset.
Let's look at table status...
SHOW TABLE STATUS LIKE 'mytable'\G
Let's have a look at the CREATE TABLE statement for this table...
SHOW CREATE TABLE mytable\G
So, this table is created with a default characterset of utf8...
Does this differ from our database?...
status
Hmm, could the root cause of this be related to a mismatch between utf8 and utf8mb4?
Changing the table characterset and collation to match the database seems like a good idea but obviously carries some risk... what other impact could changing the characterset of a table have? How did it end up different in the first place, and was this a concious decision?... and, in a production environment, this really should go through Change Management. How can we work around the issue quickly whilst we ponder the above?
Let's force a conversion in our stored procedure....
DELIMITER $$
CREATE DEFINER=`myuser`@`%`
PROCEDURE `myproc`(IN inid VARCHAR(5))
BEGIN
SELECT mycolumn
FROM mytable
WHERE myid = CONVERT(inid utf8) COLLATE utf8_general_ci;
END$$
DELIMITER ;
call myProc(12345);
This now executes in less than 1 secondThis is an ok workaround, but real fix would be to change the table so that the character sets and collation match.
Note that the root cause here is the change in default character set (from utf8 to utf8mb4) between MySQL 5.7 and MySQL 8.0. The utf8 character set is an alias for utf8mb3 which is deprecated in MySQL 8.0. Eventually, utf8 is mooted to become an alias for utf8mb4, so this problem could disappear in later versions.