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.