I am learning MySQL and i have a simple transaction to make. Need to deduct quantity from stock, if there is sufficient stock for a specific item. Else, need to roll back the whole transaction.
I have tried the following code :
SET #item_code = 'PEN-001';
set #balance = (SELECT quantity_in_hand FROM items WHERE item_code = 'PEN-001');
-- ------------------------------------------------
DELIMITER //
START TRANSACTION;
IF (#balance) >= 0 THEN
BEGIN
UPDATE items SET quantity_in_hand = #balance - #purchaseqty WHERE item_code = 'PEN-001'
END;
ELSE
BEGIN
ROLLBACK;
SELECT 'Insufficient Stock';
END;
END IF;
COMMIT;
DELIMITER ;
When I run this query in MySQL, nothing happens, there is no action output or error displayed either. Is this the right approach? And, What is the issue with this code?
You could do:
UPDATE items
SET quantity_in_hand = quantity_in_hand - #purchaseqty
WHERE item_code = 'PEN-001' and quantity_in_hand>=#purchaseqty
and then check ROW_COUNT()
The transaction in your example does nothing as rollback has nothing to do and the update when it happens, is atomic by default.
Related
I am trying to use an attribute from a 2nd table in the trigger of the 1st. To do this I am trying to load that value into a variable and then use it as a comparison.
However whenever I try and test the process the comparison answers false.
DELIMITER $$
create trigger evolve_persona before update on phantom_thieves
for each row begin
set #t := (select tier from persona where pname = old.persona);
if((new.persona != old.persona) and (select cast(#t as unsigned) = '1')) then
set
new.st = old.st+10, new.ma = old.ma+10, new.en= old.en+10, new.ag= old.ag+10,
new.lu= old.lu+20;
end if;
end$$
DELIMITER ;
I can see nothing wrong with your trigger but, this is somewhat more complicated as be written in a comment.
Make please following
SET #t = -1;
SELECT #t; -- returns -1
update phantom_thieves SET .....
SELECT #t; -should display at sometime 1
This seems to be the only problem that tier don't has the response 1 and with above, you can see what you get.
I have to create a trigger in a table proyect, to check if all the tests for the proyect are 'ready'. If all of them are ready, I need to search the proyect and put the status of the proyect as 'ready'. If not, Incomplete.
I'm aware that I need to create one trigger for the UPDATE, and another one for the INSERT. I've planned something like this, But I can not make it work.
I created this solution, if the total number of test for that proyect, is below to the number of test ready for that proyect, then the proyect is not ready. and if both are equals, the proyect is ready.
I don't know why it doesn't work:
CREATE TRIGGER update-proyect
AFTER INSERT ON tests
FOR EACH ROW
SET #total = select COUNT(*)
from tests
where IdProyect= NEW.IdProyect;
SET #ready = select COUNT(*)
from prueba
from tests
where IdProyect= NEW.IdProyect
AND status = 'ready';
IF (#total == #ready)
UPDATE proyect SET status = 'Ready' WHERE IdProyect = NEW.IdProyect;
ELSE
UPDATE proyect SET status = 'Incomplete' WHERE IdProyect = NEW.IdProyect;
END IF;
Loads of errors update-proyect is an invalid trigger name change to update_proyect, If you have more than 1 statement in a trigger they have to be encased in a begin and end statements, a select after a set has to be encased in braces, there is no == comparison relation operator in mysql either use = or null safe equals <=>, an if statement has to have a then statement.
This at least syntaxes.
DROP TRIGGER IF EXISTS update_proyect;
DELIMITER $$
CREATE TRIGGER update_proyect
AFTER INSERT ON tests
FOR EACH ROW
BEGIN
SET #total = (select COUNT(*)
from tests
where IdProyect= NEW.IdProyect
);
SET #ready = (select COUNT(*)
from prueba
#from tests
where IdProyect= NEW.IdProyect
AND status = 'ready'
);
IF (#total = #ready) THEN
UPDATE proyect SET status = 'Ready' WHERE IdProyect = NEW.IdProyect;
ELSE
UPDATE proyect SET status = 'Incomplete' WHERE IdProyect = NEW.IdProyect;
END IF;
END $$
DELIMITER ;
So I'm trying to work out a trigger that automatically adds the ingredients from a order (dhh_purchaseorder) to the table dhh_ingredients. It should get the new ordered amount out of dhh_purchaseorderingredient and dump it into the orderAmount variable. Then it should combine the current available stock with the ordered amount in newStock. Then it should update the ingredient table and set the new amounts for the correct ingredients but for some reason it keeps putting the currentStock column in ingredients to 0.
Here is my trigger:
BEGIN
DECLARE orderStatus, orderIngredientName VARCHAR(50);
DECLARE finished INTEGER DEFAULT 0;
DECLARE currentStock, newStock DECIMAL(10,2);
DECLARE orderNo, orderAmount int(10);
DECLARE lookupCursor CURSOR FOR SELECT INGREDIENTingredientName from dhh_purchaseorderingredient WHERE dhh_purchaseorderingredient.PURCHASEpurchaseOrderNo = NEW.purchaseOrderNo;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET finished = 1;
SET orderNo = NEW.purchaseOrderNo;
SET orderStatus = NEW.status;
IF(orderStatus = "DELIVERED") THEN
OPEN lookupCursor;
update_loop: LOOP
FETCH lookupCursor INTO orderIngredientName;
IF finished = 1 THEN
LEAVE update_loop;
END IF;
SET orderAmount = (SELECT amount from dhh_purchaseorderingredient WHERE dhh_purchaseorderingredient.PURCHASEpurchaseOrderNo = orderNo AND dhh_purchaseorderingredient.INGREDIENTingredientName = orderIngredientName);
SET currentStock = (SELECT currentStock FROM dhh_ingredient WHERE ingredientName = orderIngredientName);
SET newStock = currentStock + orderAmount;
INSERT INTO temp VALUES(currentStock);
UPDATE dhh_ingredient
SET currentStock = newStock
WHERE ingredientName = ingredientName;
END LOOP update_loop;
CLOSE lookupCursor;
END IF;
END
This is partly an answer and partly a suggestion for a better approach.
One of the many reasons I believe stored procedures are a very bad idea is that they are hard to debug. In this case, it's not easy to figure out what value is being assigned to the various stock amount variables.
A better approach is to use query that does update - without the need for SP variable. It's really easy to test it; just execute and inspect the results. By using such a query in your SP, you eliminate incorrect query logic from your code, allowing you to condense the SP to just the bits that matter - the update.
Fortunately, MySQL provides a way to update through a join, via its multi-table update syntax.
This is what the query could look like:
UPDATE dhh_ingredient, dhh_purchaseorderingredient
SET = currentStock = currentStock - amount
WHERE dhh_purchaseorderingredient.INGREDIENTingredientName = orderIngredientName
AND ingredientName = orderIngredientName
AND dhh_purchaseorderingredient.PURCHASEpurchaseOrderNo = NEW.purchaseOrderNo
AND NEW.status = 'DELIVERED';
No need for local variables or any loops, because the update query does a join and the NEW values are used directly in the query.
So your entire stored procedure becomes simply:
BEGIN
UPDATE dhh_ingredient, dhh_purchaseorderingredient
SET = currentStock = currentStock - amount
WHERE dhh_purchaseorderingredient.INGREDIENTingredientName = orderIngredientName
AND ingredientName = orderIngredientName
AND dhh_purchaseorderingredient.PURCHASEpurchaseOrderNo = NEW.purchaseOrderNo
AND NEW.status = 'DELIVERED';
END
which is easy to understand, maintain and debug - a better solution.
I have something like this:
UPDATE users SET credits=credits-1000 WHERE id=1
How do I make this fail if credits goes negative? I want to avoid having to query the database twice just to check for that. I need it to FAIL/return false if it goes negative, not set the credits to zero.
What about adding the condition ?
UPDATE users SET credits=credits-1000 WHERE id=1 AND credits > 1000
I think you have to use stored procedure for this purpose. For example
CREATE procedure UpdateUserCredit
#user_id INT,
#credits INT,
#op_status BIT OUTPUT
AS
BEGIN
SET #op_status = 0
UPDATE Users
SET credits = credits - #credits
WHERE id = #user_id
AND credits - #credits > 0
IF ROW_COUNT() = 0
RETURN
ELSE
SET #op_status = 1
END
This stored procedure will return 0 in variable #op_status if current credits + #credits for #user_id would be less zero.
DELIMITER $$
CREATE TRIGGER users_bu BEFORE UPDATE ON users FOR EACH ROW
BEGIN
IF NEW.credits != OLD.credits AND NEW.credits < 0 THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'negative balances are not supported';
END IF;
END $$
DELIMITER ;
Before the update is written to a row in the table, the new (replacement) value for credits is checked for negative values if that value has been changed by this update. If it changed, and is now negative, deny the update by throwing an exception. Requires MySQL Server 5.5 or later as written, but can be made to work with 5.1 or 5.0 with some modifications.
Note that FOR EACH ROW doesn't mean the whole table, it means that each row affected by the update is evaluated individually by this code, which will of course typically be just one row.
I call a procedure, it runs, and the console says "0 rows affected". Is this normal behavior for a MySQL procedure ?
The procedures are clearly doing what they should. One procedure has 2 insert statements, another has an insert and update statement, and I've seen the results with my own eyes. There are indeed rows being affected.
I'm not sure that I would use that result later on, but it seems like I'd want to get an accurate response from my DB whether or not anything was updated, especially when its expected.
Thoughts ?
MySQL 5.5 if it matters, and the procedures use transactions over auto-committed statements.
CREATE DEFINER=`root`#`localhost` PROCEDURE `create_issue`(user_id SMALLINT, title varchar(255), body LONGTEXT)
BEGIN
DECLARE MYUSERID SMALLINT;
DECLARE EXIT HANDLER FOR SQLEXCEPTION BEGIN END;
START TRANSACTION;
INSERT INTO tracker.issue (user_id, title, body, creation_date, last_mod_date) values (user_id, title, body, CURDATE(), CURDATE());
UPDATE user_activity SET last_new_issue = CURDATE(), post_count = post_count + 1 WHERE user_activity.user_id = user_id;
COMMIT;
END
Edited to show the actual query. Also I've been searching and as best as I can tell this is a known issue over a year and a half old. So I suppose this one can be closed.
the "0 rows affected" response is for the last statement executed in the stored procedure.
usually i track the number of rows effected by manually counting them into session variables
DELIMITER $$
CREATE PROCEDURE `create_issue`(user_id SMALLINT, title varchar(255), body LONGTEXT)
BEGIN
DECLARE MYUSERID SMALLINT;
DECLARE EXIT HANDLER FOR SQLEXCEPTION BEGIN END;
SET #inserted_rows = 0;
SET #updated_rows = 0;
START TRANSACTION;
INSERT INTO tracker.issue (user_id, title, body, creation_date, last_mod_date) values (user_id, title, body, CURDATE(), CURDATE());
SET #inserted_rows = ROW_COUNT() + #inserted_rows;
UPDATE user_activity SET last_new_issue = CURDATE(), post_count = post_count + 1 WHERE user_activity.user_id = user_id;
SET #updated_rows = ROW_COUNT() + #updated_rows;
COMMIT;
END
$$
the session variables can then be read after the SP was executed.
i am not sure if it is possible to override the response from the ROW_COUNT() function by setting a value to a variable,
I guess this is a reported bug. May be a good question for MySQL mailing list/forum. http://bugs.mysql.com/bug.php?id=44854
Something definitely isn't right. A sproc should still return the number of rows affected if there are multiple inserts occurring. I'm using the same version of MySQL and this works fine for me.
Are you sure you're not doing something like that
...SET col1='value1' AND col2='value2'...
instead of
...SET COL1='value1', col2='value2'...
Could you post your stored procedure?