Trigger with query data - mysql

I have table with the informations:
INSERT INTO Transferencias
('Origem_Apostador_ID', 'Destino_Apostador_ID', 'Valor_Transferido')
VALUES
('1', '6', '200.00');
And I need to get value '200.00' and sum with the data from another table after this value have been insert on table to this table below:
INSERT INTO Conta
('Apostador_ID', 'Saldo', 'Qtd_Saque', 'Qtd_Deposito', 'Qtd_Transferencia')
VALUES
('1', '700.00', '0', '1', '0');
I have no idea how can I use data from query to do the update on "Saldo" column.

I don`t know if I understood your question correctly, but i think you need something like this:
UPDATE Conta
SET Saldo = Saldo + (SELECT Valor_Transferido FROM Transferencias WHERE Origem_Apostador_ID = '1')
WHERE Apostador_ID = '1';
You can add this code to some trigger if you want to. You can find many examples out there for simple triggers to do the work depending on your database.
TRIGGER EXAMPLE ON MYSQL
CREATE TRIGGER TRG_UPDATE_SALDO BEFORE INSERT ON Transferencias
FOR EACH ROW
BEGIN
IF NEW.Valor_Transferido > 0 THEN
--Increments the destination account
UPDATE Conta
SET Saldo = Saldo + NEW.Valor_Transferido
WHERE Apostador_ID = NEW.Destino_Apostador_ID;
--Subtracts the origin account
UPDATE Conta
SET Saldo = Saldo - NEW.Valor_Transferido
WHERE Apostador_ID = NEW.Origem_Apostador_ID;
END IF;
END;

Related

mysql trigger - Compare Values Different Table

I am learning SQL, I am using mysql workbench and I have a problem related with trigger.
I have two tables, one to them called DireccionEntrega with attributes: idDir, idT, idPed, Road, Pais, Region. The other called Cobertura with attributes: idCob, idProv, Pais, Region.
I want to create a trigger for table DireccionEntrega so that when I insert a values in it, the values of Country and Region of table DireccionEntrega, they must be in the table Cobertura. If not, an error message is displayed.
I show you how I created the trigger:
create trigger Comprobacion before insert on DireccionEntrega
for each row
begin
if not exists(select Cobertura.idProv, Cobertura.Pais, Cobertura.Region from Cobertura, Proveedor, LineasPedido where
Cobertura.idProv = Proveedor.idProv and Proveedor.idProv = LineasPedido.idProv)
then
signal sqlstate '45000'
set message_text = 'Error insert...';
end if;
end;
I do not what is happening but its not working how I expect.
The expectations of the trigger is following like this:
insert into DireccionEntrega(Pais, Region) values ('Spain','West')
it's okey because I have that information in Cobertura.
insert into DireccionEntrega(Pais, Region) values ('Portugal','South')
it's wrong because I don't have that information in Cobertura.
If you need something to solve what I want, please let me know and answer whatever you want.
Thanks so much!
Ok, thanks for the dbfiddle!
So, to NOT find a matching pair of Country and Region in Cobertura, before insert into table DireccionEntrega, the trigger handling changes to:
if not exists(
select Cobertura.Pais, Cobertura.Region
from Cobertura
join Proveedor ON Cobertura.idProv = Proveedor.idProv
join LineasPedido ON Proveedor.idProv = LineasPedido.idProv
where Cobertura.Pais = NEW.Pais and Cobertura.Region = NEW.Region
) then
set #message = CONCAT(
'Error insert... did not find a matching pair for: ',
NEW.Pais, ' and ', NEW.Region, ' in Cobertura.'
);
signal sqlstate '45000'
set message_text = #message;
end if;
So with the two fiddle insert query examples:
insert into DireccionEntrega(idT, idPed, N, Calle, Poblacion, Ciudad, Telefono, email, Pais, Region)
values (1,1,13,'Alba','malaga','malaga', 654544434,'shop1#gmail.com','España','Este');
insert into DireccionEntrega(idT, idPed, N, Calle, Poblacion, Ciudad, Telefono, email, Pais, Region)
values (1,2,11,'CAdd','dos hermanas','sevilla', 654544434,'shop1#gmail.com','Portugal','Oeste');
Because neither España and Este and Portugal and Oeste are not found in Cobertura, both give the error:
Error insert... did not find a matching pair for: España and Este in Cobertura.
Error insert... did not find a matching pair for: Portugal and Oeste in Cobertura.
So those 2 records do NOT get inserted.
Here's the updated dbfiddle example with the trigger change applied.

MYSQL trigger to update or insert based on if statements

I have two tables, tbl_stock and tbl_stockEntry, those two tables are identical.
They have these fields:
warehouse
cultivar
variety
quantity
when an entry is entered in to the tbl_stockEntry table I would like it to either update the matching entry in the tbl_stock table or create a new entry if there is no matching entry.
But I need it to match 3 fields, warehouse, cultivar and variety.
This is my trigger to create the entry, but I can't seem to get it to insert a record if none is found.
BEGIN
UPDATE tbl_stock
SET quantity = quantity + NEW.quantity
WHERE vegetableCultivar = NEW.vegetableCultivar
AND warehouse = NEW.warehouse;
END
I have tried this and but it doesnt work:
BEGIN
UPDATE tbl_stock
SET quantity = quantity + NEW.quantity
WHERE vegetableCultivar = NEW.vegetableCultivar
AND warehouse = NEW.warehouse;
if sql%rowcount = 0 then
-- nothing was updated, so the record doesn't exist, insert it.
insert into tbl_stock (warehouse, vegetableCultivar, vegetableVariety, quantity)
values (NEW.warehouse, NEW.vegetableCultivar, NEW.vegetableVariety, NEW.quantity);
end if;
END
If anyone knows how I could accomplish this I would really appreciate some assistance.
Ok this is working:
BEGIN
UPDATE tbl_stock
SET quantity = quantity + NEW.quantity
WHERE vegetableCultivar = NEW.vegetableCultivar
AND warehouse = NEW.warehouse;
IF ROW_COUNT() = 0 THEN
INSERT INTO tbl_stock (warehouse, vegetableCultivar, vegetableVariety, quantity)
values (NEW.warehouse, NEW.vegetableCultivar, NEW.vegetableVariety, NEW.quantity);
end if;
END

IF EXISTS statement giving error #1064

Can't seem to get a simple if exists statement to work in mysql, is there a possible reason why it simply will not work???
IF EXISTS(SELECT * FROM Cookies WHERE VALUED ='2601:2c0:8403:5320:947e:a047:6e0f:e23a')
BEGIN
THEN
END;
UPDATE Cookies SET Amount = Amount + '1' WHERE VALUED ='2601:2c0:8403:5320:947e:a047:6e0f:e23a'
ELSE
BEGIN
INSERT INTO Cookies (Valued, Amount) Values ('2601:2c0:8403:5320:947e:a047:6e0f:e23a', '1' )
END;
Delete all BEGIN and END - you don't need them and you've used them incorrectly anyway:
IF EXISTS(SELECT * FROM Cookies WHERE VALUED ='2601:2c0:8403:5320:947e:a047:6e0f:e23a') THEN
UPDATE Cookies SET
Amount = Amount + '1'
WHERE VALUED ='2601:2c0:8403:5320:947e:a047:6e0f:e23a'
ELSE
INSERT INTO Cookies (Valued, Amount) Values
('2601:2c0:8403:5320:947e:a047:6e0f:e23a', '1' );
END IF;
If VALUED is the primary key, you can do it much simpler:
insert into Cookies (Valued, Amount) values
('2601:2c0:8403:5320:947e:a047:6e0f:e23a', 1 )
on duplicate key update Amount = Amount + 1;
You seem to have messed up the order of the then and begin keywords:
IF EXISTS(SELECT * FROM Cookies WHERE VALUED ='2601:2c0:8403:5320:947e:a047:6e0f:e23a')
THEN -- Here!
BEGIN
UPDATE Cookies SET Amount = Amount + '1' WHERE VALUED ='2601:2c0:8403:5320:947e:a047:6e0f:e23a'
END;
ELSE
BEGIN
INSERT INTO Cookies (Valued, Amount) Values ('2601:2c0:8403:5320:947e:a047:6e0f:e23a', '1' )
END;
EDIT:
Assuming valued is a primary (or even just a unique) key, it would be easier to use an insert statement with an on duplicate clause:
INSERT INTO cookies (valued, amount)
VALUES ('2601:2c0:8403:5320:947e:a047:6e0f:e23a', 1)
ON DUPLICATE KEY UPDATE amount = amount + 1;

Populate column with a default value

I'm working in MYSQL 2008 and I have created a simple table:
create table Productos (
nombre char (30),
size int,
nota char (40))
I need to create a trigger for insert. When I insert values I only need to provide values for nombre, size and nota needs to be NULL.
The trigger needs to populate nota if size from inserted is > 50, it needs to populate the column with the word mayor and if it is < 50 with the word menor.
This is what I have tried:
CREATE TRIGGER Inserta
ON Productos
for INSERT
AS
BEGIN
IF EXISTS(
SELECT NULL
FROM Productos
)
Begin
UPDATE Productos
SET nota =
CASE
WHEN (select size
from inserted ) > 50
THEN 'Mayor'
Else 'Menor'
END
from
inserted
End
End
This is changing the values of all the columns, not just the inserted ones. I'm a newbie on this and I am confused.
Can anyone enlighten me?
CREATE TRIGGER Inserta BEFORE INSERT on Productos
SET nota = CASE WHEN new.size > 50 THEN 'Mayor' Else 'Menor' END;

Calling stored procedure sequentially from .sql file

I'm stuck here.
I've got a Procedure that I want to run X* times in a row. (*X is couple of thousands times)
The procedure based on input data does this:
1. Looks for an actions.id, if not found LEAVEs.
2. Looks for users.id, if not found, creates one and uses LAST_INSERT_ID();
3-5. Looks for summaries.id (3 types, total, daily and monthly), if not found, creates one and uses it's ID.
6. Once all required ids are collected, INSERTs new row into actions and either updates the summaries rows in a transaction, so if any fails - it does a ROLLBACK - no harm done.
7. Depending on the outcome SELECTs message.
CREATE PROCEDURE NEW_ACTION(
IN a_date TIMESTAMP,
IN u_name VARCHAR(255),
IN a_name VARCHAR(255),
IN a_chars INT,
IN url VARCHAR(255),
IN ip VARCHAR(15))
lbl_proc: BEGIN
DECLARE a_id, u_id, us_id, usd_id, usm_id, a_day, a_month, error INT;
DECLARE CONTINUE HANDLER FOR SQLSTATE '23000' SET error = 1;
SET error = 0;
SET a_day = DATE_FORMAT(SUBSTRING(a_date ,1,10), '%Y%m%d');
SET a_month = SUBSTRING(a_day, 1, 6);
/* 1. RETREIVING action.id */
SET a_id = (SELECT `id` FROM `actions` WHERE `name` = a_name);
IF a_id IS NULL THEN
SELECT 'error';
LEAVE lbl_proc;
END IF;
/* 2. RETREIVING users.id */
SET u_id = (SELECT `id` FROM `users` WHERE `name` = u_name);
IF u_id IS NULL THEN
INSERT INTO `users` (name) VALUES (u_name);
SET u_id = (SELECT LAST_INSERT_ID());
END IF;
/* 3. RETREIVING user_summaries.id */
SET us_id = (SELECT `id` FROM `users_summaries` WHERE `user_id` = u_id AND `action_id` = a_id);
IF us_id IS NULL THEN
INSERT INTO `users_summaries` (user_id, action_id) VALUES (u_id, a_id);
SET us_id = (SELECT LAST_INSERT_ID());
END IF;
/* 4. RETREIVING user_summaries_days.id */
SET usd_id = (SELECT `id` FROM `users_summaries_days` WHERE `day` = a_day AND `user_id` = u_id AND `action_id` = a_id);
IF usd_id IS NULL THEN
INSERT INTO `users_summaries_days` (day, user_id, action_id) VALUES (a_day, u_id, a_id);
SET usd_id = (SELECT LAST_INSERT_ID());
END IF;
/* 5. RETREIVING user_summaries_months.id */
SET usm_id = (SELECT `id` FROM `users_summaries_months` WHERE `month` = a_month AND `user_id` = u_id AND `action_id` = a_id);
IF usm_id IS NULL THEN
INSERT INTO `users_summaries_months` (month, user_id, action_id) VALUES (a_month, u_id, a_id);
SET usm_id = (SELECT LAST_INSERT_ID());
END IF;
/* 6. SAVING action AND UPDATING summaries */
SET autocommit = 0;
START TRANSACTION;
INSERT INTO `users_actions` (`date`, `user_id`, `action_id`, `chars`, `url`, `ip`) VALUES (a_date, u_id, a_id, a_chars, url, ip);
UPDATE `users_summaries` SET qty = qty + 1, chars = chars + a_chars WHERE id = us_id;
UPDATE `users_summaries_days` SET qty = qty + 1, chars = chars + a_chars WHERE id = usd_id;
UPDATE `users_summaries_months` SET qty = qty + 1, chars = chars + a_chars WHERE id = usm_id;
IF error = 1 THEN
SELECT 'error';
ROLLBACK;
LEAVE lbl_proc;
ELSE
SELECT 'success';
COMMIT;
END IF;
END;
Now, I've got raw data that I want to feed into this procedure. There's currently about 3000 rows.
I tried all the solutions I knew:
A. # mysql -uuser -ppass DB < calls.sql - Using php I've basically created a list of calls like this:
CALL NEW_ACTION('2010-11-01 13:23:00', 'username1', 'actionname1', '100', 'http://example.com/', '0.0.0.0');
CALL NEW_ACTION('2010-11-01 13:23:00', 'username2', 'actionname1', '100', 'http://example.com/', '0.0.0.0');
CALL NEW_ACTION('2010-11-01 13:23:00', 'username1', 'actionname2', '100', 'http://example.com/', '0.0.0.0');
...
This fails always (tried few times) at row 452 where it found two summary IDs (step 3).
I thought this could be due to the fact that earlier (rows 375-376) there are calls for the same user for the same action.
As if mysql didn't update tables in time, so the summary row created in CALL from line 375 isn't yet visible when line 376 gets executed - therefore creating another summary line.
Tought I'd try delaying calls...
B. Using mysql's SLEEP(duration).
This didn't change anything. Execution stops at the very same CALL again.
I'm out of ideas now.
Suggestions and help hugely appreciated.
NOTE: action names and user names repeat.
PS. Bear in mind this is one of my first procedures ever written.
PS2. Running mysql 5.1.52-community-log 64bit (Windows 7U), PHP 5.3.2 and Apache 2.2.17
EDIT
I've removed PHP related part of question to a separate question here.
EDIT2
Ok, I've deleted the first 200 calls from the .sql file. For some reason it went fine past the previous line that was stopping execution. Now it stopped at row 1618.
This would mean, that at one point a newly INSERTed summary row is no visible for a moment, therefore when it happens that one of the following iterations want to SELECT it, it's not yet accessible for them. Is that a MySQL bug?
EDIT3
Now there's another interesting thing I noticed. I investigated where two users_summaries get created. This happens (not always, but if, then it is) when there are two CALLs referring to the same user and action in close proximity. They could be next to each other or separated by 1 or 2 different calls.
If I move one of them (within .sql file) like 50-100 rows lower (executed earlier) than it's fine. I even managed to make the .sql file work as a whole. But this still doesn't really solve the problem. With 3000 rows it's not that bad, but if I had 100000, I'm lost. I can't rely on manual tweaks to .sql file.
This isn't really a solution, but a workaround.
Just to clarify, summary tables had id column as PRIMARY KEY with AUTO_INCREMENT option and indexes on both user_id and action_id column.
My investigation showed that although my procedure was looking for an entry that existed using WHERE user_id = u_id AND action_id = a_id in certain situations it didn't find it causing new row being inserted with the same user_id and action_id values - something I did not want.
Debugging the procedure showed that the summary row I was looking for, although not accessible with WHERE user_id = u_id AND action_id = a_id condition, was properly returned when calling it's id - PRIMARY KEY.
With this find I decided to change format of id column, from UNASIGNED INT with AUTO_INCEREMENT to a CHAR(32) which consisted of:
<user_id>|<action_id>
This meant that I knew exactly what the id of the row I wanted is even before it existed. This solved the problem really. It also enabled me to use INSERT ... ON DUPLICATE KEY UPDATE ... construct.
Below my updated procedure:
CREATE PROCEDURE `NEW_ACTION`(
IN a_date TIMESTAMP,
IN u_name VARCHAR(255),
IN a_name VARCHAR(255),
IN a_chars INT,
IN url VARCHAR(255),
IN ip VARCHAR(15))
SQL SECURITY INVOKER
lbl_proc: BEGIN
DECLARE a_id, u_id, a_day, a_month, error INT;
DECLARE us_id, usd_id, usm_id CHAR(48);
DECLARE sep CHAR(1);
DECLARE CONTINUE HANDLER FOR SQLSTATE '23000' SET error = 1;
SET sep = '|';
SET error = 0;
SET a_day = DATE_FORMAT(SUBSTRING(a_date ,1,10), '%Y%m%d');
SET a_month = SUBSTRING(a_day, 1, 6);
/* RETREIVING action.id */
SET a_id = (SELECT `id` FROM `game_actions` WHERE `name` = a_name);
IF a_id IS NULL THEN
SELECT 'error';
LEAVE lbl_proc;
END IF;
/* RETREIVING users.id */
SET u_id = (SELECT `id` FROM `game_users` WHERE `name` = u_name);
IF u_id IS NULL THEN
INSERT INTO `game_users` (name) VALUES (u_name);
SET u_id = LAST_INSERT_ID();
END IF;
/* SETTING summaries ids */
SET us_id = CONCAT(u_id, sep, a_id);
SET usd_id = CONCAT(a_day, sep, u_id, sep, a_id);
SET usm_id = CONCAT(a_month, sep, u_id, sep, a_id);
/* SAVING action AND UPDATING summaries */
SET autocommit = 0;
START TRANSACTION;
INSERT INTO `game_users_actions` (`date`, `user_id`, `action_id`, `chars`, `url`, `ip`)
VALUES (a_date, u_id, a_id, a_chars, url, ip);
INSERT INTO `game_users_summaries` (`id`, `user_id`, `action_id`, `qty`, `chars`)
VALUES (us_id, u_id, a_id, 1, a_chars)
ON DUPLICATE KEY UPDATE qty = qty + 1, chars = chars + a_chars;
INSERT INTO `game_users_summaries_days` (`id`, `day`, `user_id`, `action_id`, `qty`, `chars`)
VALUES (usd_id, a_day, u_id, a_id, 1, a_chars)
ON DUPLICATE KEY UPDATE qty = qty + 1, chars = chars + a_chars;
INSERT INTO `game_users_summaries_months` (`id`, `month`, `user_id`, `action_id`, `qty`, `chars`)
VALUES (usm_id, a_month, u_id, a_id, 1, a_chars)
ON DUPLICATE KEY UPDATE qty = qty + 1, chars = chars + a_chars;
IF error = 1 THEN
SELECT 'error';
ROLLBACK;
LEAVE lbl_proc;
ELSE
SELECT 'success';
COMMIT;
END IF;
END
Anyway, I still think there's some kind of a bug in MySQL, but I consider problem solved.