recursive mysql procedure for getting a referral branch - mysql

I recently got acquainted with procedures and functions in mysql and still don't quite understand how they work. I'm trying to make a referral branch to display on the site.
DELIMITER $$
CREATE PROCEDURE fb2_get_referrals (IN refer_id INT UNSIGNED, IN parent_level INT UNSIGNED, OUT return_path MEDIUMTEXT)
BEGIN
DECLARE parent_id INT UNSIGNED;
DECLARE path_result MEDIUMTEXT;
DECLARE referral_offset INT UNSIGNED;
DECLARE count_user_referrals INT UNSIGNED;
SET referral_offset = 0;
SET parent_level = parent_level + 1;
SET count_user_referrals = (SELECT COUNT(fb2_users.id) FROM fb2_users WHERE fb2_users.referral_id = refer_id);
IF(count_user_referrals > 0) THEN
label1: WHILE referral_offset < count_user_referrals DO
SELECT
CONCAT(
'{',
'"id":',fb2_users.id,',',
'"level":',parent_level,
'}'
) as path,
fb2_users.id as pi,
parent_level as pl
INTO return_path, parent_id, parent_level
FROM fb2_users
WHERE fb2_users.referral_id = refer_id
LIMIT 1 OFFSET referral_offset;
CALL fb2_get_referrals(parent_id, parent_level, path_result);
SELECT CONCAT(path_result, return_path) INTO return_path;
SET referral_offset = referral_offset + 1;
END WHILE label1;
END IF;
END$$
DELIMITER $$
CREATE FUNCTION getPath(user_id INT) RETURNS MEDIUMTEXT DETERMINISTIC
BEGIN
DECLARE res MEDIUMTEXT;
CALL fb2_get_referrals(user_id,0, res);
RETURN res;
END$$
id
r_id
1
null
2
null
3
null
4
null
5
null
6
1
7
6
8
6
9
7
as a result, I get null

Related

MySQL Procedure variable receiving null on count(*)

Why when I do Select count(*) From table1 I receive 300 but if I do SELECT end = COUNT(*) FROM table1; returns null
Here is the fiddle example https://dbfiddle.uk/ZHzoaztV
code snippet:
CREATE TABLE table1(
start int NOT NULL,
id int PRIMARY KEY AUTO_INCREMENT,
counter int NOT NULL,
difference int NOT NULL,
end int NOT NULL
);
CREATE PROCEDURE doWhile()
BEGIN
DECLARE i INT DEFAULT 1;
DECLARE start INT DEFAULT 120;
DECLARE counter INT DEFAULT 1;
DECLARE end INT DEFAULT 300;
WHILE (i <= end) DO
INSERT INTO table1 VALUES (start,null,counter,start+counter,end);
SET i = i+1;
SET counter = counter+1;
END WHILE;
END;
CALL doWhile();
SELECT * FROM table1;
CREATE PROCEDURE insertMore()
BEGIN
DECLARE start INT;
DECLARE counter INT DEFAULT 1;
DECLARE end INT;
SELECT end = COUNT(*) FROM table1;
SELECT start = MAX(id)+1 FROM table1;
-- SELECT COUNT(*) FROM table1;
WHILE (counter <= end) DO
INSERT INTO table1 VALUES (start,null,counter,start+counter,end);
SET counter = counter+1;
END WHILE;
END;
CALL insertMore();
SELECT * FROM table1;
I expected to return 300, so hopefully my function should do it right
You have a problem with start and end Variable
Can you try this :
CREATE PROCEDURE insertMore()
BEGIN
DECLARE start INT;
DECLARE counter INT DEFAULT 1;
DECLARE end INT;
SELECT COUNT(*) into end FROM table1;
SELECT max(id)+1 into start FROM table1;
-- SELECT COUNT(*) FROM table1;
WHILE (counter <= end) DO
INSERT INTO table1 VALUES (start,null,counter,start+counter,end);
SET counter = counter+1;
END WHILE;
END;
Try it here : https://dbfiddle.uk/X6vP3wKW

convert TSQL query to MySQL

I need to convert this tSQL query into MySQL query version 5.7.14
I tried declare parameter with the following syntax
declare #commissionType INT;
but I reached a dead end and need help to convert this query
CREATE PROCEDURE MyProc
#chainId int
as
BEGIN
declare #commissionMethod int
declare #commissionType int
declare #value decimal(18,2)
set #commissionMethod = 1
set #value = 200000
set #commissionType = (select CommissionType from CommissionRules
where ChainId = #chainId )
IF(#commissionMethod = 1)
BEGIN
print('alert')
select (Fixed * #value) / 100 from CommissionRules where ChainId = #chainId
END
END
This is pretty much a literal conversion. But check your logic. It doesn't make any sense. v_commissionMethod is always 1 and v_commissionType is not even being used after selecting a value.
DELIMITER //
CREATE PROCEDURE MyProc (
p_chainId int)
BEGIN
declare v_commissionMethod int;
declare v_commissionType int;
declare v_value decimal(18,2);
set v_commissionMethod = 1;
set v_value = 200000;
select CommissionType into v_commissionType from CommissionRules
where ChainId = p_chainId ;
IF(v_commissionMethod = 1)
THEN
/* print('alert') */
select (Fixed * v_value) / 100 from CommissionRules where ChainId = p_chainId;
END IF;
END;
//
DELIMITER ;
Something like this:
DELIMITER $$
CREATE PROCEDURE MyProc (
in_chainId unsigned
) as
BEGIN
select v_CommissionType := CommissionType,
v_commissionMethod = 1,
v_value = 200000
from CommissionRules
where ChainId = in_chainId;
if v_commissionMethod = 1 then
select 'alert';
select (Fixed * v_value) / 100
from CommissionRules
where ChainId = in_chainId
end if;
END;$$
DELIMITER ;

How return 1 after insert and return 2 after update in a single stored procedure in SQL Server 2008

This procedure does not return 2, it always returns 1. How to return 1 when insert happens, and return 2 when an update happens?
It is perfectly insert and update but return value always 1.
CREATE PROCEDURE [dbo].[sp_SaveEmployeeDetails]
#employeeID int
,#first_name varchar(255)
,#middle_name varchar(255)
,#last_name varchar(255)
,#gender varchar(255)
,#date_of_birth date
,#EntryBy varchar(255)
--,#ActionStatus int = NULL OUTPUT
AS
BEGIN
SET NOCOUNT OFF;
DECLARE #ActionStatus int;
IF NOT EXISTS(Select * From employees
Where employeeID =#employeeID)
BEGIN
INSERT INTO [dbo].[employees] ([first_name], [middle_name], [last_name], [gender], [date_of_birth], [EntryBy], [EntryDate])
VALUES (#first_name, #middle_name, #last_name, #gender, #date_of_birth, #EntryBy, getdate())
SET #ActionStatus = 1;
END
ELSE
BEGIN
UPDATE employees
SET [first_name] = #first_name,
[middle_name] = #middle_name,
[last_name] = #last_name,
[gender] = #gender,
[date_of_birth] = #date_of_birth,
[EntryBy] = #EntryBy,
[EntryDate] = getdate()
WHERE
employeeID = #employeeID
SET #ActionStatus = 2;
END
RETURN #ActionStatus;
--also try SELECT #ActionStatus;
END
The value you return here
RETURN #ActionStatus;
...might be set to a variable directly, its the procedure's "own" return value. Look at this example:
CREATE PROCEDURE dbo.TestReturn(#IntValue INT)
AS
BEGIN
IF #IntValue=0
RETURN 0;
ELSE
RETURN 1;
END
GO
DECLARE #RetVal INT;
EXEC #RetVal = dbo.TestReturn -1;
SELECT #RetVal;
EXEC #RetVal = dbo.TestReturn 0;
SELECT #RetVal;
EXEC #RetVal = dbo.TestReturn 1;
SELECT #RetVal;
GO
DROP PROCEDURE dbo.TestReturn;
It comes back with 1-0-1 as expected.
But this return value is bound to INT and is meant to reflect execution states (like error codes, success level...) In your case this seems to be the case...
If you think about output of any other data use OUTPUT parameters.
CREATE PROCEDURE dbo.TestReturn(#IntValue INT, #TestPrm VARCHAR(100) OUTPUT)
AS
BEGIN
IF #IntValue=0
BEGIN
SET #TestPrm='Value was 0'
RETURN 0;
END
ELSE
BEGIN
SET #TestPrm='Value was something else';
RETURN 1;
END
END
GO
DECLARE #RetVal INT;
DECLARE #RetVarchar VARCHAR(100);
EXEC #RetVal = dbo.TestReturn -1,#RetVarchar OUTPUT;
SELECT #RetVal,#RetVarchar;
EXEC #RetVal = dbo.TestReturn 0,#RetVarchar OUTPUT;
SELECT #RetVal,#RetVarchar;
EXEC #RetVal = dbo.TestReturn 1,#RetVarchar OUTPUT;
SELECT #RetVal,#RetVarchar;
GO
DROP PROCEDURE dbo.TestReturn;

MYSQL Function gets progressively slower

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$$

MySQL: failed to create function {functionName}

This works on MySQL 5.0.41, but on 5.1.31 it just says "failed to create function".
I type this in the console:
delimiter |
<press enter>
CREATE DEFINER=`root`#`localhost` FUNCTION `ucwords`( str VARCHAR(128) ) RETURNS varchar(128) CHARSET utf8
BEGIN
DECLARE c CHAR(1);
DECLARE s VARCHAR(128);
DECLARE i INT DEFAULT 1;
DECLARE bool INT DEFAULT 1;
DECLARE punct CHAR(17) DEFAULT ' ()[]{},.-_!#;:?/';
SET s = LCASE( str );
WHILE i < LENGTH( str ) DO
BEGIN
SET c = SUBSTRING( s, i, 1 );
IF LOCATE( c, punct ) > 0 THEN
SET bool = 1;
ELSEIF bool=1 THEN
BEGIN
IF c >= 'a' AND c <= 'z' THEN
BEGIN
SET s = CONCAT(LEFT(s,i-1),UCASE(c),SUBSTRING(s,i+1));
SET bool = 0;
END;
ELSEIF c >= '0' AND c <= '9' THEN
SET bool = 0;
END IF;
END;
END IF;
SET i = i+1;
END;
END WHILE;
RETURN s;
END |
<press enter>
I have even tried minimizing it to just:
delimiter |
<press enter>
CREATE DEFINER=`root`#`localhost` FUNCTION `ucwords`( str VARCHAR(128) ) RETURNS varchar(128) CHARSET utf8
BEGIN
DECLARE s VARCHAR(128);
RETURN s;
END |
<press enter>
I have even tried it without the definer, just using:
delimiter |
<press enter>
CREATE FUNCTION `ucwords`( str VARCHAR(128) ) RETURNS varchar(128) CHARSET utf8
BEGIN
DECLARE s VARCHAR(128);
RETURN s;
END |
<press enter>
Did you recently upgrade from MySQL 5.0 to 5.1? If so, you need to run the mysql_upgrade script to update the system tables. In this case, the mysql.proc table schema changed, which could explain your problem:
http://dev.mysql.com/doc/refman/5.1/en/mysql-upgrade.html
More on upgrading to 5.1:
http://dev.mysql.com/doc/refman/5.1/en/upgrading-from-previous-series.html