MySQL - Fast query but slow Stored Procedures - mysql

I know this question has been discussed quite a lot here. But I have a particular case when I need to pass a list of parameters (comma - separated) which prevents me to have a local variable declared and used for input parameter.
As pointed out in the above discussion, it is suggested to declare a local variable and assign the parameters to this variable. However, what should I do in case my parameter is of type Text and can be comma - separated list?
For example -
CREATE DEFINER=`Admin`#`%` PROCEDURE `MyReport`(
p_myparameter_HK Text
)
BEGIN
SELECT
*
FROM MyTable
WHERE
(find_in_set(MyTable.column_HK, p_myparameter_HK) <> 0 OR MyTable.column_HK IS NULL)
;
END
Performance:
Query
If I just run the query - 300 ms
Stored Procedure
CALL MyReport('0000_abcd_fake_000')
This procedure keeps running endlessly.
My question is, how can I disable parameter sniffling and use local variable instead of find_in_set to match the query performance.

The times that I have needed to pass an arbitrary list of things to a Stored Procedure, I did it this way:
CREATE (or already have) a TABLE for passing the info in. Both the caller and the Procedure know the name of the procedure. (Or it could be passed in, but adds some messy "prepare-executes".)
Do a bulk INSERT into that table. (INSERT INTO tbl (a,b) VALUES (...), (..), ...;)
Perform JOINs or whatever to use the table efficiently.
In my case, the extra effort was worth it.

Related

Storing MySQL Stored Procedure Result Into Temporary Table

In SQL Server, I used to create a table variable to store results from a certain stored procedures. This is how I usually do with table variable.
DECLARE #My_Table_Variable TABLE(col_1 FLOAT, col_2 FLOAT)
INSERT INTO #My_Table_Variable(col_1, col_2) EXEC [My_Procedure]'param_1','param_2'
Now, while using MySQL, I recognized that table variable doesn't exist. I've seen some questions related to this, for instance, this one says it's not possible to SELECT something FROM a procedure.
How about temporary table? Can we CALL a procedure, and then put the result into a temporary table? With the syntax something like this:
CALL my_procedure('my_first_parameter','my_second_parameter') INTO my_temporary_table;
which allows me to query from my_temporary_table. Is this possible to be performed?

Is return value from a stored procedure must be declared as out parameter?

Consider a general scenario
I have a stored procedure which returns a specific value at the end. Do I need to explicitly mention it as out parameter? Or is the select statement enough to return the value?
You can construct and call different types of stored procedures:
1) A stored procedure that returns no result. For example, such a stored procedure can log non-critical information, or change database data in a straightforward way.
Example : A stored procedure which performs insert operation.
2) A stored procedure that returns one or more values using output parameters. For example, such a procedure can indicate success or failure, or retrieve and return data items.
Example : A stored procedure which performs "Select particular_field
FROM table ..." query.
3) A stored procedure that returns one or more result sets. The procedure can execute one or more queries, each of which returns an arbitrary number of rows. Your application loops through each result set to display, transform, or otherwise process each row in it.
Example : A stored procedure which performs multiple select queries.
Hope it might help.

MySQL stored procedures out variable as array

I have MySQL procedure where I want to get a result of query:
SELECT id FROM mbus_clients WHERE second_name like surnamePart AS
So it should be an array. The decision I've found in the internet is to use temporary table.
But how can I return a table and read with PHP? Is it ok?
Simply call the procedure:
CALL procedurename();
If the procedure performs a SELECT, the result set of the procedure call will be the same as if you'd performed the query itself. You can then fetch the rows using PHP the same way as if you'd performed a SELECT.

save stored procedure output into a table

I have execute only access to a stored procedure.
This SP seems to select some data from multiple tables, and returns one row. I need to store two columns of the output of this SP into a table.
Is there any way to do this within MySQL?
If it returns a row, this is a stored function and not a stored procedure. You can use something like the following to insert into your table:
INSERT INTO tablename SELECT (SELECT col1, col2 FROM (SELECT somefunction()))
Otherwise, it will be a stored procedure and you should do something like this, assuming that #var1 and #var2 are output parameters:
CALL someprocedure(#var1, #var2, #var3)
INSERT INTO tablename SELECT(#var1, #var2)
See the documentation about Create Procedure and Create Function for more information about functions versus procedures.
MySQL has an extension to stored procedures that allows the procedure to return one or more result sets to the client, as if the client had issued a SELECT query... but those results are ephemeral. They don't persist and they can't be stored in variables or otherwise accessed after the procedure finishes -- they can only be "fetched" the one time.
There is a way to make them accessible without breaking the way the procedure already works, as I discussed here, but you can't do it without a change to the procedure:
How to use Table output from stored MYSQL Procedure
The idea is for the procedure to write its output in a temporary table, and then return it to the caller by calling SELECT against the temporary table -- but to leave the temporary table behind so that the caller can access it directly if desired.
That's not exactly the same as what you're asking though, which is why I didn't mark this question as a duplicate, since you, unlike the other poster, do not appear to have administrative control of the procedure... but unless you can make the case for a change like this, there's not another way within MySQL to access those returned values, since they only exist in the result-set that's returned.
Of course, procedures do have optional OUT parameters, where you can hand variables to the procedure as part of arguments you use to call it, and it can set those variables, so that they'll have the values you need when the procedure is done, but that only works when the return values are scalars and would require a change to the procedure's interface, since procs in MySQL do not have "optional" arguments... if the procedure were changed to permit this, it would require an increased number of arguments to be provided every time it was called, and if other components are calling it, that could easily break other things.

How can I be DRY in columns names in this MySQL procedure?

I'm referencig name, description and user_id columns of meta table. Twice, and maybe more (who knows?) in future. Those columns are used to compute the ETag of my meta resource.
Adding one column that contributes to compute ETag in the future will force me to change the code N times, and this is bad.
Is there any way to make it DRY and store these column names elsewhere? Because I'd like to use these column names also when INSERT on meta is performed.
IF only = true THEN
-- Calculate ETag on meta fields only
UPDATE meta
SET etag = etag(CONCAT(name, description, user_id))
WHERE id = meta_id;
ELSE
-- Calculate Etag on meta fields and meta customers
BEGIN
DECLARE c_etags VARCHAR(32);
-- Compute c_etags
UPDATE meta
SET etag = etag(CONCAT(etag(name, description, user_id), c_etags))
WHERE id = meta_id;
END;
END IF;
Disclaimer: this code is untested, I'm pretty new to MySQL stuff, apart for simple statements.
EDIT: etag is MD5 MySQL function. Maybe this is one option:
CREATE PROCEDURE set_meta_etag(IN meta_id INT, IN related TEXT)
NOT DETERMINISTIC
BEGIN
UPDATE meta
SET etag = etag(CONCAT(name, description, user_id,
IF(related IS NOT NULL, related, '')))
WHERE id = meta_id;
END //
-- First call
CALL set_meta_etag(meta_id, NULL);
-- Second call
CALL set_meta_etag(meta_id, c_etags);
But it won't work for INSERT statement.
The obvious thing (foreach column, if it's the one I want, use it to help make the etag) doesn't work in SQL with any ease, because SQL doesn't, historically, contemplate column names stored in variables.
You could write a program in your favorite non-SQL programming language (Java, PHP, etc) to create and then define your procedure.
You could also use so-called "dynamic sql" to do this, if you were willing to do the work and take the slight performance hit. See
How To have Dynamic SQL in MySQL Stored Procedure
for information on how to PREPARE and EXECUTE statements in a stored procedure.
By the way, I have had good success building systems that have various kind of metadata stored in the column contents. For example, you could write code looking for the string '[etag]' in your column contents. The comments for columns are stored in
information_schema.COLUMNS.COLUMN_COMMENT
and are very easy to process when your program is starting up.
If you know this is confined to one table, you could add a trigger. Using an AFTER trigger should allow your stored proc to work for both INSERT and UPDATE. See MySQL Fire Trigger for both Insert and Update.