How do I create a trigger with Inner Joins - mysql

I'm trying to create a trigger to populate an aud Loan Table when there is an insert in the Loan table. I want this aud Table to have data from both the Loan table and another table, so I'm trying to set variables that get this data.
When creating the trigger I'm getting the error "Unknown system variable 'var1'"
This is the database layout:
https://cdn.discordapp.com/attachments/582912082450710528/583696750322253824/unknown.png
DELIMITER $$
CREATE TRIGGER Loan_Insert AFTER INSERT ON loan
FOR EACH ROW
BEGIN
SET var1 =
(SELECT loan_type.type_of_loan
FROM loan INNER JOIN loan_type ON
loan.loan_type_idloan_type = loan_type.idloan_type
AND
loan.loan_type_idapp_type = loan_type.idapp_type
WHERE loan.loan_type_idloan_type = new.loan_type_idloan_type
AND loan.loan_type_idapp_type = new.loan_type_idapp_type);
SET var2 =
(SELECT loan_type.app_type
FROM loan INNER JOIN loan_type ON
loan.loan_type_idloan_type = loan_type.idloan_type
AND
loan.loan_type_idapp_type = loan_type.idapp_type
WHERE loan.loan_type_idloan_type = new.loan_type_idloan_type
AND loan.loan_type_idapp_type = new.loan_type_idapp_type);
INSERT INTO Aud_Loan(bk_Loan, type_of_loan, type_of_loan_description, application_type,
application_type_description, insert_date)
VALUES(new.idloan, new.loan_type_idloan_type, var1, new.loan_type_idapp_type, var2,CURDATE());
END $$

Local variables must be DECLAREd. docs
When using SET, the right hand side must only return one value (single result with single field).
In you particular case, you can probably tweak the queries used in your SET statements like this:
SET var1 = (
SELECT loan_type.type_of_loan
FROM loan_type
WHERE loan_type.idloan_type = NEW.loan_type_idloan_type
AND loan_type.idapp_type = NEW.loan_type_idapp_type
);
and you can probably even reduce it to one query with SELECT INTO:
SELECT loan_type.type_of_loan, loan_type.app_type
INTO var1, var2
FROM loan_type
WHERE loan_type.idloan_type = NEW.loan_type_idloan_type
AND loan_type.idapp_type = NEW.loan_type_idapp_type
;

Related

SQL: Update a table column with data in column retrieved from a view in same procedure

I'm trying to do a select and update within the same local procedure. The idea is to retrieve
the number of ordered products from a specific order in the view (Produktantal) and then updating the stock numbers (antal) from the table "produktlager" with the data retrieved from the view.
I'm receiving the follow error message Error: ER_BAD_FIELD_ERROR: Unknown column 'Produktantal' in 'field list'. I don't seem to be available to access the column in the update part. How can I do this?
DROP PROCEDURE IF EXISTS update_stock_status;
DELIMITER ;;
CREATE PROCEDURE update_stock_status(
p_orderid INT
)
BEGIN
SELECT
Produktid,
Orderid,
Produktantal,
Lagerantal,
Hylla
FROM v_products2order_w_products2lager
WHERE Orderid = p_orderid;
UPDATE produkt2lager
SET
antal = antal - Produktantal
WHERE
produktid = Produktid
AND
hylla = Hylla;
END
;;
DELIMITER ;
The view:
DROP VIEW IF EXISTS v_products2order_w_products2lager;
CREATE VIEW v_products2order_w_products2lager AS
SELECT
p2o.produktid AS "Produktid",
p2o.orderid AS "Orderid",
p2o.antal AS "Produktantal",
p2l.antal AS "Lagerantal",
p2l.hylla AS "Hylla"
FROM
produkt2order AS p2o
LEFT OUTER JOIN produkt2lager AS p2l ON p2o.produktid = p2l.produktid;
The two statements in your procedure are executed independently, and the second statement cannot in any way access the results of the first one.
I suspect that you want the update ... join syntax:
UPDATE produkt2lager p
INNER JOIN v_products2order_w_products2lager o
ON o.produktid = p.produktid
AND o.hylla = p.hylla
SET p.antal = p.antal - o.produktantal
WHERE o.oderid = p_order_id
I think you want an UPDATE with a JOIN:
UPDATE produkt2lager l
v_products2order_w_products2lager v
ON l.produktid = v.produktid and
l.hylla = v.hylla
SET l.antal = l.antal - v.Produktantal;

MySQL - 3 updates need to be run 1 by 1 in Procedure

I have to run 3 updates in one procedure. My 3 separates update work well when I call them one bye one in php but I would like to have them regroup to perform only one call. My procedure is
create
definer = root#localhost procedure UpdateScorePredictionParticipant(IN predPts int(10), IN predId int(10), IN partId int(10))
BEGIN
UPDATE predictions
SET pred_pts = #predPts
WHERE pred_game_id = #predId and participant_id = #partId;
UPDATE ranking
SET pts_wildcard = (SELECT sum(pred_pts)
FROM predictions
INNER JOIN games g on predictions.pred_game_id = g.id
WHERE predictions.participant_id = #partId)
WHERE participant_id = #partId;
UPDATE ranking
SET total = (pts_wildcard+pts_conference+pts_division+pts_pred_sb+pts_sb)
WHERE participant_id = #partId;
END;
How could I perform those 3 updates 1 by 1 but in the same call.
Thanks,
As you are updating 3 different tables, you will need to do three separate updates. However if you put them in a procedure, you only need call the procedure from the PHP code.
To fix the procedure code, remove the # from the used parameter names.
It is also a good practice to prefix the parameters, so they will not mix up with the column names.
Last thing you can do, is to use a transaction inside the procedure, so all updates complete or if some of them fail, none of them gets done. This ensures the atomicity in the operation.
create procedure UpdateScorePredictionParticipant(
in_predPts int,
in_predId int,
in_partId int
)
BEGIN
START TRANSACTION;
UPDATE predictions
SET pred_pts = in_predPts
WHERE pred_game_id = in_predId and participant_id = in_partId;
UPDATE ranking
SET pts_wildcard = (
SELECT sum(pred_pts)
FROM predictions
INNER JOIN games g on predictions.pred_game_id = g.id
WHERE predictions.participant_id = in_partId
)
WHERE participant_id = in_partId;
UPDATE ranking
SET total = (pts_wildcard+pts_conference+pts_division+pts_pred_sb+pts_sb)
WHERE participant_id = in_partId;
COMMIT;
END

Summing the values from the 2nd table based on ID of the 1st table and inserting values in first table

How to in first table where it says UkupnaCena insert sum value of column Cena where RacunID in first table is equal to RacunID in other table. For example if RacunID is equal to 1 in first table, I want its UkupnaCena to be equal to sum of all values in column Cena where RacunID is 1.
Tables:
My procedure so far:
Create procedure sp_RacunUpdate(
#pRacunID int,
#pStatusRacuna nvarchar(50),
#pDatum nvarchar(20),
#pOpis nvarchar(200),
#pMesto nvarchar(50),
#pKupacID int
)
as begin
Declare #pUkupnaCena decimal(20,2)
select #pUkupnaCena=sum(Cena) from Stavka
inner join Racun
on Racun.RacunID=Stavka.RacunID
Where Racun.RacunID=Stavka.RacunID
group by Stavka.RacunID
begin transaction
UPDATE Racun
SET StatusRacuna=#pStatusRacuna, Datum=#pDatum, Opis=#pOpis,Mesto=#pMesto,UkupnaCena=#pUkupnaCena,KupacID=#pKupacID
WHERE RacunID=#pRacunID
IF ##ERROR <> 0
BEGIN
ROLLBACK
END
ELSE
BEGIN
COMMIT
END
END
GO
You can modify the update query to something like this
UPDATE Racun from Racun
SET UkupnaCena=(select sum(Cena) from Stavka s where s.RacunID= Racun.RacunID), Datum=#pDatum, Opis=#pOpis,Mesto=#pMesto,KupacID=#pKupacID
WHERE RacunID=#pRacunID
I believe you want a correlated subquery. In MySQL, this would look like:
UPDATE Racun r
SET StatusRacuna = #pStatusRacuna,
Datum = #pDatum,
Opis = #pOpis,
Mesto = #pMesto,
KupacID = #pKupacID,
UkupnaCena = (SELECT SUM(s.Cena) FROM Stavka s WHERE s.RacunID = r.RacunId)
WHERE RacunID = #pRacunID;
However, your code does not look like MySQL; it looks more like SQL Server. In either database, you can do:
UPDATE Racun
SET StatusRacuna = #pStatusRacuna,
Datum = #pDatum,
Opis = #pOpis,
Mesto = #pMesto,
KupacID = #pKupacID,
UkupnaCena = (SELECT SUM(s.Cena) FROM Stavka s WHERE s.RacunID = Racun.RacunId)
WHERE RacunID = #pRacunID;
The only difference here is removing the table alias in the outer query.

MySQL - Update Column Value with Function and Trigger Before Insert

i'm trying to update 2 column with trigger before insert, but i have unexpected result. i insert some data and the 2 last column will automatically inserted with values, here my first attempt
see? the last 2 column is null even i set some trigger and function to fill that columns automatically. Here my second attempt WITH EXACTLY SAME DATA
the last 2 column is filled with data, but i don't understand why the first attempt is fail?
here my trigger i use in the column total_harga
CREATE TRIGGER `set_total_harga` BEFORE INSERT ON `tbl_transaksi_detail`
FOR EACH ROW BEGIN
set NEW.total_harga = hitungTotalHargaPerItem(NEW.qty, NEW.harga_satuan);
END
trigger for column harga_satuan
CREATE TRIGGER `set_harga_satuan` BEFORE INSERT ON `tbl_transaksi_detail`
FOR EACH ROW BEGIN
set NEW.harga_satuan = set_Harga_Unit(NEW.unit, NEW.id_barang, NEW.no_transaksi);
END
function set_Harga_Unit
BEGIN
DECLARE
q,
id_toko INT;
SET
id_toko =(
SELECT DISTINCT
`tbl_transaksi`.`id_toko`
FROM
`tbl_transaksi`,
`tbl_transaksi_detail`
WHERE
`tbl_transaksi`.`no_transaksi` = no_trans
); IF unit = "PCS" THEN
SET
q =(
SELECT
`tbl_harga_jual`.`harga_pcs`
FROM
`tbl_harga_jual`
WHERE
`tbl_harga_jual`.`id_barang` = id_brg AND `tbl_harga_jual`.`id_toko` = id_toko
); RETURN q; ELSEIF unit = "PAK" THEN
SET
q =(
SELECT
`tbl_harga_jual`.`harga_pak`
FROM
`tbl_harga_jual`
WHERE
`tbl_harga_jual`.`id_barang` = id_brg AND `tbl_harga_jual`.`id_toko` = id_toko
); RETURN q; ELSEIF unit = "KARTON" THEN
SET
q =(
SELECT
`tbl_harga_jual`.`harga_karton`
FROM
`tbl_harga_jual`
WHERE
`tbl_harga_jual`.`id_barang` = id_brg AND `tbl_harga_jual`.`id_toko` = id_toko
); RETURN q;
END IF; RETURN q;
END
function hitungTotalHargaPerItem
BEGIN
DECLARE hasil int;
set hasil = qty * harga_satuan;
RETURN hasil;
END
The root cause serms to be the select that sets id_toko variable's value:
SET
id_toko =(
SELECT DISTINCT
`tbl_transaksi`.`id_toko`
FROM
`tbl_transaksi`,
`tbl_transaksi_detail`
WHERE
`tbl_transaksi`.`no_transaksi` = no_trans
);
In the select you inner join tbl_transaksi_detail (the table with the trigger in question) on another table. However, in the 1st case tbl_transaksi_detail is still empty (the trigger is before insert), therefore id_toko variable is set to null.
This will result q being null, which in turn results in the entire calculation set to null.
In the 2nd case there is already a record inserted into tbl_transaksi_detail table, therefore the calculation returns a non null value. But it returns the correct values only because the 1st and 2nd records' details are exactly the same.
I do not really understand that select that calculates id_toko anyway. If that is a transaction id, then you may use last_insert_id() if it is auto increment and the transaction record has just been created or max(id_toko) to get the highest value of id_toko (this is not multi user safe).
it seem i have mistaken select query in function set_Harga_Unit, based on clues from #Shadow
SET
id_toko =(
SELECT DISTINCT
`tbl_transaksi`.`id_toko`
FROM
`tbl_transaksi`,
`tbl_transaksi_detail`<<== I DON'T NEED THIS
WHERE
`tbl_transaksi`.`no_transaksi` = no_trans
); IF unit = "PCS" THEN
when first insert in tbl_transaksi_detail, the value no_transaksi is null because i use trigger before insert in empty table (tbl_transaksi_detail), so i remove tbl_transaksi_detail from query
SET
id_toko =(
SELECT DISTINCT
`tbl_transaksi`.`id_toko`
FROM
`tbl_transaksi`
WHERE
`tbl_transaksi`.`no_transaksi` = no_trans
); IF unit = "PCS" THEN
now it working, thanks everybody!

Finding min and max value of the table in a constant time

I have a table which contains relative large data,
so that it takes too long for the statements below:
SELECT MIN(column) FROM table WHERE ...
SELECT MAX(column) FROM table WHERE ...
I tried index the column, but the performance still does not suffice my need.
I also thought of caching min and max value in another table by using trigger or event.
But my MySQL version is 5.0.51a which requires SUPER privilege for trigger and does not support event.
It is IMPOSSIBLE for me to have SUPER privilege or to upgrade MySQL.
(If possible, then no need to ask!)
How to solve this problem just inside MySQL?
That is, without the help of OS.
If your column is indexed, you should find min(column) near instantly, because that is the first value MySQL will find.
Same goes for max(column) on an indexed column.
If you cannot add an index for some reason the following triggers will cache the MIN and MAX value in a separate table.
Note that TRUE = 1 and FALSE = 0.
DELIMITER $$
CREATE TRIGGER ai_table1_each AFTER INSERT ON table1 FOR EACH ROW
BEGIN
UPDATE db_info i
SET i.minimum = LEAST(i.minimum, NEW.col)
,i.maximum = GREATEST(i.maximum, NEW.col)
,i.min_count = (i.min_count * (new.col < i.minumum))
+ (i.minimum = new.col) + (i.minimum < new.col)
,i.max_count = (i.max_count * (new.col > i.maximum))
+ (i.maximum = new.col) + (new.col > i.maximum)
WHERE i.tablename = 'table1';
END $$
CREATE TRIGGER ad_table1_each AFTER DELETE ON table1 FOR EACH ROW
BEGIN
DECLARE new_min_count INTEGER;
DECLARE new_max_count INTEGER;
UPDATE db_info i
SET i.min_count = i.min_count - (i.minimum = old.col)
,i.max_count = i.max_count - (i.maximum = old.col)
WHERE i.tablename = 'table1';
SELECT i.min_count INTO new_min_count, i.max_count INTO new_max_count
FROM db_info i
WHERE i.tablename = 'table1';
IF new_max_count = 0 THEN
UPDATE db_info i
CROSS JOIN (SELECT MAX(col) as new_max FROM table1) m
SET i.max_count = 1
,i.maximum = m.new_max;
END IF;
IF new_min_count = 0 THEN
UPDATE db_info i
CROSS JOIN (SELECT MIN(col) as new_min FROM table1) m
SET i.min_count = 1
,i.minimum = m.new_min;
END IF;
END $$
DELIMITER ;
The after update trigger will be some mix of the insert and delete triggers.