I have created a stored procedure, which should delte data from some tables.
On my productive db I have tables which I have not in my local db (different schema names).
Is there an elegant way to query the data tables existence to avoid exceptions? Maybe declare an error handler which catches the specific error and should continue.
CREATE PROCEDURE `deletion`(IN s_id varchar(255))
BEGIN
DECLARE has_error BOOL DEFAULT FALSE;
/* for all other exception there should be a rollback, if table does not exist
then continue*/
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION SET has_error = TRUE;
START TRANSACTION;
DELETE FROM a WHERE id = s_id;
DELETE FROM b WHERE id = s_id;
DELETE FROM c WHERE id = s_id;
DELETE FROM d WHERE id = s_id;
IF has_error THEN
ROLLBACK;
ELSE
COMMIT;
END IF;
END
UPDATE
I tried this, but this is not working. even if the table exists the entry is not deleted:
IF EXISTS (SELECT 1 from information_schema.tables
WHERE table_schema = 'a' ) then
DELETE FROM at WHERE id = s_id;
END IF;
thanks in advance!
Related
So for some reason, I just got this error message: `Deadlock found when trying to get lock; try restarting transaction, but I'm not quite sure why since I've never gotten this before, and it used to work fine.
Stored procedure
CREATE PROCEDURE `AddTestResultRequirement`(varTestResultId INT, RequirementNameId INT)
BEGIN
declare reqId INT;
declare linkId INT;
set linkId = -1;
IF NOT EXISTS(SELECT * FROM testcaserequirement where nameId = RequirementNameId) THEN
insert into testcaserequirement(id, nameId) values (NULL, RequirementNameId);
set reqId = last_insert_id();
ELSE
set reqId = GetRequirementNameId(RequirementNameId);
END IF;
IF NOT EXISTS(select * from testresultrequirementlink where requirementId = reqId and testresultId = varTestResultId) THEN
insert into testresultrequirementlink(id, requirementId, testresultId) values(NULL, reqId, varTestResultId);
set linkId = last_insert_id();
else
set linkId = GetRequirementTestResultLinkId(varTestResultId, reqId);
end if;
select linkId;
END
Function
CREATE FUNCTION `GetRequirementTestResultLinkId`(varTestResultId INT, RequirementId INT) RETURNS int(11)
BEGIN
RETURN (SELECT id from testresultrequirementlink where testresultid = varTestResultId and requirementId = RequirementId LIMIT 1);
END
Anyone who can see where this deadlock would appear?
I do know that deadlock means that a deadlock is when two transactions are trying to lock two locks at opposite orders, but I still can't figure out why I got it.
When im running the follwing procedure
call clean_email('johnny#c.com');
I'm getting the error
Illegal mix of collations (utf8mb4_general_ci,COERCIBLE) and (latin1_swedish_ci,IMPLICIT) for operation '='
I tried changing collations for table applications but still, the error doesn't resolve.
What can I do to resolve this? Do I need to change the procedure?
CREATE DEFINER=`root`#`%` PROCEDURE `clean_email`(_email_ varchar(128))
this_proc : BEGIN
declare _appId bigint unsigned;
/*TEMP*/ declare _resumeId bigint unsigned;
DECLARE done INT DEFAULT FALSE;
DECLARE cursor_i CURSOR FOR
/*TEMP*/ (SELECT appId, null as "resumeId" FROM _appendix WHERE lower(`value`) = lower(_email_) AND lower(`key`)="applicantemail")
-- LATER -- andOr /*TEMP*/ UNION
-- LATER -- (SELECT id
-- LATER -- andOr /*TEMP*/, resumeId
-- LATER -- FROM cbax_application WHERE lower(`name`) = lower(_email_))
;
/*TEMP*/ DECLARE cursor_old CURSOR FOR select id from applications where
/*TEMP*/ CASE WHEN formData is null OR trim(formData)="" THEN false ELSE
/*TEMP*/ lower(JSON_UNQUOTE(JSON_EXTRACT(CONVERT(formData using utf8mb4),'$.candidateParams.ApplicantEmail'))) = lower(_email_) END;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
DECLARE EXIT HANDLER FOR SQLEXCEPTION, SQLWARNING
BEGIN
GET DIAGNOSTICS CONDITION 1 #p1 = RETURNED_SQLSTATE, #p2 = MESSAGE_TEXT;
ROLLBACK;
select "Failed" as "status", #p1 as "sql_state", #p2 as "message_text";
END;
-- ---------------------
-- Input Validations --
IF(_email_ IS NULL or instr(_email_,"#") < 2) THEN
select "Failed" as "status", 0 as "sql_state", "Email id is required." as "message_text";
LEAVE this_proc;
END IF;
-- END Input Validations --
-- ------------------------
START TRANSACTION;
OPEN cursor_i;
read_loop: LOOP
FETCH cursor_i INTO _appId
/*TEMP*/, _resumeId
;
IF done THEN
LEAVE read_loop;
END IF;
DELETE FROM cbax_application WHERE id = _appId;
DELETE FROM cbax_application_blob WHERE appId = _appId;
DELETE FROM job_info WHERE appId = _appId;
DELETE FROM `resume` WHERE id = _resumeId;
-- LATER -- DELETE FROM _values WHERE appId = _appId;
-- LATER -- DELETE FROM _letter WHERE appId = _appId;
-- LATER -- DELETE FROM _history WHERE appId = _appId;
/*TEMP*/ DELETE FROM _appendix WHERE appId = _appId;
END LOOP;
CLOSE cursor_i;
/*START TEMP*/
SET done = FALSE;
OPEN cursor_old;
old_loop: LOOP
FETCH cursor_old INTO _appId;
IF done THEN
LEAVE old_loop;
END IF;
DELETE FROM applications WHERE id = _appId;
DELETE FROM _values WHERE appId = _appId;
DELETE FROM_letter WHERE appId = _appId;
DELETE FROM _history WHERE appId = _appId;
END LOOP;
CLOSE cursor_old;
/*END TEMP*/
select "Success" as "status";
COMMIT;
END
Do SHOW CREATE PROCEDURE clean_email; but look at the extra columns other than the body of the code. One of them says the CHARACTER SET used when creating the proc. It probably says latin1.
To change that,
SET NAMES utf8mb4; -- assuming this is desired
DROP PROCEDURE clean_emaill;
and recreate the procedure
SQL is picky when it comes to the interaction of charset and collation. This is also in MySQL Collation: latin1_swedish_ci Vs utf8_general_ci .
Change the CHARSET to utf8_swedish_ci : DEFAULT CHARACTER SET = utf8_swedish_ci
When using 'incompatible' charsets and collations one tries to compare 'apples with pears' :
A character set is a set of symbols and encodings. A collation is a
set of rules for comparing characters in a character set.
source : https://dev.mysql.com/doc/refman/8.0/en/charset-general.html
This is the database vehicle table's trigger
DROP TRIGGER IF EXISTS InsertVehTrig;
DELIMITER $$
CREATE TRIGGER InsertVehTrig AFTER INSERT
ON Vehicle FOR EACH ROW
SWL_return:
BEGIN
DECLARE Cph CHAR(50);
DECLARE DevID CHAR(12);
DECLARE VehID BIGINT;
DECLARE TmpID BIGINT;
DECLARE DevCount INT;
SET Cph = rtrim(ltrim(NEW.cph));
SET VehID = NEW.ID;
SET DevID = NEW.DevID;
if(VehID is null) then
select count(id) into #DevCount from vehicle where (cph=#Cph) or (DevID=#DevID);
-- 条件:当前的车牌号 或 设备ID
end if;
if (DevCount > 1) then -- 如果记录数,超过1,则认为有重复
-- Rollback not supported in trigger
SET #SWV_Null_Var = 0;
Leave SWL_return;
else
if (DevCount = 1) then
select ID INTO #TmpID from Vehicle where (Vehicle.cph = #Cph) or (Vehicle.DevID = #DevID);
if (TmpID != VehID) then -- --如果增加的车牌号码与数据库中的在相同的,则不允许增加
-- Rollback not supported in trigger
Leave SWL_return;
SET #SWV_Null_Var = 0;
end if;
end if;
end if;
update vehicle set cph = #Cph where ID = #VehID;
END;
Right now i m trying to insert new data row in the vehicle table, but error with this
ERROR 1442: Can't update table 'vehicle' in stored function/trigger because it is already used by statement which invoked this stored function/trigger.
SQL Statement:
INSERT INTO `gis_server`.`vehicle` (`TrackerNum`, `cph`, `DevID`, `DevType`) VALUES ('1', 'NR09B00555', 'NR09B00555', '2')
those database are designed by 3 party company,
How do i insert data to vehicle table?
You can achieve this by making two changes to your approach:
Firstly, use a BEFORE trigger rather than an AFTER
Secondly, rather than updating the same table, update the NEW table, setting the column value to the new value before the NEW table hits the table targeted by the INSERT.
For example, replace your UPDATE line with the following:
UPDATE NEW SET cph = Cph;
MySQL doesn't allow you to edit the data in the table on which a trigger is created in that trigger, but you can edit the NEW table to modify the values going into that table.
I have this function in my DB
CREATE FUNCTION BookBed (pPaciente varchar(255),
pHospital bigint(20)) RETURNS BOOLEAN
BEGIN
DECLARE NumLeitosDisponiveis INT;
DECLARE vReservaOK BOOLEAN;
DECLARE dt TIMESTAMP;
SET dt = (Select now());
SET NumLeitosDisponiveis = (SELECT AVAILABLEBEDCOUNT FROM HOSPITAL WHERE ID = pHospital);
IF((SELECT NumLeitosDisponiveis) > 0) THEN
BEGIN
START TRANSACTION;
INSERT INTO RESERVATION(PERSON, HOSPITAL, DATE) VALUES (pPaciente, pHospital, dt);
UPDATE HOSPITAL
SET AVAILABLEBEDCOUNT = AVAILABLEBEDCOUNT - 1
WHERE ID = pHospital;
SET vReservaOk = true;
commit;
END;
ELSE
SET vReservaOk = false;
END IF;
RETURN vReservaOK;
END;
In the if part of my if-else statement, I would like to perform all the operations in a atomic way.
I wanted to use the START TRANSACTION command, but they are disallowed in functions and I couldn't find any other command to perform it.
Are functions atomic by default?
If not, is there any way I can implement it?
Thanks,
Oscar
EDIT: And if I have to use a function, is it possible to have transactions?
Use a stored procedure with an output parameter for returning the operation status.
DELIMITER //
CREATE PROCEDURE BookBed (
pPaciente varchar(255),
pHospital bigint(20),
OUT oReservaOK boolean)
BEGIN
DECLARE NumLeitosDisponiveis INT;
DECLARE dt TIMESTAMP;
SET dt = (Select now());
SET NumLeitosDisponiveis =
SELECT AVAILABLEBEDCOUNT FROM HOSPITAL WHERE ID = pHospital;
IF((SELECT NumLeitosDisponiveis) > 0) THEN
BEGIN
START TRANSACTION;
INSERT INTO RESERVATION(PERSON, HOSPITAL, DATE)
VALUES (pPaciente, pHospital, dt);
UPDATE HOSPITAL
SET AVAILABLEBEDCOUNT = AVAILABLEBEDCOUNT - 1
WHERE ID = pHospital;
SET oReservaOk = true;
commit;
END;
ELSE
SET oReservaOk = false;
END IF;
END//
If you are having trouble calling the stored procedure using Hibernate, then move the transaction logic to Hibernate. That is, start, commit or rollback the transaction using Hibernate constructs.
EDIT
Transactions require using the InnoDB engine as #bobobobo notes on the comments.
I have a simple insert select which insert _TABLE_B_ data in _TABLE_A_ new row
INSERT INTO _TABLE_A_(_USERNAME_,_ID_)
SELECT _USERNAME_,_ID_
FROM _TABLE_B_
I want to insert a row in a table named _TABLE_C_ each time i insert a row in _TABLE_A_ and add the current inserted _TABLE_C_ id in _TABLE_A_.
i'll try to explain it in an other way :
INSERT INTO _TABLE_A_(_USERNAME_,_ID_,_FOREIGN_ID_)
SELECT B._USERNAME_,B._ID_,C._FOREIGN_ID_
FROM _TABLE_B_ AS B
LEFT JOIN _TABLE_C_ AS C
#Insert a row in _TABLE_C_ to retrieve _FOREIGN_ID_...
I'm searching for a single minimal query which have the INSERT SELECT statement like mine because insert select can loop and i have to loop.
FYI :
I'm in a stored procedure.
I also use prepared statements with dynamic data, and cursors is not suitable for dynamic data select...
I would do all the INSERTs in _TABLE_C_ first and then join it in the INSERT _TABLE_A_ to get the appropriate foreign keys.
If that is not possible, I would use a cursor.
Cursor on _TABLE_B_ & Fetch
INSERT _TABLE_C_
INSERT _TABLE_A_ with Foreign_Id = SCOPE_IDENTITY()
Fetch next
I found a solution.
create a temporary table and add dynamic select statement which retrieve the primary keys (id)
declare a cursor and select this temporary table id ( variables doesn't work but temporary tables do )
execute statement to create temporary table
open the cursor and iterate the inserts
EXAMPLE
BEGIN
DECLARE isDone INT DEFAULT 0;
DECLARE fetchedmemberWhoWillReceiveMailId int;
DECLARE cur1 CURSOR FOR SELECT id FROM memberWhoWillReceiveMail;
DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET isDone = 1;
SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;
SET #sexe = VAR_sexe;
SET #event = VAR_eventId;
SET #subject = VAR_subject;
SET #body = VAR_body;
SET #to = VAR_to;
SET #from = VAR_from;
SET #region = VAR_region;
SET #departement = VAR_departement;
SET #age = VAR_age;
SET #baseSqlStatement =' CREATE TEMPORARY TABLE memberWhoWillReceiveMail SELECT e.id FROM TABLE_A as e LEFT JOIN TABLE_B AS a on a.member_id = e.id';
SET #whereSqlStatement= 'WHERE e.is_visible = 1 AND e.member_group_id IN (10,11) ';
IF (#region!='') THEN
SET #whereSqlStatement= CONCAT(#whereSqlStatement,' AND region=',#region);
END IF;
IF (#event !=null ) THEN
SET #whereSqlStatement= CONCAT(#whereSqlStatement,' AND m.event_id !=' ,#eventId);
END IF;
IF (#sexe!=null ) THEN
SET #whereSqlStatement= CONCAT(#whereSqlStatement,' AND e.sexe=',#sexe);
END IF;
SET #baseSqlStatement = CONCAT(#baseSqlStatement,#whereSqlStatement);
START TRANSACTION;
PREPARE stmt1 FROM #baseSqlStatement;
EXECUTE stmt1;
DEALLOCATE PREPARE stmt1;
OPEN cur1;
FETCH cur1 INTO fetchedmemberWhoWillReceiveMailId;
WHILE NOT isDone DO
INSERT INTO conversation(created_at,updated_at)VALUES(now(),now());
INSERT INTO message(created_at,updated_at,from, to, uniqueID) VALUES(now(),now(),#from,fetchedmemberWhoWillReceiveMailId,LAST_INSERT_ID() );
FETCH cur1 INTO fetchedmemberWhoWillReceiveMailId; END WHILE; CLOSE cur1;
COMMIT;
DROP TEMPORARY TABLE IF EXISTS memberWhoWillReceiveMail;
END