I am trying to use a transaction within a MySQL Stored Procedure.
Specifically, update a user table with amended data from a temporary record. from another table.
then once transferred, delete the temporary record.
I have created the code below, which when executed returns the string "transaction has succeeded".
However, nothing is actually updated and the temporary record is Not deleted.
Both SQL statements, when executed separately work Just fine, the first one does the update, the second does the delete.
Can anyone enlighten me as to what may be wrong?
BEGIN
-- set a default response
DECLARE response varchar(48) DEFAULT "the transaction has failed.";
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
ROLLBACK;
-- set vars
SET response = "the transaction has failed, you may have already updated the account.";
select response;
END;
START TRANSACTION;
-- we are inserting data, using information from another table
update user, updateUserNamesAndNumbers
SET user.firstName = updateUserNamesAndNumbers.firstName,
user.lastName = updateUserNamesAndNumbers.lastName,
user.landline = updateUserNamesAndNumbers.landline,
user.mobile = updateUserNamesAndNumbers.mobile
WHERE
updateUserNamesAndNumbers.uuid = transferCode
AND
updateUserNamesAndNumbers.userId= user.user_id
;
-- finally delete the original tuple
DELETE from updateUserNamesAndNumbers
where uuid= transferCode ;
SET response="The transaction has succeeded";
COMMIT;
SELECT response;
END
Change the implicit join to an explicit join
update user join updateUserNamesAndNumbers on updateUserNamesAndNumbers.uuid = transferCode
I've Partially answered my own Question.
Thanks to P.Salmon, for Querying the transferCode variable.
It turns out I had defined the string as varchar(24), but the input was actually bigger than that.
So once I sorted that, the code worked, but only the first time.
I still need to have a really good think about this, as a second call to the same routine with the same transferCode input, where the temporary tuple had already been deleted by the first call, does not throw the MySQL Exception, as I thought it should. So "its Still Thinking Cap On Time"
Related
So I was playing with transactions, and I have tried to subtract the funds from one transfer it to another. As you can see from the picture, the first update query wasn't successful...Unlike the second one, which executed successfully. Now, what I was expecting is, that when I hit the commit, I wouldn't see any changes. But that wasn't the case. Also, I have use START TRANSACTION (it implicitly sets autocommit to 0), rather than BEGIN command.
Here is the output of this:
What I am missing here?
I don't understand your confusion. Both your updates succeeded. The first one happened to not affect any rows, so only the second one actually changed the data.
You committed the transaction, so all changes take effect.
If you wanted to test transactions, roll back the transaction. Then, when you look at the data, you'll see that nothing changed.
Not any of your operations has failed.
In first update, where conditions were not satisfied and hence no any
row was updated.
In second one, where condition was satisfied for one record, hence that one record was updated.
As an addition to Gordon's answer and his mentioning of stored procedure, I will add an answer just for the future readers and for the completeness, because my real issue was how to rollback a transaction if some condition is not satisfied:
DELIMITER //
CREATE PROCEDURE transfer(IN sender INT, IN receiver INT)
BEGIN
START TRANSACTION;
SET #senderBalance = (SELECT balance FROM bank_acc WHERE acctnum = sender LIMIT 1);
select #senderBalance;
IF (#senderBalance < 50) THEN
ROLLBACK;
ELSE
update bank_acc set balance = balance - 50 where acctnum = sender;
update bank_acc set balance = balance + 50 where acctnum = receiver;
COMMIT;
END IF;
END//
DELIMITER ;
Later, you can use it like this:
call transfer(#sender := 20, #receiver := 10);
I am writing a trigger for controlling a column. The script works as I want but my problem is in the raiseerror. I want the trigger to work without showing the error message to the user.
Can anyone who knows what is the equivalent of raiseerror without showing the error message to the user?
I tried with rollback transaction which gve me another error message instead, and I tried with return which did not interrupt the execution of the trigger.
This is my trigger:
DECLARE #val varchar(9)
SELECT #val= [DC_Piece]
from INSERTED
where INSERTED [DC_Domaine]=0 and INSERTED.[DC_IdCol]=6
IF UPDATE([DC_Piece])
BEGIN
IF NOT EXISTS( select [DO_PIECE]
from DOCEN
where #val= [DO_Piece] and [DO_Domaine]=0 and [DO_Type]=6)
RAISERROR('STOP',11,1)
END
Please help me
You need to completely rewrite your trigger to take into account that it will be called once per statement (NOT per row!) and the Inserted and Deleted pseudo tables can contain multiple rows which you should consider.
So try something like this:
CREATE TRIGGER trg_abort_insert
ON dbo.YourTableNameHere
AFTER UPDATE
AS
-- check if any of the DC_Piece columns have been updated
IF EXISTS (SELECT *
FROM Inserted i
INNER JOIN Deleted d ON i.PrimaryKey = d.PrimaryKey -- link the two pseudo tables on primary key
WHERE i.DC_Piece <> d.DC_Piece -- DC_Piece has changed
AND i.DC_Domaine = 0
AND i.DC_IdCol = 6)
-- if your conditions are met --> just roll back the transaction
-- nothing will be stored, no message is shown to the user
ROLLBACK TRANSACTION
END
I need to increment the next highest character field upon an insert - e.g. if '007' exists, then next is '008'
I have a stored procedure:
BEGIN
SELECT LPAD(CAST(MAX(Line_Order) AS SIGNED INTEGER) + 1,3,'0')
FROM bill_project_lineitems
WHERE Item_Id = vItem;
END
Then I have this trigger:
CALL MaxLineOrder(new.item_id,#new.line_order )
Which I call before.
When I try to INSERT, MySQL complains: 1415 - 'Not allowed to return a result set from a trigger'
So then how do I solve this problem?
Ok, I read again and MAYBE I undestood. (but why not pasting the full code?) The error is clear, You CANT return a resultset from a Trigger.
I suppose that you want to issue an INSERT and get a resultset. But you can't. So you should:
Move your INSERT into the Stored Procedure
DROP the Trigger
CALL the Procedure.
Stored Procedures can return a resultset (or even many resultsets), so this will work. I hope that this is what you want.
I am trying to find a method to rollback a transaction after it has been executed.
For example:
DECLARE #tsubaki VARCHAR(25);
SET #tsubaki = 'A Transaction';
BEGIN TRANSACTION #tsubaki
UPDATE dbo.Maka SET id = 400, name = 'inu' --notice I didn't put there where clause
COMMIT TRANSACTION
Later on I realize that I updated everything in the databse Maka instead of just the one record I originally intended.
Now I try to write code to roll it back before the update:
DECLARE #tsubaki VARCHAR(25);
SET #tsubaki = 'A Transaction';
ROLLBACK TRANSACTION #tsubaki;
Doesn't work. Bottom line: I am looking for a way to rollback a sql transaction in MS-SQL Server 2008 after the transaction has been commit and the sql has ran.
Thanks in advance.
You can't do that from T-SQL code. You will have to restore to a point in time from the log file. Note that the restore will "undo" everything to a point in time, including your transaction.
In the future you should ALWAYS back up your db before any manual update, small or large. You can also cover yourself with a little trick. Write out your update/delete code like this:
SELECT * FROM dbo.Maka
-- UPDATE dbo.Maka SET id = 400, name = 'inu'
WHERE some_identifier = some_value
Run the SELECT version first which is innocuous and when you can verify the record(s) to be updated select the code from the WHERE clause up to the UPDATE and run it.
I'm having a rather strange problem with MySQL. Trying to create a procedure to update some fields in the database (the code is below).
The problem is with the line that is currently commented. It seems that if no SELECT statements get executed during the procedure MySQL query browser will return an error code of "-1, error executing SQL query".
I tried the same thing in HeidiSQL and the error was "cannot return result set". So I suppose the question is do I always have to select something in the procedure, or is there some other thing I missed.
The query works fine when the comment is removed.
DELIMITER /
DROP PROCEDURE IF EXISTS updateFavourites /
CREATE PROCEDURE updateFavourites(quota INT)
BEGIN
DECLARE done INT DEFAULT 0;
DECLARE artist_id,releases INT;
DECLARE c_artist Cursor FOR
SELECT Artist.id_number,COUNT(Artist.id_number) FROM Artist
JOIN CD ON CD.is_fronted_by = Artist.id_number
GROUP BY Artist.id_number;
DECLARE CONTINUE HANDLER FOR SQLSTATE '02000'
SET done=1;
IF quota > 0 THEN
OPEN c_artist;
REPEAT
FETCH c_artist INTO artist_id,releases;
IF NOT done THEN
IF releases >= quota THEN
UPDATE CD SET CD.rating='favourite' WHERE CD.is_fronted_by = artist_id;
END IF;
END IF;
UNTIL done END REPEAT;
CLOSE c_artist;
-- SELECT 'Great success';
ELSE
SELECT CONCAT('\'quota\' must be greater than 0.',' Got (',quota,')');
END IF;
END /
DELIMITER ;
Here's the sql to create the tables and some data:
DROP TABLE IF EXISTS CD;
DROP TABLE IF EXISTS Artist;
CREATE TABLE Artist (
id_number INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50),
);
CREATE TABLE CD (
catalog_no INTEGER UNSIGNED AUTO_INCREMENT PRIMARY KEY,
is_fronted_by INT UNSIGNED,
rating ENUM ('favourite','top draw','good','so-so','poor','rubbish'),
CONSTRAINT fk_CD_Artist FOREIGN KEY (is_fronted_by) REFERENCES Artist(id_number) ON UPDATE CASCADE
);
INSERT INTO Artist VALUES(11,'Artist 1');
INSERT INTO Artist VALUES(10,'Artist 2');
INSERT INTO CD VALUES (7,11, 'top draw');
INSERT INTO CD VALUES (650,11,'good');
INSERT INTO CD VALUES (651,11,'good');
INSERT INTO CD VALUES (11,10,'favourite');
Query Browser is not for running scripts, just single query.
I tried your code by moving cursor into each query (except DELIMITER) and pressing Ctrl+Enter.
It created that stored procedure without problem. (just refresh schema on the left).
If you wish creating procedure, use menu "Script"->"Create stored procedure/function".
But better forget about QueryBrowser it is not supported at all (and actunally not useful).
If you have decent hardware and plenty resources, try Workbench 5.2 otherwise use SQLyog
Googling around, there are several reports of the same error, but little information to solve the problem. There's even a bug logged at mysql.com but it appears to have been abandoned without being resolved.
There's another StackOverflow question on the same error, but it's also unresolved.
All it means is that there is no result set from the query. Looking at the source code, it appears that sometimes an error status of MYX_SQL_ERROR is set when the query has no result set. Perhaps this is not an appropriate consequence?
I notice that when I use the mysql command-line client, it yields no error for calling a proc that returns no result set.
update: I tried to revive that MySQL bug report, and provide a good test case for them. They changed the bug from "no feedback" to "verified" -- so at least they acknowledge it's a bug in Query Browser:
[11 Dec 9:18] Sveta Smirnova
Bill,
thank you for the feedback. Verified
as described.
Although most likely this only be
fixed when MySQL Query Browser
functionality is part of MySQL
workbench.
I guess the workaround is to ignore the -1 error, or to test your stored procedures in the command-line mysql client, where the error does not occur.
The comment supposes the issue will disappear as the Query Browser functionality becomes part of MySQL Workbench. This is supposed to happen in MySQL Workbench 5.2. I'll download this beta and give it a try.
MySQL Workbench 5.2 is in Beta, but I would assume MySQL engineering can't predict when the Beta will become GA. Those kinds of predictions are hard enough under standard conditions, but there's a lot of extra uncertainty of MySQL's fate due to the unresolved Oracle acquisition.
update: Okay, I have tried MySQL Workbench 5.2.10 beta. I executed a stored procedure like this:
CREATE PROCEDURE FooProc(doquery SMALLINT)
BEGIN
IF doquery THEN
SELECT * FROM Foo;
END IF;
END
When I CALL FooProc(0) the response is no result set, and the status is simply "OK".
When I CALL FooProc(1) the response is the result of SELECT * FROM Foo as expected.
However, there's another bug related to calling procedures. Procedures may have multiple result sets, so it's hard to know when to close the statement when you execute a CALL query. The consequence is that MySQL Workbench 5.2 doesn't close the statement, and if you try to do another query (either CALL or SELECT) it gives you an error:
Commands out of sync; you can't run this command now.
MySQL doesn't support multiple concurrent open queries. So the last one must be closed before you can start a new one. But it isn't closing the CALL query. This bug is also logged at the MySQL site.
The bug about commands out of sync has been resolved. They say it's fixed in MySQL Workbench 5.2.11.
Try putting BEGIN and END blocks around the multiple statements in the IF block as such:
IF quota > 0 THEN
BEGIN
OPEN c_artist;
REPEAT
FETCH c_artist INTO artist_id,releases;
IF NOT done THEN
IF releases >= quota THEN
UPDATE CD SET CD.rating='favourite' WHERE CD.is_fronted_by = artist_id;
END IF;
END IF;
UNTIL done END REPEAT;
CLOSE c_artist;
END;
ELSE
SELECT CONCAT('\'quota\' must be greater than 0.',' Got (',quota,')');
END IF;