Creating / Using a stored procedure with a Laravel migration - mysql

This one is a little squirrely!
I have built a stored procedure that works fine. However, when I put it into a Laravel (eloquent) migration, the migration builds the stored procedure but when it is called it gives me an error 1292 (Truncated incorrect DOUBLE value). From mysql workbench, I then right click on the stored procedure built by the migration, copy the create statement, delete the stored procedure and rebuild it from its own create statement.... AND..... EVERYTHING WORKS FINE!
I am open to any and all guesses. I obviously want this stored procedure to be part of my migrations and not have it be something that needs to be built manually. Thanks in advance:
DELIMITER $$
CREATE PROCEDURE sp_cancelTasksOnApplicationCancel(IN loanAppId INT(20))
BEGIN
SELECT loanAppId;
#SET #taskids := 0;
update tasks set task_status = 26, task_status_note = 'Application Cancelled or Denied'
where loan_app_id = loanAppId AND task_status IN (22, 23, 24) AND ( SELECT #taskids := CONCAT_WS(',', id, #taskids) );
#SELECT #taskids;
SET #cleanids = SUBSTRING(#taskids, 1, CHAR_LENGTH(#taskids) -1);
#SELECT #cleanids;
SET #pos = 0;
SET #len = 0;
WHILE LOCATE(',', #cleanids, #pos+1) > 0 DO
SET #len = LOCATE(',', #cleanids,#pos+1) - #pos;
SET #id = SUBSTRING(#cleanids, #pos, #len);
#select (#id);
IF (#id <> 0) THEN
insert into status_historys
(loan_app_id, `type`, type_recid, type_keyfield,type_status_field, type_status_date_field, type_status_note_field, type_status_userid_field, `status`, status_date, status_note, status_userid, created_at, updated_at)
values(loanAppId, 'tasks', #id, 'id', 'task status', 'tasks status datetime', 'tasks status note', 'tasks status userid', 26, now(), 'Application Cancelled or Denied', 1, now(), now());
END IF;
SET #pos = LOCATE(',', #taskids, #pos+#len) + 1;
END WHILE;
END $$
DELIMITER ;
############## UPDATE
The problem lies in the following two lines. Even from the command line it gives a warning about the incorrect double value.
SET #taskids := '0';
update tasks set task_status = 22, task_status_note = 'Just Testing'
where loan_app_id = loanAppId AND task_status IN (22, 23, 24) AND ( SELECT
#taskids := CONCAT_WS(',', cast(id as char), #taskids) );

OK - The main problem was the select keyword in the update where clause. I believe it kept re-declaring the #taskids user variable and mysql gave warnings if the stored procedure was built from the workbench, but failed if the stored procedure was built by a migration.
So the correct code was
#snip#
#Note string '0' rather than 0
SET #taskids := '0';
#note casting of id and removing the select keyword before the first #taskids
update tasks set task_status = 22, task_status_note = 'Just Testing'
where loan_app_id = loanAppId AND task_status IN (22, 23, 24) AND (
#taskids := CONCAT_WS(',', cast(id as char), #taskids) );
#snip#

Related

How to dynamically add value in a mySQL procedure?

I have the following procedure:
DROP PROCEDURE IF EXISTS insertCapacityIntoADay;
DELIMITER //
CREATE PROCEDURE insertCapacityIntoADay(startTime TIME, endTime TIME, capacity INT)
BEGIN
INSERT INTO Capacities VALUES(UUID(), startTime, endTime, capacity;
END //
DELIMITER ;
So I can use it like this:
CALL insertCapacityIntoADay('00:00:00', '08:00:00', 120);
My question is, how can I define my procedure, so that when I call it with multiple startTime, endTime, capacity, it adds the value multiple times? i.e. if I do:
CALL insertCapacityIntoADay('00:00:00', '08:00:00', 120, '08:00:00', '20:00:00', 200, '20:00:00', '24:00:00', 150);
it should be equivalent to:
CALL insertCapacityIntoADay('00:00:00', '08:00:00', 120);
CALL insertCapacityIntoADay('08:00:00', '20:00:00', 200);
CALL insertCapacityIntoADay('20:00:00', '24:00:00', 150);
You can emulate array parameters with a CSV string, kind of
CREATE PROCEDURE insertCapacityIntoADay(startTimes varchar(1000), endTimes varchar(1000), capacity varchar(1000))
...
CALL insertCapacityIntoADay('00:00:00, 08:00:00, 20:00:00', '08:00:00, 20:00:00, 24:00:00', '120, 200, 150')
In the proc create a loop which parses the parameters, and issues INSERT accordingly.
I worked out a solution:
DROP PROCEDURE IF EXISTS insertCapacityIntoADay;
DELIMITER //
CREATE PROCEDURE insertCapacityIntoADay(capacities TEXT)
BEGIN
DECLARE startTime VARCHAR(90);
DECLARE endTime VARCHAR(90);
DECLARE capacity VARCHAR(90);
DECLARE maxIterations INT;
DECLARE i INT;
SET maxIterations = 3;
SET i = 1;
-- use a loop to insert values 3 times
loop_label: LOOP
IF i > maxIterations THEN
LEAVE loop_label;
END IF;
-- separate values using comma
SET startTime = (SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(capacities, ",", i), ',', -1));
SET endTime = (SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(capacities, ",", i + 1), ',', -1));
SET capacity = (SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(capacities, ",", i + 2), ',', -1));
INSERT INTO Capacities VALUES(UUID(), startTime, endTime, capacity);
SET i = i + 3;
ITERATE loop_label;
END LOOP loop_label;
END//
DELIMITER ;
SET #capacity1= '00:00:00, 08:00:00, 80, 08:00:00, 15:00:00, 200, 15:00:00, 20:00:00, 150';
CALL insertCapacityIntoADay(#capacity1);

Transaction error in stored procedure that is not using BEGIN or END TRANSACTION

I have a stored procedure "let's call it MY_NEW_SP" in which I'm not using BEGIN TRY / BEGIN CATCH. but, when I'm excecuting this SP (MY_NEW_SP), I get the following error:
Msg 266, Level 16, State 2, Procedure <MY_NEW_SP>, Line 132
Transaction count after EXECUTE indicates a mismatching number of BEGIN and COMMIT statements. Previous count = 0, current count = 1.
This new stored procedure makes a big select basically, no transactions are made "in the sense of make DML operations on tables (INSERT, DELETE, UPDATE)", but in temp tables "i.e. #tmp".
I'm thinking this transaction error is due I'm using SET XACT_ABORT ON; in other stored procedures, but, I'm not sure.
I follow what it is said here: C. Using TRY...CATCH with XACT_STATE
The basic structure of the stored procedure that uses SET XACT_ABORT ON; is as follows:
IF NOT EXISTS (SELECT * FROM sysobjects WHERE TYPE = 'P' AND NAME = 'PROCEP_NEW_SP' )
BEGIN
EXEC dbo.sp_executesql #statement = N'CREATE PROCEDURE PROCEP_NEW_SP AS'
END
GO
ALTER PROCEDURE PROCEP_NEW_SP
(
#ID_TABLE INT
)
AS
BEGIN
DECLARE #TBL_CONSECUTIVE TABLE ( LOG_CONSECUTIVE INT );
SET XACT_ABORT ON;
BEGIN TRANSACTION
BEGIN TRY
IF ISNULL(#ID_TABLE, -1) = -1
BEGIN
SET #ID_TABLE = 1;
DELETE FROM #TBL_CONSECUTIVE;
INSERT INTO T_BH_LOG_TABLE (ASO_NCODE, CHA_NCODE, TSO_NCODE,
MSO_DACTION_DATE, MSO_CRESULT, MSO_CCAUSE_FAILURE)
OUTPUT INSERTED.MSO_NCODE INTO #TBL_CONSECUTIVE
SELECT #ASO_NCODE, ISNULL(#CHA_NCODE, 1), ISNULL(#TSO_NCODE, 1),
GETDATE() AS MSO_DACTION_DATE, #CST_FAIL_OR_SUC, #CST_GENERIC_MSG;
IF (XACT_STATE()) = 1
BEGIN
COMMIT TRANSACTION;
END
SELECT NULL Id_table, 'Failed' Result_process, 'Parameter (ID_TABLE) is required.' Result_process_message;
RETURN;
END
-- Operation:
UPDATE MY_TABLE
SET NAME = 'SAMPLE'
WHERE ID_TABLE = #ID_TABLE;
IF (XACT_STATE()) = 1
BEGIN
COMMIT TRANSACTION;
END
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION;
INSERT INTO T_BH_LOG_TABLE (ASO_NCODE, CHA_NCODE, TSO_NCODE,
MSO_DACTION_DATE, MSO_CRESULT, MSO_CCAUSE_FAILURE)
OUTPUT INSERTED.MSO_NCODE INTO #TBL_CONSECUTIVE
SELECT 1 AS ASO_NCODE, 1, 1 As TSO_NCODE,
GETDATE() AS MSO_DACTION_DATE, #CST_FAIL_OR_SUC, #CST_GENERIC_MSG;
SELECT NULL Id_table, 'Failed' Result_process, 'Internal error. See log # (' + CAST(L.LOG_CONSECUTIVE AS NVARCHAR) + ') for more details.' Result_process_message;
FROM #TBL_CONSECUTIVE L;
RETURN;
END CATCH
END;
I really don't know if by using SET XACT_ABORT ON; is causing this kind of error.
Anyone can point me in the right direction for solve this issue?

MySQL migrate data from one table to another

I'm attempting to move data from one table to another, as part of a restructure to the database architecture. I'm using PHPMyAdmin and MySQL to do so.
The SQL is meant to, for each emergency_contacts.id, move e_c.id and e_c.activity_id to activities_emergency_contacts, where the pair will form a composite key to link a contact with an activity.
The following SQL returns an error:
CREATE PROCEDURE dowhile()
BEGIN
SET #cid = (SELECT MIN(`id`) FROM `emergency_contacts`);
SET #aid = (SELECT `activity_id` FROM `emergency_contacts` WHERE `id` = #cid);
WHILE #cid IS NOT NULL DO
INSERT INTO activities_emergency_contacts (activity_id, contact_id)
VALUES (#aid, #cid);
SET #cid = (SELECT MIN(id) FROM emergency_contacts WHERE #cid < id);
SET #aid = (SELECT activity_id FROM emergency_contacts WHERE id = #cid);
END WHILE;
END;
CALL dowhile();
SQL query:
CREATE PROCEDURE dowhile() BEGIN SET #cid = (SELECT MIN(id) FROM
emergency_contacts)
MySQL said:
#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 '' at line 3
I have searched the MySQL specific documentation to try and find any issues with my SET, WHILE, BEGIN/END, INSERT, CREATE - just about every line. I'm unsure how to proceed. Any advice would be greatly appreciated.
EDIT: I missed a closing bracket inside the WHILE on SELECT MIN (id), however I've fixed this and am still getting the exact same issue.
EDIT: The issue was that I was not using DELIMITER. Correct SQL:
DELIMITER $$
CREATE PROCEDURE dowhile()
BEGIN
SET #cid = (SELECT MIN(`id`) FROM `emergency_contacts`);
SET #aid = (SELECT `activity_id` FROM `emergency_contacts` WHERE `id` = #cid);
WHILE #cid IS NOT NULL DO
INSERT INTO activities_emergency_contacts (activity_id, contact_id) VALUES (#aid, #cid);
SET #cid = (SELECT MIN(id) FROM emergency_contacts WHERE #cid < id);
SET #aid = (SELECT activity_id FROM emergency_contacts WHERE id = #cid);
END WHILE;
END$$
DELIMITER ;
CALL dowhile();

Mysql : Get diagnonstic condition returns null

I have written following mysql stored procedure.
CREATE DEFINER=`clocery`#`%` PROCEDURE `ADMIN_ADD_NEW_USER`(IN `IN_FIRST_NAME` varchar(50),IN `IN_LAST_NAME` varchar(50),IN `IN_USER_NAME` varchar(50), IN `IN_EMAIL` varchar(50),IN `IN_PASSWORD` varchar(80),IN `IN_ROLE` varchar(20),IN `IN_CREATED_BY` INT,OUT `OUT_STATUS` char(1),OUT `OUT_MESSAGE` varchar(200))
BEGIN
DECLARE logged_in_user_role_id INT;
DECLARE to_be_created_user_role_id INT;
DECLARE user_who_created_logged_in_user INT;
DECLARE matching_row_count_user_name INT;
DECLARE matching_row_count_user_e_mail INT;
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
ROLLBACK;
GET DIAGNOSTICS CONDITION 1 #sqlstate = RETURNED_SQLSTATE, #errno = MYSQL_ERRNO, #text = MESSAGE_TEXT;
CALL UTIL_LOG_ERROR( 'ADMIN_ADD_NEW_USER', #sqlState, #errno, #text);
SELECT 'N', UTIL_GET_ERROR_MESSAGE( 'some-error' )
INTO OUT_STATUS, OUT_MESSAGE;
END;
/**
* If passed user_id is NULL.
*/
IF IN_CREATED_BY = NULL THEN
SELECT 'N', UTIL_GET_ERROR_MESSAGE( 'some-error' )
INTO OUT_STATUS, OUT_MESSAGE;
CALL LOG_ERROR( 'ADD_NEW_USER', UTIL_GET_ERROR_MESSAGE( 'in-created-by-null' ));
ELSE
SELECT U.ROLE_ID, U.CREATED_BY INTO logged_in_user_role_id, user_who_created_logged_in_user FROM USERS U WHERE U.ID = IN_CREATED_BY;
SELECT R.ID INTO to_be_created_user_role_id FROM ROLES R WHERE R.ROLE = IN_ROLE;
/**
* If user has provided invalid role.
*/
IF to_be_created_user_role_id = NULL THEN
SELECT 'N', UTIL_GET_ERROR_MESSAGE( 'in-valid-role-provided' ) INTO OUT_STATUS, OUT_MESSAGE;
ELSE
/**
* Lets' see if logged in user has more authority than the one we are trying to create.
*/
IF to_be_created_user_role_id <= logged_in_user_role_id THEN
SELECT 'N', UTIL_GET_ERROR_MESSAGE( 'permission-denied' ) INTO OUT_STATUS, OUT_MESSAGE;
CALL CREATE_NOTIFICATION_FOR_USER( user_who_created_logged_in_user, CONCAT( GET_USER_IDENTIFICATION( IN_CREATED_BY), 'tried to create an account of higher authority.'));
ELSE
/**
* Lets' check if user name already present in the database.
*/
SELECT COUNT(*) INTO matching_row_count_user_name FROM USERS U WHERE U.USER_NAME = IN_USER_NAME;
SELECT COUNT(*) INTO matching_row_count_user_e_mail FROM USERS U WHERE U.EMAIL = IN_EMAIL;
IF matching_row_count_user_name > 0 THEN
SELECT 'N', UTIL_GET_ERROR_MESSAGE('username-exists') INTO OUT_STATUS, OUT_MESSAGE;
ELSEIF matching_row_count_user_e_mail > 0 THEN
SELECT 'N', UTIL_GET_ERROR_MESSAGE( 'email-exists' ) INTO OUT_STATUS, OUT_MESSAGE;
ELSE
/**
* Now, we are good to insert.
*/
START TRANSACTION;
/**
* Inserting into credentials first.
*/
INSERT INTO CREDENTIALS( PASSWORD, LOCKED ) VALUES ( IN_PASSWORD, 'N' );
INSERT INTO USERS( F_NAME, L_NAME, USER_NAME, EMAIL, ROLE_ID, ACTIVE, DELETED, CREATED_BY, CREATED_DATE, CREDENTIAL_ID, LAST_ACTOR_ID )
VALUES( IN_FIRST_NAME, IN_LAST_NAME, IN_USER_NAME, IN_EMAIL, to_be_created_user_role_id, 'Y', 'N', IN_CREATED_BY, NOW(), LAST_INSERT_ID(), IN_CREATED_BY );
INSERT INTO HISTORY_USER_EVENTS( USER_ID, TEXT, TYPE ) VALUES( LAST_INSERT_ID(),
CONCAT( UTIL_GET_USER_IDENTIFICATION( LAST_INSERT_ID() ), '''s account was created by ', UTIL_GET_USER_IDENTIFICATION( IN_CREATED_BY ), '.'),
UTIL_GET_EVENT_ID_FOR_ACCOUNT_ACTION( 'Created' ));
INSERT INTO HISTORY_ADMIN_EVENTS( ADMIN_ID, TEXT, TYPE ) VALUES( IN_CREATED_BY,
CONCAT( UTIL_GET_USER_IDENTIFICATION( IN_CREATED_BY ), ' created account of ', UTIL_GET_USER_IDENTIFICATION( LAST_INSERT_ID()), ' with role ', IN_ROLE, '.' ),
UTIL_GET_EVENT_ID_FOR_USER_ADDITION( IN_ROLE ));
SELECT 'Y', 'User created.' INTO OUT_STATUS, OUT_MESSAGE;
COMMIT;
END IF;
END IF;
END IF;
END IF;
END
As you can see, I have created a handler that executes in case of any SQL exception. The procedure should produce a lot of run time errors. However,
I am not able to retrieve any of the RETURNED_SQL_STATE or MYSQL_ERROR_CODE OR MESSAGE_TEXT ? They all get value null.
Is it because I am calling rollback initially ? And it somehow messes up all the relevant information ?
Here is the example of rollback code block with ability to log the error to some other table without loosing the exception details in MySQL and throwing the error again after logging it. You should get diagnostics BEFORE the ROLLBACK.
# CREATE PROCEDURE AND OTHER DECLARE STATEMENTS HERE
# ....
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
GET DIAGNOSTICS CONDITION 1 #sqlstate = RETURNED_SQLSTATE, #errno = MYSQL_ERRNO, #text = MESSAGE_TEXT;
ROLLBACK;
SET #full_error = CONCAT('ERR:', #errno, '(', #sqlstate, '):', #text);
CALL sp_logaction(#full_error); # Some logging procedure
RESIGNAL;
END;
# PROCEDURE BODY WITH START TRANSACTION & COMMIT HERE
# .....
Seems you miss to add DELIMITER in your script.
DELIMITER |
you code .. .. ..
DELIMITER;
Thanks

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);