Running the loop stops after changing the Counter Variable - mysql

When I run the following code, it always returns a NULL value for the two variables inside the loop.
SET #c = '';
SET #i = 4;
REPEAT
SET #c = CONCAT(
' -> ',
(
SELECT name
FROM c
WHERE id = #i
),
#c
);
SET #i = (
SELECT pid
FROM c
WHERE id = #i
);
UNTIL TRIM(COALESCE(#i, '')) = ''
END REPEAT;
The loop must be run at least 4 times; But in the second run, the run of the loop ends.
The value is set for variables in the first time that the loop is executed; But in the second run of the loop, the value of both changes to NULL and the loop stops. However, inside table c there are related values for the execution of the loop.
The problem occurs when the value of the variable #i in the loop
changes.
For example, the first time the loop is executed, the value of #i becomes 3, and there is an id with a value of 3 in table c in addition to the initial value of 4.
Structure of Table c
CREATE TABLE c (
id int UNSIGNED NOT NULL AUTO_INCREMENT,
name varchar(50) NOT NULL,
pid int UNSIGNED DEFAULT NULL,
PRIMARY KEY (id)
) ENGINE = INNODB,
AUTO_INCREMENT = 1,
CHARACTER SET utf8mb4,
COLLATE utf8mb4_unicode_ci;
ALTER TABLE c
ADD CONSTRAINT c_pid_foreign FOREIGN KEY (pid) REFERENCES c (id);
INSERT INTO c(id, name, pid)
VALUES (1, '1st', NULL),
(2, '2nd', 1),
(3, '3rd', 2),
(4, '4th', 3);
Thank you in advance for the good cooperation of the people to solve this problem.

I was finally able to solve my question after about more than a day; Of course, I did not understand the cause of this problem and I do not know why MySQL changes the return value of the result of the SELECT statement to NULL when using the #i variable after changing its value inside the loop?!!!
The solution is as follows:
SET #d = '';
SET #j = 4;
REPEAT
SELECT #c := name
FROM c
WHERE id = #j;
SET #d = CONCAT(' -> ', #c, #d);
SELECT #i := pid
FROM c
WHERE id = #j;
SET #j = #i;
UNTIL TRIM(COALESCE(#j, '')) = ''
END REPEAT;
Here are the best solution to this problem:
SET #d = '';
SET #j = 4;
REPEAT
SELECT name INTO #c
FROM c
WHERE id = #j;
SET #d = CONCAT(' -> ', #c, #d);
SELECT pid INTO #i
FROM c
WHERE id = #j;
SET #j = #i;
UNTIL TRIM(COALESCE(#j, '')) = ''
END REPEAT;
I hope it is useful for others as well.

Related

filter ids by comma separated input ids MYSQL [duplicate]

can anybody tell me how to implement a split function in mysql which behaves like Javascript split.
I want a function like this
SELECT Split('a,b,c,d', ',') AS splitted
Which give result like
splitted
--------------
a
b
c
d
Can anybody help me?
I saw some answers here, and somewhere else, but those functions need the position of string to be returned, which I don't want/have
Thank you
It is possible:
-- Preparation
CREATE TABLE tb_Digits (d int(11) UNSIGNED NOT NULL PRIMARY KEY);
INSERT tb_Digits VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
INSERT IGNORE tb_Digits
SELECT o1.d+o2.d*10+o3.d*100+o4.d*1000 d FROM tb_Digits o4, tb_Digits o3, tb_Digits o2, tb_Digits o1;
CREATE PROCEDURE pc_splitStr(IN in_string TEXT, IN in_separator CHAR(1))
COMMENT 'Use (SELECT word FROM return_splitStr) para ver o resultado'
BEGIN
DECLARE o_limit INT(11) UNSIGNED DEFAULT LENGTH(in_string)-LENGTH(REPLACE(in_string,in_separator,''))+1;
DROP TABLE IF EXISTS return_splitStr;
CREATE TEMPORARY TABLE return_splitStr (word TEXT);
INSERT return_splitStr
SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(in_string, in_separator, d.d), in_separator, -1)
FROM tb_Digits d
WHERE d.d>0 AND d.d<=o_limit;
END;
-- Execution
CALL pc_splitStr('a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z',',');
SELECT word FROM return_splitStr;
You can create a function and return the specific result you are waiting for.
here is an example with multi ids in one column extracted and queried to get the specific values :
DROP FUNCTION IF EXISTS getValuesByOptionIds;
DELIMITER $$
CREATE FUNCTION getValuesByOptionIds
(
optionIds VARCHAR(255)
)
RETURNS VARCHAR(255)
DETERMINISTIC
BEGIN
DECLARE iter INTEGER DEFAULT 0;
DECLARE result VARCHAR(255) DEFAULT '';
DECLARE previousIter VARCHAR(255) DEFAULT '';
iters: WHILE LENGTH(SUBSTRING_INDEX(optionIds, ',', iter + 1)) != LENGTH(previousIter) DO
SET iter = iter + 1;
SET previousIter = SUBSTRING_INDEX(optionIds, ',', iter);
IF iter = 1 THEN
SET result = (select tmp.value from eav_attribute_option_value tmp where tmp.option_id = SUBSTRING_INDEX(optionIds, ',', iter));
ELSEIF iter > 1 THEN
SET result = concat(result, ',', (select tmp.value from eav_attribute_option_value tmp where tmp.option_id = SUBSTRING_INDEX(SUBSTRING_INDEX(optionIds, ',', iter), ',', -1)));
END IF;
END WHILE iters;
RETURN result;
END$$
DELIMITER ;

Update table from table with multiple rows of data

I have a function with several product ID's in a table. I want to perform this update and set these variables to the desired product ID's. Right now, this updates only the first productID from my function that splits the multiple values of my variable. I'm aware a CTE would be ideal here, but I'm not great at the syntax.
My desired outcome is for each value in #CSV, execute the update statement. Any assistance would be great.
DECLARE
#MaxAllowance INT = 25,
#AllowanceType INT = 2,
#AllowancePeriod INT = 2,
#Account_ID INT = 13379,
#CSV VARCHAR(50) = '506,280,281,282,286',
#count INT
DECLARE #DiscountClass_ID INT =
(
SELECT DiscountClass_ID FROM Account WHERE Account_ID = #Account_ID
);
SELECT #count =
(
SELECT COUNT(*) FROM dbo.FN_csv_tolist(#CSV)
);
declare #ProductID int
declare #rn int
declare ItemCursor cursor local static forward_only read_only for
select Value,
row_number() over(partition by Value order by Value) as rn
FROM dbo.FN_csv_tolist(#CSV)
open ItemCursor
fetch next from ItemCursor
into #ProductID, #rn
WHILE #count > 0
begin
update dbo.ProdTerm
SET MaxAllowance = #MaxAllowance,
AllowanceType = #AllowanceType,
AllowancePeriod = #AllowancePeriod
WHERE Product_ID = #ProductID
SET #count = #count - 1;
fetch next from ItemCursor
into #ProductID, #rn
end
close ItemCursor
deallocate ItemCursor
I am a little confused by your code, I am not sure of the reason for the need of a cursor, as the code could be a lot simpler. One way would be:
DECLARE #MaxAllowance INT = 25,
#AllowanceType INT = 2,
#AllowancePeriod INT = 2,
#CSV VARCHAR(50) = '506,280,281,282,286';
WITH Test
AS
(
SELECT [Value]
FROM dbo.FN_csv_tolist(#CSV)
)
UPDATE PT
SET MaxAllowance = #MaxAllowance,
AllowanceType = #AllowanceType,
AllowancePeriod = #AllowancePeriod
FROM dbo.ProdTerm AS PT
INNER JOIN Test AS T ON PT.Product_ID = T.Value;

Preventing duplicates while selecting into variable in MySQL

My function generates unique combinations from Items table and stores into list variable. In the end of generation, it returns list as a result and application processes every combination: saves into Combo table.
The Problem
It checks every time for duplicates from another tables called Combo which is getting filled in the second step of process (by application, not by function itself.)
But, It doesn't check for duplicates inside listvariable before inserting into it newly generated combinations.
So I'm getting result from function with duplicates inside the result itself. For example. 3423 appears here in the result 2 times:
3410;3463;3423;3489;3446;3445;3417;3436;3497;3454;3491;3420;3502;3496;3458;3493;3439;3499;3497;3487;3486;3504;3458;3501;3503;3441;3443;3453;3508;3474;3469;3497;3508;3433;3451;3449;3422;3453;3428;3475;3474;3458;3480;3422;3488;3432;3501;3414;3425;3444;3509;3502;3440;3422;3472;3501;3477;3483;3449;3480;3456;3463;3493;3476;3479;3425;3485;3464;3410;3434;3488;3504;3439;3423;3434;3486;3448;3456;3496;3413;3428;3482;3439;3437;3473;3420;3439;3470;3463;3494;3415;3442;3428;3500;3488;3478;3475;3417;3472;3463
How can I check list itself for duplicates before insertion?
Details
My function:
SELECT gen_n_uniq_perms_by_cat(1, 100, 1, 45, 1, 120, 20) as comb
which look like:
BEGIN
SET #result := "";
SET #counter := 0;
iterat :
LOOP
SELECT
gen_uniq_perm_by_cat(
permSize ,
user_id ,
catID ,
itemType ,
tsc_id ,
tries
) INTO #combo;
IF(ISNULL(#combo)) THEN
RETURN #result;
ELSE
SET #result := CONCAT_WS(';' ,#result ,#combo);
END
IF;
SET #counter := #counter + 1;
IF #counter > permCount THEN
RETURN #result;
END
IF;
END
LOOP
iterat;
END
and gen_uniq_perm_by_cat looks like:
BEGIN
iterat :
LOOP
SELECT
SUBSTRING_INDEX(
GROUP_CONCAT(`id` ORDER BY RAND() SEPARATOR '-') ,
'-' ,
permSize
) INTO #list
FROM
`Item`
LEFT JOIN `ItemCategory` ON `Item`.`id` = `ItemCategory`.`itemID`
WHERE
(`Item`.`user_id` = user_id)
AND(`ItemCategory`.`catID` = catID)
AND(`Item`.`type` = itemType);
SET #md5 := MD5(CONCAT_WS('-' , #list , tsc_id));
IF(
SELECT
count(*)
FROM
`Combo`
WHERE
`Combo`.`hash` = #md5
LIMIT 1
) = 0 THEN
RETURN #list;
END
IF;
SET tries := tries - 1;
IF tries = 0 THEN
RETURN NULL;
END
IF;
END
LOOP
iterat;
END
generates unique (never created in past) combinations by following arguments:
permSize = 1
permCount =100
user_id = 1
catID = 45
itemType = 1
tsc_id = 120
tries = 20
Use NOT LIKE for this purpose. In your case, replace corresponding condition lines with following:
IF(ISNULL(#combo)) THEN
RETURN #result;
END IF;
IF(#result NOT LIKE CONCAT('%' , #combo , '%')) THEN
SET #result := CONCAT_WS(';' ,#result ,#combo);
SET #counter := #counter + 1;
END IF;
IF #counter = permCount THEN
RETURN #result;
END IF;

Fill a column with a sequence int numbers in MySQL

I have to alter my table adding a column that I have to fill with sequence of int numbers. Does anyone knows how can I fill this column?
I tried this:
set #i = 1;
Update db.table set new_column=(#i := #i+1) where id>0;
But this just fill all lines with the number 2. =(
you can easy do so:
UPDATE db.table
CROSS JOIN ( SELECT #myid := 0) AS parameter
SET new_column = #myid := (#myid +1);
Try
Update your_table
join
(
select id, #i := #i+1 as rank
from your_table
cross join (select #i := 0) ia
where id > 0
order by id
) tmp on tmp.id = your_table.id
set your_table.new_column = tmp.rank
All this answers works for me. And I did another one
delimiter #
create procedure fill_column()
begin
declare v_max int unsigned default 83;-- number of registers that I have
declare v_counter int unsigned default 0;
declare a int default 0;
start transaction;
while v_counter < v_max do
update db.tb_table set new_column= (#a := #a+1) where id> 0;
set v_counter=v_counter+1;
end while;
commit;
end #
call fill_column();
Thanks guys =)

MySQL, CONCAT in SELECT statement

(SELECT CONCAT(#I, '_Delta') FROM table WHERE id_tb = #ID)
I'm using the above statement as part of an INSERT. The problem is the whole line is being translated to the value of #I (this is an index, values are i.e. 0, 1, 2, ...). So, instead of getting the output of the SELECT I'm getting 0, 1, 2, ...
The expected value of the CONCAT is like "0_Delta", then "1_Delta", etc. Replacing the CONCAT by one of this works.
Any comments will be appreciated. Thanks!
[code]
DROP TABLE IF EXISTS xxx_tb;
CREATE TABLE xxx_tb
(
i_Validity INT,
Delta INT
);
DROP TRIGGER IF EXISTS AFTER_INSERT_ON_tb_in;
DELIMITER $$
CREATE TRIGGER AFTER_INSERT_ON_tb_in
AFTER INSERT ON tb_in
FOR EACH ROW
BEGIN
SET #ID = NEW.id_tb;
SET #TYPE = (SELECT Type FROM header WHERE id_tb = #ID);
IF #TYPE = 'abcd' THEN
SET #SAMPLES = (SELECT SampleNumber FROM table WHERE id_tb = #ID);
IF(#SAMPLES > 1) THEN
SET #I = 0;
WHILE(#I < #SAMPLES) DO
INSERT INTO xxx_tb
(
i_Validity,
Delta
)
VALUES
(
(SELECT 0_Validity FROM table WHERE id_tb = #ID),
(SELECT CONCAT(#I, '_Delta') FROM table WHERE id_tb = #ID)
);
SET #I = #I + 1;
END WHILE;
END IF;
END IF;
END$$
DELIMITER ;
[code]
delta is declared as an integer. You are getting a silent conversion from the string value. Because #i is at the beginning, that is the value you are getting.
You can try declaring it as varchar(255) if you want a string value.
Your insert can be written more easily as an insert . . . select:
INSERT INTO xxx_tb(i_Validity, Delta)
SELECT `0_Validity`, CONCAT(#I, '_Delta')
FROM table WHERE id_tb = #ID);