I have a table structured as
Id int(10)
Parent int(10) --reference I'd
Name
This table contain organization structure. Example data
Id | parent | name
1 Null organization A
2 1 Office A
3 1 Office B
4 3 Room 1
5 3 Room 2
`
This example simple map that org. A have 2 offices,
Office B have 2 rooms .
Parent field hold either null (if no parent -end node-) or an Id of parent.
I want to fetch all rows of a parent row
I want my query to return all child's and children of children.
Can I do this with one query ?
Unless you have a fixed hierarchy depth, I can't think of how you could do it with one query in MySQL (could always be mistaken). If you have hierarchy depth of less than 255 you can do it with a single call to previously created recursive procedures like below.
Inner Procedure Inner
CREATE PROCEDURE `parent_child`(in `I_Parent` int)
BEGIN
DECLARE `V_done` INT;
DECLARE `V_Id` INT;
DECLARE `V_Parent` INT;
DECLARE `V_Name` VARCHAR(45);
DECLARE `cur1` CURSOR FOR SELECT * FROM `stackoverflow`.`parent_child` WHERE `Parent` = `I_Parent`;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET `V_done` = TRUE;
INSERT INTO `stackoverflow`.`parent_child_temp` SELECT `Id`, `Parent`, `Name` FROM `stackoverflow`.`parent_child` WHERE `Parent` IS NULL AND `Id` = `I_Parent`;
OPEN `cur1`;
read_loop: LOOP
FETCH `cur1` INTO `V_Id`, `V_Parent`, `V_Name` ;
IF `V_done` THEN
LEAVE read_loop;
END IF;
INSERT INTO `stackoverflow`.`parent_child_temp` SELECT `V_Id`, `V_Parent`, `V_Name`;
CALL `stackoverflow`.`parent_child`(V_Id);
END LOOP;
CLOSE `cur1`;
END
Wrapper Procedure Inner
CREATE PROCEDURE `parent_child_wrapper`(in `I_WrapperParent` int)
BEGIN
SET ##SESSION.max_sp_recursion_depth = 255;
DROP TABLE IF EXISTS `stackoverflow`.`parent_child_temp`;
CREATE TEMPORARY TABLE `stackoverflow`.`parent_child_temp` (id int, parent int, name varchar(45));
CALL `stackoverflow`.`parent_child`(`I_WrapperParent`);
SELECT * FROM `stackoverflow`.`parent_child_temp`;
END
Call SQL
CALL stackoverflow.parent_child_wrapper(1);
References
*http://dev.mysql.com/doc/refman/5.0/en/cursors.html
*How I query (with mysql) column names that "unfortunately" have round brackets?
*http://www.sitepoint.com/cursors-mysql-stored-procedures/
*How to echo print statements while executing a sql script
*http://dev.mysql.com/doc/refman/5.6/en/server-system-variables.html#sysvar_max_sp_recursion_depth
*MySql :: stored procedure recursive
*http://www.tutorialspoint.com/mysql/mysql-temporary-tables.htm
*How to get depth in mysql store procedure recursion?
Related
I store in my DB the demands some users can do. The demands can have differents status (stored as events), such as in progress, finished, waiting, and so on (there's 30ish differents status). The demands have differents deadlines corresponding of differents steps of the treatment.
I need to "freeze" some deadlines of the demands, if their current status belongs to a list of pre-defined ones.
In example :
If a demand has the status "A", I have to "freeze" the deadline 2 to 5.
If the status is "B" or "C", I have to "freeze" the deadline 3 to 5.
If the status is "D", I have to "freeze" the deadline 4 and 5.
I plan to use an EVENT that runs every day, at 19:00 to update (add 1 day) the differents deadlines of the concerned demands.
Table structures :
Table demand
id | someDatas | deadline1 | deadline2 | deadline3 | deadline4 | deadline5
---+-----------+-----------+-----------+-----------+-----------+-----------
| | | | | |
Table status
id | name
---+-----
|
Table events
id | id_demand | someOthersDatas | id_status
---+-----------+-----------------+----------
| | |
I wrote a query to get the demands corresponding of a list of status :
SELECT dem.*, st.`name` as 'statusName'
FROM `status` st
INNER JOIN `events` eve
ON eve.id_status = st.id
INNER JOIN `demand` dem
ON eve.id_demand = dem.id
WHERE st.`name` IN ('A', 'B', 'C', 'D')
AND eve.id IN
(
SELECT MAX(even.id) ev
FROM `demand` de
INNER JOIN `events` even
ON even.id_demand = de.id
GROUP BY de.id
);
This query works perfectly and I can get the desired informations for my treatment, I have the id of the demands, its deadlines and the name of the current status.
I don't mind storing this result in a temporary table such as :
DROP TEMPORARY TABLE IF EXISTS pendingDemands;
CREATE TEMPORARY TABLE IF NOT EXISTS pendingDemands
SELECT /* the query shown above */
To make sure the day I want to add to the deadline is valid (= not a day off) I wrote a function that calculate the next valid day :
DELIMITER //
DROP FUNCTION IF EXISTS `get_next_valid_date`;
CREATE FUNCTION `get_next_valid_date`(MyDate DATETIME) RETURNS DATETIME
BEGIN
REPEAT
SET MyDate = (DATE_ADD(MyDate, INTERVAL 1 DAY));
SET #someCondition = (select isDayOff(MyDate));
UNTIL (#someCondition = 0) END REPEAT;
RETURN MyDate;
END//
This function works perfectly and I get the expected results, and isDayOff() don't need to be detailed.
My problem is that I don't know how to use them (the temporary table pendingDemands and the function get_next_valid_date) together to update the table demand, I'm not skilled enough in SQL to build such pretty UPDATE query.
Any direction I could take?
I finally found a work around based on this answer
I created a stored procedure in which I'm using a cursor storing the query I was using to feed the pendingDemands temporary table.
Then, I looped over that cursor and used a CASE WHEN statement to determine the values to modify :
DELIMITER $$
DROP PROCEDURE IF EXISTS `freezePendingDeadlines` $$
CREATE PROCEDURE `freezePendingDeadlines`()
BEGIN
-- from http://stackoverflow.com/questions/35858541/call-a-stored-procedure-from-the-declare-statement-when-using-cursors-in-mysql
-- declare the program variables where we'll hold the values we're sending into the procedure;
-- declare as many of them as there are input arguments to the second procedure,
-- with appropriate data types.
DECLARE p_id INT DEFAULT 0;
DECLARE pT2P DATETIME DEFAULT NULL;
DECLARE pT3P DATETIME DEFAULT NULL;
DECLARE pT4P DATETIME DEFAULT NULL;
DECLARE pT5P DATETIME DEFAULT NULL;
DECLARE pstatusName VARCHAR(255) DEFAULT NULL;
-- we need a boolean variable to tell us when the cursor is out of data
DECLARE done TINYINT DEFAULT FALSE;
-- declare a cursor to select the desired columns from the desired source table1
-- the input argument (which you might or might not need) is used in this example for row selection
DECLARE demandCursor
CURSOR FOR
SELECT p.id,
p.T2P,
p.T3P,
p.T4P,
p.T5P,
P.statusName
FROM
(
SELECT dem.*, st.`name` as 'statusName'
FROM `status` st
INNER JOIN `events` eve
ON eve.id_status = st.id
INNER JOIN `demand` dem
ON eve.id_demand = dem.id
WHERE st.`name` IN ('A', 'B', 'C', 'D')
AND eve.id IN
(
SELECT MAX(even.id) ev
FROM `demand` de
INNER JOIN `events` even
ON even.id_demand = de.id
GROUP BY de.id
)
) AS p;
-- a cursor that runs out of data throws an exception; we need to catch this.
-- when the NOT FOUND condition fires, "done" -- which defaults to FALSE -- will be set to true,
-- and since this is a CONTINUE handler, execution continues with the next statement.
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
DROP TEMPORARY TABLE IF EXISTS days_off;
CREATE TEMPORARY TABLE IF NOT EXISTS days_off
(
date_off VARCHAR(5)
);
INSERT INTO days_off VALUES('01-01'),
('05-01'),
('05-08'),
('07-14'),
('08-15'),
('11-01'),
('11-11'),
('12-25');
-- open the cursor
OPEN demandCursor;
my_loop: -- loops have to have an arbitrary label; it's used to leave the loop
LOOP
-- read the values from the next row that is available in the cursor
FETCH demandCursor INTO p_id, pT2P, pT3P, pT4P, pT5P, pstatusName;
IF done THEN -- this will be true when we are out of rows to read, so we go to the statement after END LOOP.
LEAVE my_loop;
ELSE
CASE pstatusName
WHEN 'A' THEN
SET pT2P=get_next_valid_date(pT2P);
SET pT3P=get_next_valid_date(pT3P);
SET pT4P=get_next_valid_date(pT4P);
SET pT5P=get_next_valid_date(pT5P);
WHEN 'B' THEN
SET pT3P=get_next_valid_date(pT3P);
SET pT4P=get_next_valid_date(pT4P);
SET pT5P=get_next_valid_date(pT5P);
WHEN 'C' THEN
SET pT3P=get_next_valid_date(pT3P);
SET pT4P=get_next_valid_date(pT4P);
SET pT5P=get_next_valid_date(pT5P);
WHEN 'D' THEN
SET pT4P=get_next_valid_date(pT4P);
SET pT5P=get_next_valid_date(pT5P);
END CASE;
UPDATE `demand`
SET T2P=pT2P,
T3P=pT3P,
T4P=pT4P,
T5P=pT5P
WHERE id=p_id;
END IF;
END LOOP;
CLOSE demandCursor;
DROP TEMPORARY TABLE IF EXISTS days_off;
END$$
I want to add the salary from two tables in stored procedure on the basis of id column:
DDl:
create table salary1 (id varchar(20), salary varchar(20));
create table salary2 (id varchar(20), salary varchar(20));
DML:
insert into salary1 values('1', '100');
insert into salary1 values('2', '200');
insert into salary2 values('1', '10');
insert into salary2 values('2', '10');
Database: mysql
Output should like this:
id total_sal
1 110
2 210
My stored procedure look like:
CREATE PROCEDURE totalSal()
BEGIN
DECLARE tbl1_id varchar(30);
DECLARE tbl1_sal varchar(30);
DECLARE tbl2_id varchar(30);
DECLARE tbl2_sal varchar(30);
DECLARE total_sal varchar(30);
DECLARE c1 CURSOR FOR SELECT * FROM salary1;
DECLARE c2 CURSOR FOR SELECT * FROM salary2;
-- Open first cursor
OPEN c1;
LOOP
FETCH c1 INTO tbl1_id, tbl1_sal;
-- Open second cursor
OPEN c2;
LOOP
FETCH c2 INTO tbl2_id, tbl2_sal;
IF tbl1_id = tbl2_id THEN
set total_sal := tbl1_sal + tbl2_sal;
ELSE
set total_sal := tbl_sal;
END IF;
END LOOP;
CLOSE c2;
END LOOP;
CLOSE c1;
end $$
It got's successfully compiled, but when i am running the procedure i am getting the below error:
ERROR 1329 (02000): No data - zero rows fetched, selected, or processed
I have also used the DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1; in my procedure. but still my problem is unresolved.
If someone can solve this problem in oracle, that would also help me.
Note : I cannot perform join operation on these tables. Because of a few performance issues.
Thanks in advance !!!
Solution 1:
Using collection and only one iteration of 2 loop
You should consider to fix your performance issue on join. Performing loop is slower than a set base approach in most case.
If I follow your logic, what you realy want is to loop trough all the salary2 table for each salary1 row in order to found the right ID => millions of loop.
You can consider doing 2 separated loop and store data inside and indexed array. ( the key will be the tlb1_id).
If the key exist : sum the salary values, if not exist insert it inside the array.
At the end of the procedure, just select the array as table.
Solution 2:
Using a join on integer indexed columns
you can add a new integer column on each table
Populate this column with the casted value of the ID column
Add an index on these columns on each tables
After that you will be able to perform a join
Have a look at this fiddle http://sqlfiddle.com/#!9/c445de/1 , it can be time consuming to perform theses step and disk space consumuming to add a new columns and indexes but the join operation may be faster than before.
You can do something like this... I have moved the second cursor inside the loop so that it only goes over the id's from table 1. This should help the logic for the procedure but still I would recommend trying to figure out how to fix the join to get the results as that seems like an easier way and should be much faster if done correctly.
CREATE PROCEDURE totalSal()
BEGIN
DECLARE tbl1_id varchar(30);
DECLARE tbl1_sal varchar(30);
DECLARE tbl2_id varchar(30);
DECLARE tbl2_sal varchar(30);
DECLARE total_sal varchar(30);
DECLARE c1 CURSOR FOR SELECT * FROM salary1;
-- Open first cursor
OPEN c1;
LOOP
FETCH c1 INTO tbl1_id, tbl1_sal;
SELECT COUNT(*) INTO v_rowcount FROM salary2 WHERE id = tbl1_id;
IF v_rowcount > 0 THEN
Begin
DECLARE c2 CURSOR FOR SELECT * FROM salary2 WHERE id = tbl1_id;
-- Open second cursor
OPEN c2;
LOOP
FETCH c2 INTO tbl2_id, tbl2_sal;
IF tbl1_id = tbl2_id THEN
set total_sal := tbl1_sal + tbl2_sal;
ELSE
set total_sal := tbl_sal;
END IF;
END LOOP;
CLOSE c2;
END IF;
END
END LOOP;
CLOSE c1;
end $$
Well you asked for an answer without JOIN, but that seemed arbitrary, so here's an answer with JOIN.
SELECT
sums1.id
, S1Sum + S2Sum AS SalarySum
FROM (SELECT id, SUM(CAST(salary AS int)) AS S1Sum
FROM salary1
GROUP BY id) sums1
JOIN (SELECT id, SUM(CAST(salary AS int)) AS S2Sum
FROM salary2
GROUP BY id) sums2
ON sums1.id = sums2.id
I am guessing your performance is bad because all of your columns are varchar when they should be int or numeric. But we don't have much to go on so hopefully this helps you come to a solid solution.
Also the post was edited to add both MySQL and Oracle tags so it's difficult to determine what the syntax should be...
DELIMITER $$
CREATE PROCEDURE Load_Fact_List()
BEGIN
DECLARE Project_Number_Temp INT;
DECLARE Panel_Id_Temp INT;
DECLARE Employee_Id_Temp INT;
DECLARE Zip_Temp VARCHAR(255);
DECLARE Created_Date_Temp DATE;
DECLARE Country_Temp VARCHAR(255);
DECLARE no_more_rows BOOLEAN;
DECLARE loop_cntr INT DEFAULT 0;
DECLARE num_rows INT DEFAULT 0;
DECLARE load_cur CURSOR FOR
SELECT Project_Id, Panel_Id, Employee_Id, Zip, Created_Date
FROM Fact_List;
DECLARE CONTINUE HANDLER FOR NOT FOUND
SET no_more_rows = TRUE;
OPEN load_cur;
select FOUND_ROWS() into num_rows;
the_loop: LOOP
FETCH load_cur
INTO Project_Number_Temp, Panel_Id_Temp, Employee_Id_Temp, Zip_Temp, Created_Date_Temp;
IF no_more_rows THEN
CLOSE load_cur;
LEAVE the_loop;
END IF;
SET Country_Temp= (select Country from Zip where Zip= Zip_Temp);
INSERT INTO Test_Fact
(
Project_Key,
Campaign_Key,
Respondents_Key,
Event_Key,
Employee_Key,
Geography_Key,
Date_Key
)
SELECT (SELECT Project_Key from Project_Dim where Project_Id= Project_Number_Temp AND Quota_Country= Country_Temp),0,(SELECT MAX(Respondents_Key) from Respondents_Dim WHERE Panel_Id= Panel_Id_Temp),1,(select MAX(Employee_Key) from Employee_Dim WHERE Employee_Id= Employee_Id_Temp),(Select Geography_Key from Geography_Dim where Zip= Zip_Temp), (Select Date_Key from Date_Dim where Full_Date= Created_Date_Temp);
SET loop_cntr = loop_cntr + 1;
END LOOP the_loop;
select num_rows, loop_cntr;
END $$
The above code is properly working but it is damn slow. For every 1 hour it is loading 1000 records. I got lacks of records to load into fact table. can anyone suggest me any optimization?
Requirement is to load fact table by looping through other table and gathering required key values from dimension tables.
The usual procedure is actually like this.
You have your dimensions built and you just gathered the data you want to insert into your fact table in a temporary table. Then you insert this data in another temporary table like this:
INSERT INTO tmp_fact_table
(
fact_key,
dim1_key,
dim2_key,
...
fact1,
fact2
...
)
SELECT
ISNULL (f.fact_key, 0),
ISNULL (d1.sid, 0) as whatever,
ISNULL (d2.sid, 0) as whatever2,
...
ISNULL (tt.fact1, 0),
ISNULL (tt.fact2, 0)
FROM
yourTempTable tt
LEFT JOIN Dim1 d1 ON tt.identifying_column = d1.identifying_column
...
LEFT JOIN fact_table f ON
f.dim1_key = d1.sid
AND f.dim2_key = d2.sid
where
fact_key is the identifying column in your fact table
dim1_key is the foreign key in your fact table to the dimensions
fact1 and so on are the facts you want in your fact table, clear
the ISNULL() function returns 0 when no entry is found. 0 is the id of your dummy row in each dimension for unknown data
Then you will have a table where you have the IDs of your dimensions linked to the data you want to import into your fact table with 0 as fact key when the entry in the fact table does not already exist and the ID of the fact table entry otherwise.
Then you update the fact table where tmp_fact_table.fact_key != 0
Then you insert into the fact table where tmp_fact_table.fact_key = 0
That's it.
I'm doing this with millions of rows and it takes about half an hour. 300,000 rows is peanuts.
I have a table which references itself, like this:
CREATE TABLE Foo (
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
parent INT NULL,
name VARCHAR (30) NOT NULL,
FOREIGN KEY (parent) REFERENCES Foo(id) ON DELETE CASCADE);
Sample data:
id parent name
1 NULL a
2 NULL b
3 1 a1
4 1 a2
5 3 a1x
6 3 a2x
I want to write queries which will list the ancestors and decenders of a given row, e.g.
CALL find_ancestors('a1x')
Will return
id name
3 a1
1 a
and
CALL find_descendants('a')
Will return
id name
3 a1
5 a1x
How can I write these stored procedures for MySQL 5? Thanks
Bonus question for bounty: also select the distance of the returned row from the source and pass a maximum-distance parameter to the procedure, e.g.
CALL find_ancestors('a1x')
Will return
id name distance
3 a1 1
1 a 2
and
CALL find_ancestors_bounded('a1x',1)
Will return
id name distance
3 a1 1
Lets say we have a table with four elements, id, item, class and parent_id. We want to have the complete Ancestors of any given Item, what we need to do is a custom mysql function that will actually loop through every record looking for a match for our item parent_id, once it founds a match, if the matched item has a parent_id, it will start looping again, and so forth. Every time our function finds a match, it will store it in a comma separated string that will be returned in the end (ex: 1,2,3,4)
Our function would look something like this:
DELIMITER $$
DROP FUNCTION IF EXISTS `junk`.`GetAncestry` $$
CREATE FUNCTION `junk`.`GetAncestry` (GivenID INT) RETURNS VARCHAR(1024)
DETERMINISTIC
BEGIN
DECLARE rv VARCHAR(1024);
DECLARE cm CHAR(1);
DECLARE ch INT;
SET rv = '';
SET cm = '';
SET ch = GivenID;
WHILE ch > 0 DO
SELECT IFNULL(parent_id,-1) INTO ch FROM
(SELECT parent_id FROM pctable WHERE id = ch) A;
IF ch > 0 THEN
SET rv = CONCAT(rv,cm,ch);
SET cm = ',';
END IF;
END WHILE;
RETURN rv;
END $$
DELIMITER ;
This code is authored by RolandoMySQLDBA
In MySQL, I am converting a table from a single row per item type (a quantity of items) to a single row per item, so that additional detail can be stored about individual items.
Here is an example source table:
id parent_id qty item_type
-- --------- --- ---------
1 10291 2 widget
2 10292 4 thinger
I want to create a new table with a new column containing info that cannot be applied to more than one item. Thus, the above table would end up as follows:
id parent_id item_type info
-- --------- --------- ----
1 10291 widget [NULL]
2 10291 widget [NULL]
3 10292 thinger [NULL]
4 10292 thinger [NULL]
5 10292 thinger [NULL]
6 10292 thinger [NULL]
Is there a way I can iterate or loop each row of the source table, inserting a number of records equal to the source qty column? I would prefer to do this in sql instead of code to keep all of the conversion steps together (there are many others).
You can do with stored procedure. That will be like below. Below is stored procedure I am using for inserting products into log based on their quantity.
Seem you have to do similar task. You can get how to use database cursor in stored procedure to loop over a result set in MySQL from below example.
DELIMITER $$
DROP PROCEDURE IF EXISTS CursorProc$$
CREATE PROCEDURE CursorProc()
BEGIN
DECLARE no_more_products, quantity_in_stock INT DEFAULT 0;
DECLARE prd_code VARCHAR(255);
DECLARE cur_product CURSOR FOR
SELECT productCode FROM products;
DECLARE CONTINUE HANDLER FOR NOT FOUND
SET no_more_products = 1;
/* for loggging information */
CREATE TABLE infologs (
Id int(11) NOT NULL AUTO_INCREMENT,
Msg varchar(255) NOT NULL,
PRIMARY KEY (Id)
);
OPEN cur_product;
FETCH cur_product INTO prd_code;
REPEAT
SELECT quantityInStock INTO quantity_in_stock
FROM products
WHERE productCode = prd_code;
IF quantity_in_stock < 100 THEN
INSERT INTO infologs(msg)
VALUES (prd_code);
END IF;
FETCH cur_product INTO prd_code;
UNTIL no_more_products = 1
END REPEAT;
CLOSE cur_product;
SELECT * FROM infologs;
DROP TABLE infologs;
END$$
DELIMITER;
Seems your task is 90% same as above procedure. Just do needful changes. It will work.
I think you can create stored procedure, declare a cursor that reads source table and for each row inserts qty rows into destination table.
Based on other answers which provided some insight, I was able to find additional information (by Kevin Bedell) to create a stored procedure and use a cursor in a loop. I have simplified my solution so that it matches the example in my question:
DROP PROCEDURE IF EXISTS proc_item_import;
DELIMITER $$
CREATE PROCEDURE proc_item_import()
BEGIN
# Declare variables to read records from the cursor
DECLARE parent_id_val INT(10) UNSIGNED;
DECLARE item_type_val INT(10) UNSIGNED;
DECLARE quantity_val INT(3);
# Declare variables for cursor and loop control
DECLARE no_more_rows BOOLEAN;
DECLARE item_qty INT DEFAULT 0;
# Declare the cursor
DECLARE item_cur CURSOR FOR
SELECT
i.parent_id, i.qty, i.item_type
FROM items i;
# Declare handlers for exceptions
DECLARE CONTINUE HANDLER FOR NOT FOUND
SET no_more_rows = TRUE;
# Open the cursor and loop through results
OPEN item_cur;
input_loop: LOOP
FETCH item_cur
INTO parent_id_val, item_type_val, quantity_val;
# Break out of the loop if there were no records or all have been processed
IF no_more_rows THEN
CLOSE item_cur;
LEAVE input_loop;
END IF;
SET item_qty = 0;
qty_loop: LOOP
INSERT INTO items_new
(parent_id, item_type)
SELECT
parent_id_val, item_type_val;
SET item_qty = item_qty + 1;
IF item_qty >= quantity_val THEN
LEAVE qty_loop;
END IF;
END LOOP qty_loop;
END LOOP input_loop;
END$$
DELIMITER ;
Before asking this question, I had not used a stored procedures, cursors, or loops. That said, I have read and encountered them frequently on SE and elsewhere, and this was a good opportunity to learn
It may be worth noting that the example on Kevin's page (linked above) does not use END%% (just END) which caused some headache in trying to get the script to work. When creating a procedure, it is necessary to change the delimiter temporarily so that semicolons terminate statements inside the procedure, but not the creation process of the procedure itself.
That is just an example of code that I have here, it is not adapted to your needs, but it does exactly what you need, and it is simple than a procedure, or temporary table.
SELECT event, id, order_ref, storeitem_barcode_create(8), NOW()
FROM (
SELECT mss.id, mss.event, mss.order_ref, mss.quantity, mss.product_id,
#rowID := IF(#lastProductID = mss.product_id AND #lastID = mss.id, #rowID + 1, 0) AS rowID,
#lastProductID := mss.product_id,
#lastID := mss.id
FROM module_barcode_generator mbg,
(SELECT #rowID := 0, #lastProductID := 0, #lastID := 0) t
INNER JOIN module_events_store_sold mss ON mss.order_ref = "L18T2P"
) tbl
WHERE rowId < quantity;
Typo in JYelton's solution for his/her own question:
FETCH item_cur
INTO parent_id_val, item_type_val, quantity_val;
Should be:
FETCH item_cur
INTO parent_id_val, quantity_val, item_type_val;
Otherwise very good.