right way to write stored procedure in mysql - mysql

I have written one stored procedure in mysql database as follows :
CREATE PROCEDURE sp_Test(
IN Mode VARCHAR(50),
IN UserID INT,
....
....
)
BEGIN
SET #sqlQuery = "";
IF (Mode = 'Select') THEN
//Select query
ELSEIF (Mode = 'Update') THEN
//update query
ELSEIF (Mode = 'Delete') THEN
//Delete query
END
END
and as per my need I can call it like this
CALL sp_Test("Select", 1, ...)
OR
CALL sp_Test("Update", 1, ...)
OR
CALL sp_Test("Delete", 1, ...)
It is ok for performance? Or I should write individual stored procedures for each mode like
for SELECT
CREATE PROCEDURE sp_Test_Select(
IN UserID INT,
....
....
)
BEGIN
//Select query
END
for UPDATE
CREATE PROCEDURE sp_Test_Update(
IN UserID INT,
....
....
)
BEGIN
//Select query
END

In my opinion, there is no significant difference in the performance of these procedures (unless you're going to use them hundreds of times per second 24x7).
But there is a difference in readability. It's a significant difference. test_DELETE(item) is easier to understand than test("delete", item). Keep in mind that you're writing code not just for the machine, but for the next person who will work on your project. Use your second alternative.

Apart from performance restriction (if any at all); It's always recommended to have separate procedure defined for separate operations (select/insert/update/delete) because
It increases Redability.
Maintainability. separate procedure can be maintained well if at all any changes needed to be done in future.
Consider, about passing parameter for different operations. For select most probably you don't need to pass any parameter to the procedure but for insert, update and delete you will have to pass parameter. Will not be a mess for you to differentiate them within single procedure body?

Related

Would it be better to organize this in multiple functions or make one that takes different arguments ? (PL/SQL)

in your opinion, what would be the best or most efficient way to do this.
I'm working with treegrid where you can edit a row, add a new one, or remove existing one.
Save button triggers creating a json file
EDIT
ADD (INSERT)
REMOVE
Now I have to write a procedure that is also triggered with save button that would actually store these changes into the database.
I have something like this for updating.
create or replace function updateDrivers (p_data varchar2)
return varchar2
IS
p_result varchar2(50);
p_team_name varchar2(50);
p_driver_name varchar2(50);
p_driver_lastName varchar2(50);
p_driverID varchar2(50);
Begin
SELECT JSON_value(p_data,'$.Changes.ime_tima') INTO p_team_name from dual;
SELECT JSON_value(p_data,'$.Changes.ime_vozaca') INTO p_driver_name from dual;
SELECT JSON_value(p_data,'$.Changes.prezime_vozaca') INTO p_driver_lastName from dual;
SELECT JSON_value(p_data,'$.Changes.id') INTO p_driverID from dual;
UPDATE drivers
SET ime_tima = p_team_name,
ime_vozaca = p_driver_name,
prezime_vozaca = p_driver_lastName
where id = p_driverID;
p_result := '1|success!';
return p_result;
EXCEPTION
WHEN OTHERS THEN
p_result := '-1|Error!';
RETURN p_result;
END updateDrivers;
The problem is, I have multiple tables, this one is example for drivers, but I have one for teams, races etc. Would it be better to make one procedure for every action (update/insert/delete) and for each table or should I make it one procedure which takes different arguments depending on the table and action.

Mysql function to return row count from a procedure call [duplicate]

This question already has answers here:
How can I get the number of rows 'returned' from a result set of a stored procedure
(2 answers)
Closed 7 years ago.
I am trying to write a function to return the number of rows a call to a stored procedure would return. I'm trying to minimise repetition of the code (for reduced code maintenance/debugging- the procedure select is long).
The stored procedure just read-only selects rows matching certain criteria (vague I know but details should not be material to the question).
I could just copy the procedure into a function and change the select to count() but as it is long with multiple joins I was hoping to write a function that could call the procedure and return the row count. The goal is not for optimised running but for efficient code maintenance, boiler plate reduction.
I have tried this as a test:
DELIMITER //
CREATE PROCEDURE IF NOT EXISTS proc_select1()
BEGIN
SELECT 1;
END //
CREATE FUNCTION IF NOT EXISTS select1_count() RETURNS INT UNSIGNED
BEGIN
CALL proc_select1();
RETURN FOUND_ROWS();
END //
DELIMITER ;
However when I SELECT select1_count(); - which I am hoping will return 1 - I get the "cannot return a result set from a function" error.
I tried assigning FOUND_ROWS to a variable, clearing the result set then returning the variable value but can't get it to work.
Does anyone know a work around or do I really need to copy-paste the procedure and convert to a SELECT COUNT and function?
I'm using MySQL 5.5.16 (can upgrade if necessary), Windows 7 (nobody seems to want to upgrade :) with HeidiSQLv7.0.0.4053 (if relevant)
As always, any help much appreciated.
first use distinct to get distinct values then use count on that..like
select count(distinct column_name) from table_name
cannot return a result set from a function
This error happens when a SELECT query is done in a procedure without storing the output values.
Other thing : don't forget to use SQL_CALC_FOUND_ROWS to indicates your DBMS to store the number of found rows.
I tried to make it work without any temporary variable (Maybe there is exists a mysql keyword to not "return" the set of the SELECT query), but no success. Here a piece of code which works using temporary var.
CREATE PROCEDURE proc_select1()
BEGIN
DECLARE temp INT;
SELECT SQL_CALC_FOUND_ROWS 1 INTO temp;
END //
CREATE FUNCTION select1_count() RETURNS INT UNSIGNED
BEGIN
CALL proc_select1();
RETURN FOUND_ROWS();
END //
The result is 1 as expected :-) cf : SQLFiddle
It appears this is not possible. The select statement is required to SELECT INTO something to avoid the cannot return a result set from a function error during the function call. If this is not possible or required in the SELECT statement used in the procedure then the function will not run without error.
Using CLEAR QUERY CACHE or FLUSH QUERY CACHE after the procedure call did not help (and is probably a bad idea / bad coding anyway).

Call a stored procedure for each row returned by a query in MySQL

I want a MySQL stored procedure which effectively does:
foreach id in (SELECT id FROM objects WHERE ... ) CALL testProc(id)
I think I simply want the MySQL answer to this question but I don't understand cursors well: How do I execute a stored procedure once for each row returned by query?
Concepts such as “loops” (for-each, while, etc) and “branching” (if-else, call, etc) are procedural and do not exist in declarative languages like SQL. Usually one can express one’s desired result in a declarative way, which would be the correct way to solve this problem.
For example, if the testProc procedure that is to be called uses the given id as a lookup key into another table, then you could (and should) instead simply JOIN your tables together—for example:
SELECT ...
FROM objects JOIN other USING (id)
WHERE ...
Only in the extremely rare situations where your problem cannot be expressed declaratively should you then resort to solving it procedurally instead. Stored procedures are the only way to execute procedural code in MySQL. So you either need to modify your existing sproc so that it performs its current logic within a loop, or else create a new sproc that calls your existing one from within a loop:
CREATE PROCEDURE foo() BEGIN
DECLARE done BOOLEAN DEFAULT FALSE;
DECLARE _id BIGINT UNSIGNED;
DECLARE cur CURSOR FOR SELECT id FROM objects WHERE ...;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done := TRUE;
OPEN cur;
testLoop: LOOP
FETCH cur INTO _id;
IF done THEN
LEAVE testLoop;
END IF;
CALL testProc(_id);
END LOOP testLoop;
CLOSE cur;
END

set range for column

I'm using in my database, many fields of a certain range, like:
CREATE TABLE figures (
deg FLOAT,-- between 0 and pi
prc FLOAT,-- between 0 and 1
.......
);
CREATE TRIGGER filter1 BEFORE UPDATE ON figures FOR EACH ROW SET
NEW.deg=IF(NEW.deg>3.1415926 OR NEW.deg<0, OLD.deg,NEW.deg),
NEW.prc=IF(NEW.prc>1 OR NEW.prc<0, OLD.prc,NEW.prc),
..........;
CREATE TRIGGER filter2 BEFORE INSERT ON figures FOR EACH ROW SET
NEW.deg=IF(NEW.deg>3.1415926 OR NEW.deg<0, NULL,NEW.deg),
NEW.prc=IF(NEW.prc>1 OR NEW.prc<0, NULL,NEW.prc),
.........;
Is there any way to write it more clearly ?
Something like:
--CREATE PROCEDURE/FUNCTION between()..................
CREATE TABLE figures (
deg FLOAT between(0,3.1415),
prc FLOAT between(0,1),
.......
At least, I don't want to write every filter twice. (ON INSERT,ON UPDATE)
prior to MySQL 8.0.16
Triggers are the best solution
Re:check constraints...
'The CHECK clause is parsed but ignored by all storage engines.'.....
'The reason for accepting but ignoring syntax clauses is for compatibility, to
make it easier to port code from other SQL servers, and to run applications
that create tables with references. '
lifted directly from: http://dev.mysql.com/doc/refman/5.1/en/alter-table.html
From MySQL 8.0.16 though they now work as you would expect
CREATE TABLE figures (
deg FLOAT,
prc FLOAT,
CONSTRAINT `deg_min` CHECK ((`deg` > 0)),
CONSTRAINT `deg_max` CHECK ((`deg` < 3.1415)),
CONSTRAINT `prc_min` CHECK ((`prc` > 0)),
CONSTRAINT `prc_max` CHECK ((`prc` < 1))
)
At least, I don't want to write every filter twice. (ON INSERT,ON UPDATE)
You can write a stored function and call that in your trigger.
DELIMITER $$
CREATE FUNCTION check_deg (degree FLOAT, olddegree FLOAT) RETURNS FLOAT
BEGIN
DECLARE result FLOAT;
result = IF(degree>3.1415926 OR degree <0, olddegree,degree);
RETURN result;
END$$
DELIMITER ;
That way you have one point where the limits are defined and if anything changes you only have to change the boundaries in one place.
The best solution is to use a CHECK() constraint, but MySQL doesn't support CHECK() constraints. (MySQL parses them, then ignores them.)
In some cases, you can replace a CHECK() constraint with a foreign key reference to a table that contains all the valid values. Floating-point numbers are not a good candidate for that kind of solution, though.
That leaves triggers. In your case, your best bet is to use a trigger that calls a stored function.
I have a unfinished idea :
CREATE PROCEDURE check_constraint (table VARCHAR,field VARCHAR,condition VARCHAR)
BEGIN
SET #update_trigger = CONCAT ("
IF EXIST TRIGGER check_constraint_",table,"_",field,"
BEGIN /*I don't know yet what to do*/ END
ELSE /*IF TRIGGER DONT EXIST*/
BEGIN
CREATE TRIGGER check_constraint_",table,"_",field,"
BEFORE UPDATE ON ",table," FOR EACH ROW SET
NEW.",field,"=IF("condition, ", OLD.",field,",NEW.",field,");
END
");
PREPARE update_trigger FROM #update_trigger;
EXECUTE update_trigger;
DEALLOCATE PREPARE update_trigger;
SET #insert_trigger = ..............................
END
After we have a completed function, we can just call it during creation of the database:
CALL check_constraint("table","field","NEW.field<34567");

Filtering A MySQL Result Set Based On The Return Value Of A Function

I'm a SQL noob, and I need a little bit of help understanding the big picture of if it is possible, and if so how to go about filtering a result set based on the return value of a function which is passed one of the fields of the record.
Let's say I have a table called "Numbers" with just one field: "Value".
How could I correctly specify the following "pseudo-sql"?:
SELECT Value FROM numbers WHERE IsPrime(Value)=true
Can I accomplish such a thing, and if so, where/how do I put/store "IsPrime"?
I'm using MySQL.
I agree with extraneon that it's usually better to store this value in the database rather than compute it in the where clause. This way you can compute it once per row and index the data for faster performance.
As long as you are on MySQL 5.x, I would recommend a stored function, as opposed to a UDF. You can add an IS_PRIME column of type TINYINT to your DB, add an index on that column, then use the stored function to calculate the value at insert time. You can even calculate the IS_PRIME value using a before insert trigger if you don't want to change the insert code.
The stored function would look something like this:
DELIMITER $$
DROP FUNCTION IF EXISTS IS_PRIME $$
CREATE FUNCTION IS_PRIME(P_INT BIGINT) RETURNS TINYINT
BEGIN
DECLARE V_FACTOR BIGINT;
DECLARE V_MAX_FACTOR BIGINT;
SET V_FACTOR := 2;
SET V_MAX_FACTOR := round(sqrt(P_INT),0);
WHILE (V_FACTOR <= V_MAX_FACTOR)
DO
IF (P_INT % V_FACTOR) = 0
THEN
RETURN FALSE;
END IF;
SET V_FACTOR := V_FACTOR + 1;
END WHILE;
RETURN TRUE;
END $$
DELIMITER ;
I think you may find some help with the doc about the user-defined functions: http://dev.mysql.com/doc/refman/5.1/en/adding-functions.html
I don't know anything about user defined functions, but I could imagine that for computation-intensive functions it might be best to have that value precomputed and stored in the database somewhere.
Depending on how the data gets in the database you could require the client to compute isPrime (that can be done if the client is a web service or app on your own server), or perhaps a scheduled job which processes every record with isPrime is null or something like that. That is not instantaneous, but for some scenario's it may be good enough.
If isPrime is only used sometimes you could also post-process/filter the data on the client when retrieving data.