No-Data error in stored procedure - mysql

I am in the process of converting a SQL Server 2005 database to MySQL and having problems with a Stored procedure. I'm new to MySQL stored procedures so I'm sure it is a problem with my conversion but I'm not seeing it.
The stored procedure is supposed to generate a temporary table which is used to populate a Data Grid View in a vb.net application. However, I'm getting the error "Data No Data - Zero rows fetched, selected or processed.". Seems simple enough but the select procedure in the stored procedure will get data if I just run it as a query which is why I don't understand why the error.
I'm really hoping someone can tell me why because I have several hundred stored procedures to convert and I'm having this problem on the very first one.
Here's the Stored Procedure:
DELIMITER $$
DROP PROCEDURE IF EXISTS `usp_get_unassigned_media`$$
CREATE DEFINER=`showxx`#`67.111.11.110` PROCEDURE `usp_get_unassigned_media`()
BEGIN
/* GET CURSOR WITH LOCAL LOCATIONS */
DECLARE intKey INT;
DECLARE dteDateInserted DATETIME;
DECLARE vchIdField VARCHAR(200);
DECLARE vchValueField VARCHAR(200);
DECLARE intLastKey INT;
/*TAKE OUT SPECIFIC PLAYLIST ITEMS IF TOO SLOW*/
DECLARE csrMediaToBeAssigned CURSOR FOR
SELECT
`media`.`key` AS `key`,
`media`.`date_inserted` AS `date_inserted`,
`media_detail_types`.`id` AS `id`,
`media_details`.`value` AS `value`
FROM (`media`
LEFT JOIN (`media_detail_types`
JOIN `media_details`
ON ((`media_detail_types`.`key` = `media_details`.`detail_key`)))
ON ((`media_details`.`media_key` = `media`.`key`)))
WHERE ((`media`.`is_assigned` = 0)
AND ((`media_detail_types`.`id` = 'Volume Name')
OR (`media_detail_types`.`id` = 'Drive Id')))
ORDER BY `media`.`key`,`media`.`date_inserted`,`media_detail_types`.`id`;
OPEN csrMediaToBeAssigned;
DROP TEMPORARY TABLE IF EXISTS temp_unassigned_media;
CREATE TEMPORARY TABLE temp_unnassigned_media
(temp_key INT, DateInserted DATETIME, IdField VARCHAR(200), ValueField VARCHAR (200))
ENGINE=MEMORY;
SET intLastKey = 0;
/*--GET FIRST RECORD */
FETCH FROM csrMediaToBeAssigned
INTO intKey, dteDateInserted, vchIdField, vchValueField;
/*--LOOP THROUGH CURSOR */
WHILE intLastKey = 0 DO
/*--DATA SHOULD BE IN DRIVE ID THEN VOLUME NAME */
INSERT INTO temp_unnassigned_media
VALUES (intKey, dteDateInserted, vchValueField, '');
FETCH NEXT FROM csrMediaToBeAssigned
INTO intKey, dteDateInserted, vchIdField, vchValueField;
UPDATE temp_unnassigned_media
SET IdField = vchValueField
WHERE temp_key = temp_key;
FETCH NEXT FROM csrMediaToBeAssigned
INTO intKey, dteDateInserted, vchIdField, vchValueField;
END WHILE;
SELECT *
FROM temp_unnassigned_media
ORDER BY date_inserted;
CLOSE csrMediaToBeAssigned;
/*DEALLOCATE csrMediaToBeAssigned */
/*DROP TABLE #temp_unnassigned_media */
END$$
DELIMITER ;

You never hit a condition where that WHILE loop will exit; you initialize intLastKey variable, but it never changes, so you fetch through the entire resultset. The exception is thrown when you fetch again, after the last record.
The normative pattern is to declare a CONTINUE HANDLER, which MySQL will execute when the NOT FOUND condition is triggered. The handler is normally used to set a variable, which you can then test, so you know when to exit the loop.
In your case, it looks like just adding this line, after your DECLARE CURSOR statement and before the OPEN statement, would be sufficient:
DECLARE CONTINUE HANDLER FOR NOT FOUND SET intLastKey = 1;

Related

Differentiate between no results returned and end of rows for cursor

I'm trying to implement a cursor in MYSQL. Inside that cursor, I have a select statement that might or might not return any rows.
I'm facing a problem when that query does not return any rows.
According to the MYSQL documentation,
NOT FOUND: Shorthand for the class of SQLSTATE values that begin with '02'. This is relevant within the context of cursors and is used to control what happens when a cursor reaches the end of a data set. If no more rows are available, a No Data condition occurs with SQLSTATE value '02000'. You can set up a handler for it or a NOT-FOUND condition to detect this condition. For another example, see Section 13.6.6, “Cursors”. The NOT FOUND condition also occurs for SELECT ... INTO var_list statements that retrieve no rows.
My termination of the cursor is implemented as usual.
DECLARE CONTINUE HANDLER
FOR NOT FOUND SET FINISHED = 1;
The problem is that the termination is triggered because of the query inside the cursor returning 0 rows. Is there a way to differentiate between those two cases so that the cursor continues despite my query returning 0 results?
Thanks in advance.
DROP PROCEDURE IF EXISTS CURSOR_PLACEHOLDER;
DELIMITER $$
CREATE PROCEDURE CURSOR_PLACEHOLDER()
BEGIN
DECLARE FINISHED INTEGER DEFAULT 0;
DECLARE CURRENT_ROW_ID VARCHAR(256);
DEClARE CURRENT_ROW
CURSOR FOR
SELECT ID
FROM A
WHERE ID_P IN (SELECT ID_P
FROM A
GROUP BY ID_P
HAVING COUNT(ID_P) > 1);
DECLARE CONTINUE HANDLER
FOR NOT FOUND SET FINISHED = 1;
OPEN CURRENT_ROW;
GET_ACTION:
LOOP
FETCH CURRENT_ROW INTO CURRENT_ROW_ID;
IF FINISHED = 1 THEN
LEAVE GET_ACTION;
END IF;
SET #CURRENT_P_ID := (SELECT ID_P FROM A WHERE ID = CURRENT_ROW_ID);
SELECT a.ID_U, a.ID_R, a.A, a.FROMDATE, a.TODATE
INTO #ID_U, #ID_R, #A, #FROM_DATE, #TO_DATE
FROM ASSIG a
WHERE a.ID_G = #CURRENT_P_ID; # <- Query that returns 0 rows and terminates the cursor
IF (#ID_U IS NOT NULL) THEN
SET #ASSIG_ID := GENERATE_ID();
INSERT INTO ASSIG
VALUES (#ASSIG_ID,
#G_ID,
#ID_U,
#ID_R,
#A,
#FROM_DATE,
#TO_DATE);
END IF;
UPDATE A
SET ID_P = #G_ID
WHERE ID = CURRENT_ROW_ID;
END LOOP GET_ACTION;
CLOSE CURRENT_ROW;
END
$$
DELIMITER ;

mysql stored procedure slow may need prepared statement

Data comes in via a zip file in a group of csv files which are used to populate holding tables. Some can include new data or updates of existing entries. The holding tables are used for intermediate processing then are used for import to the working tables.
In an effort to speed up the process I have been writing stored procedures to update the working tables. On my laptop one such file with only 730 records is taking about a minute to do its stuff.
I did consider making the 'insert ... on duplicate key update' into a prepared statement inside the SP but some of the other tables have many more fields and I could find no good guide on how to write such a complex one.
Here is my stored procedure:
DROP PROCEDURE IF EXISTS impLuRacodes;
DELIMITER $$
CREATE DEFINER=`root`#`localhost` PROCEDURE impLuRacodes()
BEGIN
DECLARE done INT DEFAULT 0;
DECLARE v_ATID varchar(10);
DECLARE v_Code varchar(10);
DECLARE v_IssType varchar(10);
DECLARE v_Category varchar(10);
DECLARE v_CNumber int(4);
DECLARE v_CDesc varchar(255);
DECLARE v_ColCode varchar(6);
DECLARE v_LLike int(3);
DECLARE v_LifeS int(3);
DECLARE v_PropS int(3);
DECLARE v_BusS int(3);
DECLARE c_1 CURSOR FOR
SELECT `ATID`, `Code`, `IssType`, `Category`, `CNumber`, `CDesc`, `ColCode`, `LLike`, `LifeS`, `PropS`, `BusS`
FROM lu_racodes_temp;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done=1;
OPEN c_1;
REPEAT
FETCH c_1 INTO v_ATID, v_Code, v_IssType, v_Category, v_CNumber,
v_CDesc, v_ColCode, v_LLike, v_LifeS, v_PropS, v_BusS;
INSERT INTO lu_racodes (ATID, `Code`, IssType, Category, CNumber,
CDesc, ColCode, LLike, LifeS, PropS, BusS)
VALUES(v_ATID, v_Code, v_IssType, v_Category, v_CNumber, v_CDesc,
v_ColCode, v_LLike, v_LifeS, v_PropS, v_BusS)
ON DUPLICATE KEY UPDATE
ATID= v_ATID, `Code`= v_Code, IssType= v_IssType, Category= v_Category,
CNumber= v_CNumber, CDesc= v_CDesc, ColCode= v_ColCode, LLike=v_LLike,
LifeS= v_LifeS, PropS= v_PropS, BusS= v_BusS;
UNTIL done END REPEAT;
CLOSE c_1;
END $$
Alternatively, is it possible to put the Select inside the 'Insert ... On Duplicate Key' instead of using a cursor - again something I have had no joy in finding a clear answer to.
Thanks to the guidance I rewrote the code to put the select inside and execution time dropped to 0.156 seconds for a first population. I was concerned about the syntax of the update section with the reference to VALUES as it was a Select statement bringing in the data but changing a couple of rows in the source table did get updated and still with only a 0.235 second time. So here is the code:
DROP PROCEDURE IF EXISTS impLuRacodes;
DELIMITER $$
CREATE DEFINER=`root`#`localhost` PROCEDURE impLuRacodes()
BEGIN
INSERT INTO lu_racodes (`ATID`, `Code`, `IssType`, `Category`, `CNumber`, `CDesc`, `ColCode`, `LLike`, `LifeS`, `PropS`, `BusS`) SELECT `ATID`, `Code`, `IssType`, `Category`, `CNumber`, `CDesc`, `ColCode`, `LLike`, `LifeS`, `PropS`, `BusS` FROM lu_racodes_temp ON DUPLICATE KEY UPDATE `ATID`= VALUES(`ATID`), `Code`= VALUES(`Code`), `IssType`= VALUES(`IssType`), `Category`= VALUES(`Category`), `CNumber`= VALUES(`CNumber`), `CDesc`= VALUES(`CDesc`), `ColCode`= VALUES(`ColCode`), `LLike`= VALUES(`LLike`), `LifeS`= VALUES(`LifeS`), `PropS`= VALUES(`PropS`), `BusS`= VALUES(`BusS`);
END
$$
DELIMITER ;

Assigning the value from a select statement to a vairable in MySQL

I'm fairly new to SQL in general and even more so to MySQL and I've hit a stumbling block. I'm attempting to use a procedure to copy the value of one field to another if the original field is not null, this procedure is then called by triggers whenever the table is updated or has a new row inserted into it. Here is what I have so far:
-- WORK_NOTES_PROCEDURE - This copies the contents of the estimate notes to the work order notes if the original estimate had any notes with it.
DROP PROCEDURE IF EXISTS 'WORK_NOTES_PROCEDURE';
DELIMITER $$
CREATE PROCEDURE WORK_NOTES_PROCEDURE()
BEGIN
DECLARE var_temp VARCHAR(50);
SET var_temp := (SELECT ESTIMATE_NOTES FROM ESTIMATES WHERE ESTIMATES.ESTIMATE_NUMBER = WORK_ORDERS.ESTIMATE_NUMBER);
IF var_temp IS NOT NULL THEN
UPDATE WORK_ORDERS SET WORK_ORDER_NOTES = var_temp WHERE WORK_ORDERS.ESTIMATE NUMBER = ESTIMATES.ESTIMATE_NUMBER;
END IF;
END$$
DELIMITER ;
Absolutely any help would be appreciated, the error I'm getting is a syntax error for the line where I'm assigning a value to var_temp.
try,
SET var_temp = (SELECT ESTIMATE_NOTES
FROM ESTIMATES INNER JOIN WORK_ORDERS
ON ESTIMATES.ESTIMATE_NUMBER = WORK_ORDERS.ESTIMATE_NUMBER
LIMIT 1);

datas are appending everytime on running stored procedure

Am new to Stored procedures.I wrote the stored procedure to copy table from one dtabase to another database.On executing my stored procedures everytime My datas are added in the destination table .My row counts was increasing on every execution.
Please help to resolve the issue.Hope the problem In the loops.
My SP is:
--exec mall
alter procedure mall
as
begin
declare #mallid int
declare #mallname nvarchar(40)
declare #mallstatus nvarchar(40)
declare #malludsuomid nchar(2)
declare #malludsassetcode nvarchar(6)
declare #malludsassettype nvarchar(15)
declare #malludsremarks nvarchar(max)
declare #malludsdwdb int
declare #mallsecterr int
declare #mallassetid int
declare #secterr int
declare #Maxmallid int
declare #mallentityid int
Select #mallentityid = customtable.Bord_TableId From CRM.dbo.Custom_Tables as customtable With (NoLock) Where Upper(Bord_Caption) = Upper('Mall') And Bord_Deleted Is Null
DECLARE cur_address CURSOR FOR
SELECT
udsasset.Asset_ID,udsasset.Asset_Name,udsasset.Asset_Status,udsasset.UOM_ID, udsasset.Asset_Code,udsasset.Asset_Type,udsasset.Remarks,udsasset.DW_Key_Source_DB --,crmterr.TPro_SecTerr
from
CMA_UDS.dbo.Dim_Asset as udsasset
OPEN cur_address
FETCH NEXT FROM cur_address INTO #mallid,#mallname,#mallstatus,#malludsuomid,#malludsassetcode,#malludsassettype,#malludsremarks,#malludsdwdb --,#mallsecterr
WHILE ##FETCH_STATUS = 0
BEGIN
if not exists (select crmmall.mall_MallID from CRM.dbo.Mall as crmmall where crmmall.mall_MallID = #mallid)
begin
exec #Maxmallid = CRM.dbo.crm_next_id #Table_Id=#mallentityid
insert into
CRM.dbo.Mall
(mall_MallID,mall_Name,mall_Status,mall_uds_UOMID,mall_uds_asset_code,mall_uds_asset_type,
mall_uds_remarks,mall_uds_dw_db,mall_CreatedBy,mall_CreatedDate,mall_Secterr,mall_AMOSUploaded,mall_asset_id)
values(#Maxmallid,#mallname,#mallstatus,#malludsuomid,#malludsassetcode,#malludsassettype,#malludsremarks,#malludsdwdb,1,GETDATE(),
#mallsecterr,GETDATE(),#mallid)
end
else
begin
update
CRM.dbo.Mall
set
mall_asset_id=#mallid,mall_Name = #mallname,mall_Status=#mallstatus,mall_uds_UOMID =#malludsuomid,mall_uds_asset_code=#malludsassetcode,
mall_uds_asset_type=#malludsassettype,mall_uds_remarks=#malludsremarks,mall_uds_dw_db=#malludsdwdb,mall_UpdatedBy=1,
mall_UpdatedDate=GETDATE(),mall_Secterr=#mallsecterr,mall_AMOSUploaded=GETDATE()
where
mall_MallID=#mallid
end
FETCH NEXT FROM cur_address INTO #mallid,#mallname,#mallstatus,#malludsuomid,#malludsassetcode,#malludsassettype,#malludsremarks,#malludsdwdb--,#mallsecterr
end
CLOSE cur_address
DEALLOCATE cur_address
End
Why are you inserting crm_next_id as the value in mall_MallID, but using that same id to compare with #mallid to see if the record is already inserted? For example, if you have id 5, and you insert a new record with id 150, it's not going to see that the record is already inserted when you run the SP again. Next run, it will add record with id 151, then 152, and so forth forever. You shouldn't use the same field as both an auto-increment identity and a foreign key reference at the same time...
You either need to use the same #mallid when you insert the new records so they match, or after you generate a new id and insert into the table, update the original record CMA_UDS.dbo.Dim_Asset to have Asset_ID = #mallid so they are linked up properly. Which method you use depends on the meanings of those id's and what constraints you have in your particular application.

How to insert multiple rows based on a quantity value in one row?

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.