MYSQL Function gets progressively slower - mysql

I created the following function to input records by parsing a string:
DROP FUNCTION IF EXISTS RowPerRow ;
DELIMITER $$
CREATE FUNCTION RowPerRow()
RETURNS VARCHAR(300)
BEGIN
DECLARE n INT DEFAULT 0 ;
DECLARE m INT DEFAULT 0 ;
DECLARE i INT DEFAULT 0 ;
DECLARE j INT DEFAULT 0 ;
SELECT count(*) FROM Temp0 INTO n ;
WHILE i < n DO
SELECT words INTO m FROM Temp0 LIMIT i, 1 ;
SET j = 1 ;
WHILE j <= m DO
INSERT INTO Temp1 SELECT words,id,api,basin,play,drilltype,tradeflag,score,cnf,fracdate,
fracdateend,state,county,operator,wellname,prodtype,latitude,
longitude,datum,depth,water,nonwater,surfactant,tradename,TRIM(Split_Str(tradename, ',', j)) AS tradename_c,
supplier,purpose,ingredients,cas,additive,fluid
FROM Temp0 LIMIT i,1 ;
SET j = j + 1 ;
END WHILE ;
SET i = i + 1 ;
END WHILE ;
RETURN '' ;
END ;
$$
The function gets progressively slower as the number of records increase.

If the question is "What is an example of an alternative way to do this same RBAR (row-by-agonizing-row) processing but which doesn't get progressively slower", I can give an example of what that would look like.
If there's an upper bound (maximum value) for words, this same result can be accomplished in a single statement:
INSERT INTO Temp1
( words, id, api, basin, play, drilltype, tradeflag ,score ,cnf, fracdate, fracdateend, state, county, operator, wellname, prodtype, latitude, longitude, datum, depth, water, nonwater, surfactant, tradename, supplier, purpose, ingredients, cas, additive, fluid
, tradename_c )
SELECT t.words,t.id,t.api,t.basin,t.play,t.drilltype,t.tradeflag,t.score,t.cnf,t.fracdate,t.fracdateend,t.state,t.county,t.operator,t.wellname,t.prodtype,t.latitude,t.longitude,t.datum,t.depth,t.water,t.nonwater,t.surfactant,t.tradename,t.supplier,t.purpose,t.ingredients,t.cas,t.additive,t.fluid
, TRIM(Split_Str(t.tradename, ',', n.i)) AS tradename_c
FROM Temp0 t
JOIN (SELECT 1 AS i UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9 UNION ALL SELECT 10 UNION ALL SELECT 11 UNION ALL SELECT 12 UNION ALL SELECT 13 UNION ALL SELECT 14 UNION ALL SELECT 15 UNION ALL SELECT 16 UNION ALL SELECT 17 UNION ALL SELECT 18 UNION ALL SELECT 19 UNION ALL SELECT 20) n
ON n.i <= t.words
But here is an example of a procedure that does the equivalent of the original procedure, except that it uses cursor loop instead of repeatedly re-querying the Temp0 table. Note that the datatypes of the variables (which accept the values from the row returned by fetch) should match or be compatible with the datatype of the corresponding expression in the SELECT list. Here I've declared them all as VARCHAR(100) as a placeholder, these will need to be changed to match the columns from the Temp0 table.
DELIMITER $$
CREATE FUNCTION RowPerRow_alt()
RETURNS VARCHAR(300)
BEGIN
-- variables for cursor fetch
-- the datatype of each variable should match the datatype
-- of the expression in SELECT list of the query
DECLARE v_words INT;
DECLARE v_id VARCHAR(100);
DECLARE v_api VARCHAR(100);
DECLARE v_basin VARCHAR(100);
DECLARE v_play VARCHAR(100);
DECLARE v_drilltype VARCHAR(100);
DECLARE v_tradeflag VARCHAR(100);
DECLARE v_score VARCHAR(100);
DECLARE v_cnf VARCHAR(100);
DECLARE v_fracdate VARCHAR(100);
DECLARE v_fracdateend VARCHAR(100);
DECLARE v_state VARCHAR(100);
DECLARE v_county VARCHAR(100);
DECLARE v_operator VARCHAR(100);
DECLARE v_wellname VARCHAR(100);
DECLARE v_prodtype VARCHAR(100);
DECLARE v_latitude VARCHAR(100);
DECLARE v_longitude VARCHAR(100);
DECLARE v_datum VARCHAR(100);
DECLARE v_depth VARCHAR(100);
DECLARE v_water VARCHAR(100);
DECLARE v_nonwater VARCHAR(100);
DECLARE v_surfactant VARCHAR(100);
DECLARE v_tradename VARCHAR(100);
DECLARE v_supplier VARCHAR(100);
DECLARE v_purpose VARCHAR(100);
DECLARE v_ingredients VARCHAR(100);
DECLARE v_cas VARCHAR(100);
DECLARE v_additive VARCHAR(100);
DECLARE v_fluid VARCHAR(100);
DECLARE j INT DEFAULT 0 ;
DECLARE v_tradename_c VARCHAR(100);
DECLARE done TINYINT(1) DEFAULT 0;
-- cursor for Temp0
DECLARE csr CURSOR FOR
SELECT words,id,api,basin,play,drilltype,tradeflag,score,cnf,fracdate,
fracdateend,state,county,operator,wellname,prodtype,latitude,
longitude,datum,depth,water,nonwater,surfactant,tradename,
supplier,purpose,ingredients,cas,additive,fluid
FROM Temp0;
-- setup NOT FOUND handler
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
OPEN csr;
rowloop: LOOP
FETCH csr INTO v_words,v_id,v_api,v_basin,v_play,v_drilltype,v_tradeflag,v_score,v_cnf,v_fracdate,v_fracdateend,v_state,v_county,v_operator,v_wellname,v_prodtype,v_latitude,v_longitude,v_datum,v_depth,v_water,v_nonwater,v_surfactant,v_tradename,v_supplier,v_purpose,v_ingredients,v_cas,v_additive,v_fluid;
IF done THEN
LEAVE rowloop;
END IF;
SET j = 1;
WHILE j <= v_words DO
-- split tradename and insert row ti Temp1
SET v_tradename_c = TRIM(Split_Str(v_tradename, ',', j));
INSERT INTO Temp1 ( words, id, api, basin, play, drilltype, tradeflag ,score ,cnf, fracdate, fracdateend, state, county, operator, wellname, prodtype, latitude, longitude, datum, depth, water, nonwater, surfactant, tradename, supplier, purpose, ingredients, cas, additive, fluid,
tradename_c )
VALUES ( v_words,v_id,v_api,v_basin,v_play,v_drilltype,v_tradeflag,v_score,v_cnf,v_fracdate,v_fracdateend,v_state,v_county,v_operator,v_wellname,v_prodtype,v_latitude,v_longitude,v_datum,v_depth,v_water,v_nonwater,v_surfactant,v_tradename,v_supplier,v_purpose,v_ingredients,v_cas,v_additive,v_fluid,
v_tradename_c );
SET j = j + 1 ;
END WHILE;
END LOOP rowloop;
CLOSE csr;
RETURN '' ;
END$$

Related

Mysql Store procedure stops iteration after some rows

Below procedure code running with out error . Line select FOUND_ROWS() returning n no. rows but cursor curs did not loop all rows
CREATE DEFINER=`root`#`%` PROCEDURE `middleLocationAutoUpdatePorc`()
BEGIN
declare vname varchar(45);
declare vmobile varchar(20);
declare vsapid varchar(6);
declare vuser varchar(45);
declare vday date;
declare vmintime time;
declare vmaxtime time;
declare vminLocation mediumtext;
declare vmaxLocation mediumtext;
declare vmiddlelocation mediumtext;
declare vdistance int(11);
declare vdrdid int(11);
declare vn_no_details int(11);
declare uatt varchar(45) default 'X';
declare adatta varchar(45) default 'X';
declare b_not_found BOOL DEFAULT FALSE;
declare curs CURSOR FOR
SELECT user ,day, mintime,maxtime_mobile,minLocation,maxLocation,distance,n_no_details,drdid FROM mydb.statusreport where day >= '2017-06-26';
DECLARE CONTINUE HANDLER FOR NOT FOUND SET b_not_found = true;
OPEN curs;
loop1: LOOP
FETCH curs INTO vuser,vday,vmintime,vmaxtime,vminLocation,vmaxLocation,vdistance,vn_no_details,vdrdid;
IF b_not_found THEN
LEAVE loop1;
END IF;
-- select FOUND_ROWS();
case substring_index(vuser,"-",-1)
when 'RSM' then
select username,Name,mobile into vsapid,vname,vmobile from mydb.supervisor where idSu=CONVERT(substring_index(vuser,"-",1),UNSIGNED INTEGER);
when 'ASM' then
select userName,name,mobile into vsapid,vname,vmobile from mydb.executive where exeid=CONVERT(substring_index(vuser,"-",1),UNSIGNED INTEGER);
when 'F' then
select sapId,name,phone into vsapid,vname,vmobile from mydb.user where idUser=CONVERT(substring_index(vuser,"-",1),UNSIGNED INTEGER);
end case;
select userGiven , adminGiven into uatt ,adatta from userdaystatus st where st.date =vday and st.idUser=CONVERT(substring_index(vuser,"-",1),UNSIGNED INTEGER) and st.userType=substring_index(vuser,"-",-1);
set vmiddlelocation=( select location from mydb.dailyReportDetails as dtb where dtb.idDailyReport=vdrdid and dtb.counter>=(abs(vn_no_details/2) ) order by dtb.idDailyReportDetails asc limit 1);
call mydb.addOMiddleLocation(day(vday), month(vday),
year(vday),vuser, vname,
vsapid, vmobile, vmintime,
vmaxtime, SUBTIME(vmaxtime,vmintime),
vminLocation,
vmiddlelocation,
vmaxLocation,Round(vdistance/1000,2),
uatt, adatta);
END LOOP;
CLOSE curs;
END
The Procedure call mydb.addOMiddleLocation() just inserting Row on another table.There does not have any data type validation .
So what can be the problem ?
It happen for log wait time for a particular Query line
set vmiddlelocation=( select location from mydb.dailyReportDetails as dtb where dtb.idDailyReport=vdrdid and dtb.counter>=(abs(vn_no_details/2) ) order by dtb.idDailyReportDetails asc limit 1);

Insert n number of rows into table when input string contains n number of fields in mysql

I do have a requirement like when two inputs are passed as input to stored proc, inserting into another table need to happen and the number of rows will depend on number of fields passed in input.
I have tried to create a stored proc but its only inserting taking field length as 1
BEGIN
Declare valu varchar(200);
Declare valu2 varchar(200);
Declare fwo varchar(200);
Declare fwo2 varchar(200);
declare len int;
Declare sl int;
Declare valu3 varchar(255);
Declare jval varchar(255);
set #fwo = (select replace(pinput1,',', ''));
set #fwo2 = (select replace(pinput2,',', ''));
-- select #fwo;
set #len = (select length(#fwo));
-- select #fwo,#len;
set #sl = 1;
if #len >0 then
increment: repeat
set #valu = SUBSTRING(#fwo, #sl , 1);
set #valu2 = SUBSTRING(#fwo2, #sl , 1);
set #jval = JSON_OBJECT('id',#valu2,'policy',#valu);
INSERT INTO `Example`(`Column1`,`Column2`) values(#valu2,#jval);
set #sl = #sl+1;
UNTIL #sl > #len END repeat increment ;
END if ;
END$$
DELIMITER ;
When the input are 1,2,3 and a,b,c the insertion is happening correctly, but when we pass 11,12,13 and ab,cd,ef the insertion is happening likethe above condition inputs.The same repetes when the lenght increases more than 1.
Can someone suggestion me to solve this issue

Mysql stored procedure not returning any values

This is the first time I am working on Mysql stored procedure I know it is a lame question please spare me for this,
is it not possible to print any value after the END LOOP statement in MySQL procedure. If it is, how we can achieve this.
what I did for this:
BEGIN
DECLARE U_movingCity varchar(50);
DECLARE U_state varchar(50);
DECLARE U_education varchar(50);
DECLARE id2 int(10);
DECLARE RankPoint int(10) DEFAULT 0;
DECLARE movingCity2 varchar(50);
DECLARE state2 varchar(50);
DECLARE education2 varchar(50);
DECLARE cur1 CURSOR FOR SELECT id, state , education FROM user WHERE id != userid AND Enabled='y' AND Active='y';
SELECT state , education into U_state , U_education FROM user WHERE id = userid ;
OPEN cur1;
read_loop: LOOP
SET RankPoint := 0;
FETCH cur1 INTO id2, state2 , education2 ;
IF ((state2 = U_state)) THEN
SET RankPoint := RankPoint + 14;
END IF;
IF ((education2 = U_education)) THEN
SET RankPoint := RankPoint + 16;
END IF;
//this displays
select RankPoint;
END LOOP;
//this doesn't.
select id, RankPoint from user;
CLOSE cur1;
END
You should use id2 instead of id in your last query.
select `id2`, `RankPoint` from `user`;

How to create a cursor in Mysql Within Stored procedure

I have created a stored procedure where I need fetch all the records from complaint table and then loop through it and find out who commented when on the particular complaint. You can assume it as blog application where a blog can have multiple comment.
I am getting syntax error in
Declare cur CURSOR statement,
What could be the problem or what I am missing into this.
This is the following message I am getting
Error Code: 1064
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'declare cur CURSOR for SELECT id,user_id FROM complaint3 WHERE user_id IN( SELEC' at line 22
Below is my procedure
DELIMITER $$
CREATE PROCEDURE `myDB`.`ShowResult`(user_id INT, hours INT,L_Complaint_id INT)
BEGIN
DECLARE complaint_count INT;
DECLARE 24Hrs_count INT;
DECLARE 48Hrs_count INT;
DECLARE Gr_48Hrs_count INT;
DECLARE notCommented INT;
DECLARE agentName VARCHAR(100);
DECLARE v_agentId INT;
DECLARE v_userId INT;
DECLARE lastUserCommentDate DATETIME;
DECLARE lastInternalUserCommentDate DATETIME;
DECLARE tempDate INT;
DECLARE v_complaint_id INT;
SET 24Hrs_count =0;
SET 48Hrs_count =0;
SET Gr_48Hrs_count =0;
SET notCommented =0;
SET v_complaint_id =0;
DECLARE cur CURSOR FOR SELECT id,user_id FROM complaint3 WHERE user_id IN( SELECT id FROM user3 WHERE user_type=0);
CREATE TEMPORARY TABLE IF NOT EXISTS resultTable (
id MEDIUMINT NOT NULL AUTO_INCREMENT,t_agentId INT,t_agentName VARCHAR(1000),t_24HrsCount INT,t_48HrsCount INT,t_Gr48HrsCount INT,
t_nullCount INT,PRIMARY KEY (id)) ENGINE = MEMORY;
SELECT COUNT(DISTINCT(id)) INTO complaint_count FROM complaint3 WHERE user_id IN(SELECT id FROM user3 WHERE user_type=0);
OPEN cur;
insert_loop: LOOP
IF complaint_count > 0 THEN
FETCH cur INTO complaint_id,user_id;
SELECT created_at INTO lastUserCommentDate FROM complaint3_diary WHERE complaint_id=v_complaint_id AND user_id = v_user_id ORDER BY id DESC LIMIT 1;
SELECT assigned_to INTO v_agentId FROM assignment3 WHERE complaint_id=v_complaint_id AND a.expire_at IS NULL;
SELECT NAME INTO agentName FROM user3 WHERE id=v_agentId;
SELECT created_at INTO lastInternalUserCommentDate FROM complaint3_diary WHERE complaint_id=v_complaint_id AND user_id = v_agentId ORDER BY id DESC LIMIT 1;
SELECT TIMESTAMPDIFF(HOUR, lastInternalUserCommentDate, lastUserCommentDate) INTO tempDate;
IF (tempDate >0 && tempDate <= 24) THEN
SET 24Hrs_count =1;
ELSEIF (tempDate >24 && tempDate <= 48) THEN
SET 48Hrs_count = 1;
ELSEIF (tempDate >48) THEN
SET Gr_48Hrs_count = 1;
ELSE
SET notCommneted = 1;
END IF;
INSERT INTO resultTable(t_agentId,t_agentName,t_24HrsCount,t_48HrsCount,t_Gr48HrsCount,t_nullCount) VALUES(v_agentId,agentName,24Hrs_count,48Hrs_count,Gr_48Hrs_count,notCommneted);
ELSE
LEAVE insert_loop;
END IF;
SET complaint_count = complaint_count - 1;
END LOOP;
CLOSE cur;
SELECT t_agentId,t_agentName,COUNT(t_24HrsCount),COUNT(t_48HrsCount),COUNT(t_Gr48HrsCount),COUNT(t_nullCount) FROM resultTable GROUP BY agentId;
END$$
DELIMITER ;
As per documentation on DECLARE
DECLARE is permitted only inside a BEGIN ... END compound statement and must be at its start, before any other statements.
In your code, the statement
DECLARE cur CURSOR FOR
SELECT id, user_id
FROM complaint3
WHERE user_id IN( SELECT id FROM user3 WHERE user_type = 0 );
has no followed the declaration order rule. And hence is the error.
Change part of your code as following:
BEGIN
DECLARE complaint_count INT;
DECLARE 24Hrs_count INT;
DECLARE 48Hrs_count INT;
DECLARE Gr_48Hrs_count INT;
DECLARE notCommented INT;
DECLARE agentName VARCHAR(100);
DECLARE v_agentId INT;
DECLARE v_userId INT;
DECLARE lastUserCommentDate DATETIME;
DECLARE lastInternalUserCommentDate DATETIME;
DECLARE tempDate INT;
DECLARE v_complaint_id INT;
DECLARE cur CURSOR FOR
SELECT id, user_id
FROM complaint3
WHERE user_id IN( SELECT id FROM user3 WHERE user_type = 0 );
SET 24Hrs_count =0;
SET 48Hrs_count =0;
SET Gr_48Hrs_count =0;
SET notCommented =0;
SET v_complaint_id =0;

Parse Comma(,) String in MySql stored procedure

Stored procedure:
CREATE PROCEDURE lead_to_loan(xReffID_list text)
I want to use this xReffID_list variable in a select statement as
SELECT * FROM XXXX where xreffID IN (xReffID_list);
but the xreffID is a int Variable
How Can I use xReffID_list text which is a string of comma separated numbers in the INcondition for int variables ?
Stored procedure:
DELIMITER $$
DROP PROCEDURE IF EXISTS lead_to_loan$$
CREATE PROCEDURE lead_to_loan(XRefID_list text)
BEGIN
DECLARE loanCount int(11) default 0;
DECLARE matchCount int(11) default 0;
DECLARE loan_XRefID int(11);
DECLARE loan_LEADS360ID int(11);
DECLARE loan_email varchar(100);
DECLARE loan_phone varchar(30);
DECLARE loan_cellphone varchar(20);
DECLARE loan_workphone varchar(20);
DECLARE loan_closeDate datetime;
DECLARE loan_FundedDate datetime;
DECLARE lead_id int(11);
DECLARE lead_RefId varchar(100);
DECLARE lead_Email varchar(100);
DECLARE lead_DayPhone varchar(50);
DECLARE lead_EveningPhone varchar(20);
DECLARE lead_Cellphone varchar(20);
DECLARE lead_DateAdded varchar(30);
DECLARE done boolean default false;
DECLARE startTime datetime;
DECLARE cursor_loanDetail CURSOR FOR
SELECT XRefID,LEADS360ID,email,phone,cellphone,workphone,closeDate,FundedDate
FROM fsbcorponline.view_loandetail where find_in_set(XRefID, XRefID_list) > 0;
DECLARE cursor_loanMatchLeads CURSOR FOR
SELECT id,RefId,Email,DayPhone,EveningPhone,Cellphone,DateAdded
FROM fsbcorponline.leads360leads
WHERE RefId !="" AND RefId IS NOT NULL AND RefId =loan_LEADS360ID AND loan_LEADS360ID>0 OR
Email !="" AND Email IS NOT NULL AND Email =loan_email OR
DayPhone !="" AND DayPhone IS NOT NULL AND DayPhone = loan_workphone OR
EveningPhone !="" AND EveningPhone IS NOT NULL AND EveningPhone= loan_phone OR
Cellphone !="" AND Cellphone IS NOT NULL AND Cellphone =loan_cellphone;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
set startTime = now();
OPEN cursor_loanDetail;
cursor_loanDetail_loop: loop
fetch cursor_loanDetail into loan_XRefID,loan_LEADS360ID,loan_email,loan_phone,loan_cellphone,loan_workphone,loan_closeDate,loan_FundedDate;
if done then
set done = false;
leave cursor_loanDetail_loop;
END if;
SET loanCount = loanCount+1;
OPEN cursor_loanMatchLeads;
cursor_loanMatchLeads_loop: loop
fetch cursor_loanMatchLeads into lead_id,lead_RefId,lead_Email,lead_DayPhone,lead_EveningPhone,lead_Cellphone,lead_DateAdded;
if done then
set done = false;
leave cursor_loanMatchLeads_loop;
END if;
SET matchCount = matchCount+1;
INSERT INTO `fsbcorponline`.`leads_to_loan`(`lead_id`,`lead_RefId`,`lead_Email`,`lead_DayPhone`,`lead_EveningPhone`,`lead_Cellphone`,`lead_DateAdded`,`loan_XRefID`,`loan_LEADS360ID`,`loan_email`,`loan_phone`,`loan_cellphone`,`loan_workphone`,`loan_closeDate`,`loan_FundedDate`)
VALUES(lead_id,lead_RefId,lead_Email,lead_DayPhone,lead_EveningPhone,lead_Cellphone,lead_DateAdded,loan_XRefID,loan_LEADS360ID,loan_email,loan_phone,loan_cellphone,loan_workphone,loan_closeDate,loan_FundedDate)
ON duplicate key update loan_updateCount = loan_updateCount +1 ;
leave cursor_loanMatchLeads_loop;
END loop cursor_loanMatchLeads_loop;
CLOSE cursor_loanMatchLeads;
END loop cursor_loanDetail_loop;
close cursor_loanDetail;
INSERT INTO `fsbcorponline`.`log`(`processName`,`pageName`,`path`,`status`,`note`,`processStartTime`,`processEndTime`)
VALUES('Store Procedure','Lead_to_Loan','Database','1',CONCAT('Loan Matches ',matchCount,' of total ',loanCount),startTime,now());
END$$
DELIMITER ;
You can use find_in_set to do this:
SELECT * FROM XXXX WHERE find_in_set(xreffID, xreffID_list) > 0
Hey you can use cast() function available with Mysql
http://dev.mysql.com/doc/refman/5.0/en/cast-functions.html#function_cast
Hope this will help you.