MySQL: Combining all results from a LOOP (possibly with UNION?) - mysql

EDIT: already solved and answered: but feel free to read along and see how the process went if you have similar questions
BACKGROUND
I created a procedure called 'sea_dew_potion' and placed it within a loop called 'loopseadew'. the loop works as intended.
QUESTION
My loop produced 18 results, but I would like it to be in a single result set so I can export it to tableau to make a line graph with multiple data points to make some form of curve. After some research I think a UNION would work but I'm not sure how to go about it, mostly cause I just figured out how loops work and then how to fit another procedure within a loop haha. Thanks for the help!
DELIMITER $$
CREATE PROCEDURE loopseadew(IN p_base_name VARCHAR(255), IN p_base_amount integer, IN p_ing_name VARCHAR(255), p_ing_amount integer, p_pot_units INTEGER(3))
BEGIN
DECLARE p_ing_amount int;
SET p_ing_amount = 0;
loop1: LOOP
SET p_ing_amount = p_ing_amount + 1;
CALL sea_dew_potion('sea dew', 100, 'skadite', 0, 10);
IF p_ing_amount = 18 THEN
LEAVE loop1;
END IF;
END LOOP loop1;
SELECT p_base_name, p_base_amount, p_ing_name, p_ing_amount,
m.price * p_ing_amount AS cost
FROM
multiplier m
WHERE
m.ing_name = p_ing_name;
END$$
DELIMITER ;
EDIT: im getting some progress, but i am getting null values for columns 'heal' and 'cost'. values for heal is produced the procedure "sea_dew_potion". Right now i get 18 individual results with all the correct values, and one result with all the results combined but null values for heal and cost.
Screenshots at the bottom to show the problem between the result sets.
DELIMITER $$
CREATE PROCEDURE allloopseadew(IN p_base_name VARCHAR(255), IN p_base_amount integer, IN p_ing_name VARCHAR(255), p_ing_amount integer, p_pot_units INTEGER(3))
BEGIN
DECLARE x int;
CREATE TEMPORARY TABLE allresults (p_base_name varchar(255), p_base_amount int(3), p_ing_name VARCHAR(255), p_ing_amount int(3), heal decimal(10,2), cost decimal(10,2));
SET x = 0;
loop1: LOOP
SET x = x + 1;
CALL sea_dew_potion('sea dew', 100, 'skadite', x, 10);
IF x = 18 THEN
LEAVE loop1;
END IF;
INSERT INTO allresults
VALUES(p_base_name, p_base_amount, p_ing_name , x, heal, cost);
END LOOP loop1;
SELECT p_base_name, p_base_amount, p_ing_name, p_ing_amount,
m.price * p_ing_amount AS cost
FROM
multiplier m
WHERE
m.ing_name = p_ing_name;
SELECT * FROM allresults;
END$$
DELIMITER ;
EDIT PART 2
Shout out to #blabla_bingo i finally got it! Here is the final query for anyone interested haha
CREATE PROCEDURE allloopseadew(IN p_base_name VARCHAR(255), IN p_base_amount integer, IN p_ing_name VARCHAR(255), p_ing_amount integer, p_pot_units INTEGER(3))
BEGIN
DECLARE x decimal(10,3);
DECLARE v_heal decimal(10,3);
DECLARE v_cost decimal(10,3);
DECLaRE v_total_amount int(3);
CREATE TEMPORARY TABLE allresults (p_base_name varchar(255), p_base_amount int(3), p_ing_name VARCHAR(255), p_ing_amount int(3), heal decimal(10,2), cost decimal(10,2));
SET x = 0;
loop1: LOOP
SET x = x + 1;
SET v_total_amount =
(SELECT p_base_amount + x);
SET v_heal =
(SELECT
round(max((2*(2.1*(p_base_amount/v_total_amount))*1*(1+sqrt(x/v_total_amount)*m.multiplier_value)*p_pot_units)),3)
FROM
multiplier m
WHERE
m.ing_name = p_ing_name);
SET v_cost =
(SELECT
m.price * x
FROM
multiplier m
WHERE
m.ing_name = p_ing_name);
CALL sea_dew_potion('sea dew', 100, 'skadite', x, 10);
IF x = 18 THEN
LEAVE loop1;
END IF;
INSERT INTO allresults
VALUES(p_base_name, p_base_amount, p_ing_name, x, v_heal, v_cost);
END LOOP loop1;
SELECT p_base_name, p_base_amount, p_ing_name, p_ing_amount,
m.price * p_ing_amount AS cost
FROM
multiplier m
WHERE
m.ing_name = p_ing_name;
SELECT * FROM allresults;
END$$
DELIMITER ;
all results with no heal or cost
individual result with heal and cost
Final correct result with query

UNION has its drawbacks such as being unable to sort out using ORDER BY clause. Besides, a total of 17 UNION need to be used in this case if you decides to go along, which makes the query drag on too much. I would suggest use a result table and have your procedure sea_dew_potion insert into it. Then you can query the result table and probably make some adjustment before exporting into your tableau table. Finally,as a side note, instead of calling the sea_dew_potion 18 times in a loop, we can also add a count parameter for the procedure sea_dew_potion ,so that we only need to define the count number and run the procedure once. e.g sea_dew_potion(p1,p2,p3,p4,p5,count_num int).

Related

error in stored procedure ...into keyword

Two tables Borrower(rollno,name,bookissue_date) and Fine(rollno,name,amount)
delimiter //
create procedure student( in roll_no int,in Nameofbook varchar(40))
begin
declare Dateofiss1 date;
Declare cur cursor for
select Dateofiss from Borrower where Roll_no = roll into Dateofiss1;
OPEN cur;
fetch cur into Dateofiss1
if(datediff(sysdate(),Dateofiss1)<15) then varchar(20))
update Borrower set status='R'where Roll_no=roll_no
elseif(datediff(sysdate(),Dateofiss1)>=15)and datediff (sysdate(),Dateofiss1<30)
SET FINEAMOUNT=5*(datediff(sysdate(),Dateofiss1)-15)
insert into Fine(Roll_no,Date,amount)values(rollno,sysdate,fineamount);
update.borrower set status='R' where Roll_no='rollno';
elseif (datediff(sysdate(),Dateofiss1)>30)
SET FINEAMOUNT=50*(datediff(sysdate(),Dateofiss1)-15)
insert into Fine(Roll_no,Date,amount)values(rollno,sysdate,fineamount);
update.borrower set status='R' where Roll_no='rollno';
close cur;
end if
select * from Borrower;
elect * from Fine;
end
You have a number of syntax errors.
You have an extraneous varchar(20)) in the first if statement.
You're missing THEN in the ELSEIF statements.
You wrote update.borrower instead of update borrower.
You have roll_no in quotes in some of your update statements.
The roll_no parameter is the same as a table column, since column names are case-insensitive. The condition where Roll_no = roll_no will match every row because of this. Give the parameter a different name.
In a SELECT, the INTO clause goes after FROM, not at the end.
There's no need to use a cursor if you're using SELECT INTO. Just execute the query and it will set the variable.
You can also simplify the code by putting the date difference in a variable, so you don't have to repeatedly calculate it. And in the ELSEIF you don't need to test >= 15, since you'll only get there if the < 15 test failed.
The UPDATE statement is the same in all conditions, so it doesn't need to be in the IF at all.
delimiter //
create procedure student( in p_roll_no int,in Nameofbook varchar(40))
begin
declare Dateofiss1 date;
declare diff INT;
select Dateofiss from Borrower into Dateofiss1 where Roll_no = p_roll_no;
OPEN cur;
SET diff = datediff(sysdate(),Dateofiss1)
IF diff BETWEEN 15 AND 29 THEN
SET FINEAMOUNT= 5 * (diff - 15)
insert into Fine(Roll_no,Date,amount)values(rollno,sysdate,fineamount);
else
SET FINEAMOUNT= 50 * (diff - 15)
insert into Fine(Roll_no,Date,amount)values(rollno,sysdate,fineamount);
end if
update Borrower set status='R'where Roll_no=p_roll_no
select * from Borrower;
select * from Fine;
end

Pass the columns of each row in a result set to a stored function in single step

The following is my stored function
DELIMITER $$;
CREATE FUNCTION set_credit_values (emp_id bigint, lpc_id int, elc_date date) RETURNS date DETERMINISTIC
BEGIN
SELECT do_credit(emp_id, lpc_id, elc_date,ltp_id, lpb_cr_count, lpb_cr_on, lpb_next_cr, lpb_cr_period)
FROM erp_leave_policy_body,erp_leave_type
WHERE lpb_fk_leave_policy = lpc_id AND ltp_id = lpb_fk_leave_type AND ltp_status=1;
RETURN elc_date;
END $$;
DELIMITER ;
I need to pass the selected values in each row in the result set (by the SELECT query) to do_credit() function. But it is showing errors as
#1415 - Not allowed to return a result set from a function
The do_credit() function is as
DELIMITER $$;
CREATE FUNCTION do_credit (emp_id bigint, lpc_id int, new_elc_date date,ltp_id int, cr_count tinyint, cr_on tinyint, next_cr date, cr_period tinyint)
RETURNS boolean DETERMINISTIC
BEGIN
DECLARE next_credit_date DATE DEFAULT NULL;
DECLARE is_credited INT(11) DEFAULT 0;
SET next_credit_date = get_next_credit(emp_id, lpc_id, new_elc_date,ltp_id, cr_on, next_cr, cr_period);
IF next_credit_date = new_elc_date THEN
SELECT COUNT(*) INTO is_credited FROM erp_employee_leave_credits WHERE
elc_fk_employees = emp_id AND elc_fk_leave_policy = lpc_id AND elc_fk_leave_type = ltp_id AND elc_date = new_elc_date;
-- If the leave is not credited yet
IF is_credited = 0 THEN
INSERT INTO erp_employee_leave_credits(elc_fk_employees,elc_fk_leave_policy,elc_fk_leave_type,elc_date,elc_cr_count) VALUES (emp_id,lpc_id,ltp_id,new_elc_date,cr_count);
END IF;
END IF;
RETURN 1;
END $$;
DELIMITER ;
It is working successfully.
I know using cursor. But when using cursor I should traverse through each row in the result set. So may take more time. If I call the function with the select query it works faster than cursor. So How can solve this error.

Mysql Stored Proc not returning a VARCHAR out parameter

Below is my stored procedure. It works fine but my problem is I can't get the output parameter as VARCHAR.
The part where I'm having problem is the assignment of #curcName to the out parameter op_resultMessage
BEGIN
SET op_resultMessage = #curcName;
END;
Here's the Stored Procedure.
CREATE DEFINER=`root`#`localhost` PROCEDURE `addCurriculum`(
IN p_curcName varchar(100),
IN p_description TEXT,
IN p_yearLevel VARCHAR(50),
IN p_syStart INT,
IN p_syEnd INT,
IN p_creator VARCHAR(50),
OUT op_resultMessage VARCHAR(50))
BEGIN
DECLARE curcName VARCHAR(20) ;
IF EXISTS
(SELECT #curcName := `name`
FROM curriculum
WHERE
yearLevel = p_yearLevel
AND syStart = p_syStart
AND syEnd = p_syEnd )
THEN --
BEGIN
SET op_resultMessage = #curcName;
END;
ELSE
BEGIN
INSERT INTO curriculum(`name`, description, yearLevel, syStart, syEnd, creator)
VALUES(p_curcName,p_description,p_yearLevel,p_syStart,p_syEnd,p_creator);
END;
END IF;
END
I'm trying to return a message IF name EXISTS
So it should go something like
SET op_resultMessage = #curcName 'already uses the school year and year level you're trying to insert';
But I don't know how to properly concatenate and assign values. I'm still confused with := SET and = operators. I guess that's where I'm having problems with.
If I change the out parameter's type to an INT like
OUT op_resultMessage VARCHAR(50)
then assigns a number to op_resultMessage like SET op_resultMessage = 1;
It returns the number 1 as out parameter values. It just won't work with varchar.
So when I try to call the procedure
CALL `enrollmentdb`.`addCurriculum`
('Test Curriculum ','Test ','Grade 1',2015,2016,'jordan',#outputMsg);
SELECT #outputMsg; -- this doesn't return any value even if Grade 1, 2015 and 2016 exists
I'd appreciate any help. I actually just learned mysql recently.
Thanks.
drop procedure if exists addCurriculum;
delimiter $$
CREATE PROCEDURE `addCurriculum`(
IN p_curcName varchar(100),
IN p_description TEXT,
IN p_yearLevel VARCHAR(50),
IN p_syStart INT,
IN p_syEnd INT,
IN p_creator VARCHAR(50),
OUT op_resultMessage VARCHAR(50))
BEGIN
DECLARE curcName VARCHAR(20) ;
SELECT `name` into #curcName
FROM curriculum
WHERE
yearLevel = p_yearLevel
AND syStart = p_syStart
AND syEnd = p_syEnd
LIMIT 1;
-- Note change above. When selecting into a variable (or more than 1)
-- then 0 or 1 rows can come back max or an Error occurs
IF #curcName is not null then
SET op_resultMessage = #curcName;
ELSE
BEGIN
INSERT INTO curriculum(`name`, description, yearLevel, syStart, syEnd, creator)
VALUES(p_curcName,p_description,p_yearLevel,p_syStart,p_syEnd,p_creator);
END;
SET op_resultMessage = 'GEEZ I am right here'; -- Drew added this
END IF;
END$$
delimiter ;
Note the commentary in the stored procedure, especially the part of only 0 or 1 rows returning else an Error will occur with a select into var pattern. So LIMIT 1. That may or may not be the row you want (limit 1), but that is where it is at right now.

Stored procedure using if statement

I have here stored procedure:
delimiter //
create procedure insert(p int, n varchar(10), d date, q int)
begin
select pname if(pname!=n)
then
insert into prod(p,n,d,q)values(p,n,d,q)
else quant=quant + q;
from prod;
end;
//
This stored procedure will add records to the table prod if pname and n are not equal ELSE if they are equal, the quant will be updated and added by the value of q. This idea is not working can you help me? How to achieve this one?
....
BEGIN
DECLARE p CHAR DEFAULT NULL;
SELECT pname FROM prod WHERE pname = n LIMIT 1 INTO p FOR UPDATE;
IF(p) THEN
UPDATE prod SET quant = quant + q WHERE pname = n;
ELSE
INSERT INTO prod(p,n,d,q) values(p,n,d,q);
END IF;
END;
There are simpler ways to do it if you have a UNIQUE key on pname in prod.

Stored Procedure (mysql) fails with "can't return a result set in the given context"

I'm trying to get this SP to return (leave) if some conditions fails and so forth.
This code validates and it saves the procedure, but when I call the procedure with:
CALL ACH_Deposit(30027616,3300012003,200.00,"USD", "127.0.0.1")
It fails with error: "Procedure can't return a result set in the given context"
Does anyone have any idea on what the error is?
Procedure code:
CREATE DEFINER=`redpass_web_urs`#`%` PROCEDURE `ACH_Deposit`(
IN __Account_ID BIGINT,
IN __To_Bank_Account BIGINT,
IN __Amount DECIMAL(10,2),
IN __Currency CHAR(3),
IN __IP_Address VARCHAR(50)
)
COMMENT 'Makes a ACH deposit'
BEGIN
-- Declare Account Parameters
DECLARE _Account_Status INT;
DECLARE __Transaction_ID INT;
DECLARE _Account_Type INT DEFAULT 0;
DECLARE _Fee INT;
SELECT
Account_Status AS _Account_Status,
Account_Type AS _Account_Type
FROM Account_Users
WHERE Account_ID = __Account_ID;
main: BEGIN
-- Step 1, is account active ?
IF _Account_Status <> 1 THEN
-- Account must be active (not restricted, closed etc)
SELECT Response_Code, Description FROM ResponseCodes WHERE Response_Code = 106;
LEAVE main; -- Here we die..
END IF;
-- Step 2, Calculate the FEE (Loading Funds with ACH)
IF _Account_Type = 1 THEN
-- Personal Account
SET _Fee = (SELECT Fee_Template_Personal_1 FROM Fees WHERE Fee_Type_ID = __Fee_Type_ID);
ELSE
-- Business Account
SET _Fee = (SELECT Fee_Template_Business_1 FROM Fees WHERE Fee_Type_ID = __Fee_Type_ID);
END IF;
-- Step 3, Check that Fee is not bigger then the actual amount
IF _Fee > __Amount THEN
SELECT Response_Code, Description FROM ResponseCodes WHERE Response_Code = 108;
LEAVE main; -- Here we die..
END IF;
-- If we come here, we can make the transactions
INSERT INTO Bank_Transaction
(Bank_Account_ID
,Transaction_Type
,Amount
,IP_Address
,Pending)
VALUES
(__To_Bank_Account
,11
,__Amount
,__IP_Address
,1); -- Reserverade pengar
-- Transaction ID
SET __Transaction_ID = (SELECT LAST_INSERT_ID());
-- Deduct the Fee
INSERT INTO Bank_Transaction
(Bank_Account_ID
,Transaction_Type
,Amount
,Fee_Type_ID
,Fee_Transaction_ID)
VALUES
(__To_Bank_Account
,4
,-__Fee
,21
,__Transaction_ID);
END main;
SELECT Response_Code, Description, __Transaction_ID AS Transaction_ID
FROM ResponseCodes
WHERE Response_Code = 1;
END
To retrieve multiple resultsets from the stored procs, you should use a client which supports multiple queries.
If you use PHP, use MySQLi extension and call the procedure using mysqli_multi_query.
MySQL extension is only able to retrieve the first recordset returned by the proc. To be able to use ti, you should set CLIENT_MULTI_RESULTS (decimal 131072) in the parameter $client_flags to mysql_connect