How to use MYSQL Trigger with Cursor - mysql

Want to Insert multiple rows into table "txn_dbtransactionnotification" when new data is inserted/update into the "Txn_Sales" table.
Suppose 5 rows are present into the transaction table "Txn_Sales" and 1 row into the master table "Mst_Sales".
So when Data into the master table "Mst_Sales" is modified then related data into the table "Txn_Sales" must be modified and the update be sent to the table "txn_dbtransactionnotification" so how can I use a Cursor inside a Trigger so when data in table "Mst_Sales" is modified then data into the table "Txn_Sales" must be modified row by row.

Cursor support is incomplete in MySQL's stored routine language. It does not support UPDATE ... WHERE CURRENT OF CURSOR like some other brands of SQL implementation.
But in many cases, it's not necessary to update row by row. It's simpler to update a batch. Think of updating sets of rows, instead of row by row.
For example:
CREATE TRIGGER mytrigger AFTER UPDATE ON Mst_Sales
FOR EACH ROW
BEGIN
-- update a set of rows related to the same product that
-- spawned this trigger.
UPDATE Txn_Sales SET price = NEW.price WHERE product = NEW.product;
-- and enter a record in the notifications table
INSERT INTO txn_dbtransactionnotification ...;
END
I had to imagine what type of update you were talking about, because you described the problem in such an abstract way. In a real-life scenario, I don't think changing the price of a product would apply retroactively to past transactions. So the example above is only to show the technique, not the real code you would use.

Related

Npm mysql getting the insertId of each row when inserting more than one row [duplicate]

Short version
Would someone provide an example of this? There are 3 SQL tables. Using INSERT ... SELECT, take data from table 1 and insert it into table 2. Then, INSERT rows into table 3, using the auto-increment id of each table 2 row just inserted using that INSERT ... SELECT statement.
INSERT ... SELECT creates multiple rows but you cannot obtain the auto-increment ID from them, for use in a subsequent INSERT statement.
Expanded version
I'm looking for an efficient way to use the auto increment IDs, created from an INSERT ... SELECT, in a second INSERT.
Imagine this scenario in a warehouse.
The warehouse receives a pallet of goods from a supplier. The pallet contains multiple individual items, which must be dispatched to different customers. The pallet is booked in, broken down and checked. Each item is then assigned to the correct customer and marked as "ready". At this point, each item is dispatched with the dispatch status recorded per customer. Each Customer's account balance is reduced by a given value based on the item.
The issue is linking the account reduction to the item dispatch. There are 3 tables:
GoodsIn: records the pallet arrival from the supplier
CREATE TABLE GoodsIn ('InID' 'CustomerID', 'ItemSKU_ID', 'HasBeenChecked')
GoodsOut: records the SKU dispatch to the Customer
CREATE TABLE GoodsOut ('OutID', 'CustomerID', 'ItemSKU_ID', 'DateDispatched')
Account: records each Customer transaction/balance
CREATE TABLE Ledger ('LedgerID', 'BalanceClose', 'AdjustmentAmount', 'CustomerID', 'ActionID')
(I've massively simplified this - please accept that GoodsIn and GoodsOut cannot be combined)
When an SKU is marked as ready for dispatch, I can use the following to automatically update the Ledger balance, taking the last balance row per customer and updating it
INSERT INTO Ledger (BalanceClose, AdjustmentAmount, CustomerID)
SELECT Ledger.BalanceClose +
(SELECT #Price:=ItemSKUData.ItemPrice FROM ItemSKUData WHERE ItemSKUData.ItemSKU_ID = GoodsIn.ItemSKU_ID) AS NEWBALANCECLOSE,
#Price AS ADJUSTMENTAMOUNT,
Ledger.CustomerID
FROM Ledger
INNER JOIN GoodsIn ON GoodsIn.CustomerID = Ledger.CustomerID
WHERE GoodsIn.HasBeenChecked = TRUE
AND Ledger.LedgerID IN (SELECT MAX(Ledger.LedgerID) FROM Ledger GROUP BY Ledger.CustomerID)
This all works absolutely fine - I get a new Ledger row, with the updated BalanceClose, for each GoodsIn row where GoodsIn.HasBeenChecked = TRUE. Each of these Ledger rows gets an auto-increment Ledger.LedgerID on INSERT.
I can then do pretty much the same code to INSERT into the GoodsOut table. Again as with Ledger, GoodsOut.OutID is an auto-increment ID.
I now need to link those Ledger rows (Ledger.ActionID) to the GoodsOut.OutID. This is the purpose of Ledger.ActionID - it needs to map to each GoodsOut.OutID, so that the reduction of the Ledger balance is linked to the action of sending the goods to the customer.
In theory, if this was a single INSERT and not an INSERT SELECT, I would simply take the GoodsOut.LAST_INSERT_ID() and use it on the INSERT INTO Ledger.
But because I'm using an INSERT ... SELECT, I can't get the auto-increment ID of each row.
The only way I can see to do this is to use a dummy column in the GoodsOut table, and store the GoodsIn.InID in it. I could then get the GoodsOut.OutID using a WHERE in the INSERT ... SELECT for the Ledger.
It doesn't feel very elegant and safe though.
So this is my question. I need to link table A to table B using table B's auto-increment ID, when all rows in BOTH table A and table B are created using INSERT ... SELECT.
You're right, when you do INSERT...SELECT for batch inserts, you don't have easy access to the auto-increment id. LAST_INSERT_ID() returns only the first id generated.
One documented behavior of bulk inserts is that the id's generated are guaranteed to be consecutive, because bulk inserts lock the table until the end of the statement.
https://dev.mysql.com/doc/refman/5.7/en/innodb-auto-increment-handling.html says:
innodb_autoinc_lock_mode = 1 (“consecutive” lock mode)
This is the default lock mode. In this mode, “bulk inserts” use the special AUTO-INC table-level lock and hold it until the end of the statement. This applies to all INSERT ... SELECT, REPLACE ... SELECT, and LOAD DATA statements. Only one statement holding the AUTO-INC lock can execute at a time.
This means if you know the first value generated, and the number of rows inserted (which you should be able to get from ROW_COUNT()), and the order of rows inserted, then you can reliably know all the id's generated.
The MySQL JDBC driver relies on this, for example. When you do a bulk insert, the full list of id's generated is not returned to the client (that is, the JDBC driver), but the driver has a Java method to return the full list. This is accomplished by Java code inferring the values, and assuming they are consecutive.

How do I get the auto incremented id in a before insert trigger for mySQL?

I need to create a trigger in mySQL that uses the auto incremented id to fill another column.
Let's say the id is "12", i need another column to be automatically filled with "12-xxx".
I'm trying to do that using an before insert trigger but it is not working.
CREATE TRIGGER TR_CARTAO_BI BEFORE INSERT ON CARTAO FOR EACH ROW
BEGIN
SET NEW.NUMERO = CONCAT(NEW.IDCARTAO, '-XXX');
END $$
It seems that I can't use the id in the before insert trigger because it hasn't been generated yet;
I heard about the query "SELECT AUTO_INCREMENT FROM INFORMATION_SCHEMA.TABLES..." that returns the next auto increment element but sometimes it works, sometimes it doesn't;
I tried to use an after insert trigger but apparently you can't change the row that you are inserting in the after insert trigger;
From your question it looks like you've exhausted all possible routes (I would have initially suggested the SELECT AUTO_INCREMENT query, but this isn't reliable for you) that avoid using a secondary table.
So, as a hacky work-around, you could try this instead... You could use an AFTER INSERT trigger to create a row in a secondary table, which would have the ID of the row you just created and your secondary column with the ID-XXX value. On the secondary table, set up an AFTER INSERT trigger to update your primary table row with the ID-XXX value.
This could be expensive, depending on your use-case and velocity of transactions etc. But I thought I'd offer lateral thinking...

preserving the first value assigned to a field in mysql

I have a MYSQL database with a table like:
Id myId Description
ABD1 0 some desc
ABD2 1 some desc
....
myId is an autoincremented column. I need to create a mysql trigger that will prevent anyone from changing the first myId value assigned to a row at the time of its insertion. How can this be done in mysql? I was thinking:
CREATE TRIGGER upd_check BEFORE UPDATE ON myTable
FOR EACH ROW
BEGIN
NEW.myId = OLD.myID
END
Could this be enough? If so, is this trigger going to run for all rows of my table? only for the new ones? I just need for one row.
Thx
To answer your question directly:
Could this be enough?
Yes, this will make sure any UPDATE will not change the value of your myID column. It will always reset that column to the value it was prior to the UPDATE.
If so, is this trigger going to run for all rows of my table? only for the new ones?
The answer is in the manual page https://dev.mysql.com/doc/refman/5.7/en/trigger-syntax.html which says:
The statement following FOR EACH ROW defines the trigger body; that is, the statement to execute each time the trigger activates, which occurs once for each row affected by the triggering event.
In other words, the trigger executes once for each row matching the condition in your UPDATE's WHERE clause.
It will not apply to every row in the table—unless your WHERE clause matches every row.

Why this query is not working

I'm new to SQL so I don't understand why this this query is not working. Thank you in advance
CREATE VIEW temp AS
SELECT return_date_time, renting_date_time
FROM renting;
CREATE TRIGGER charge_calc AFTER UPDATE ON renting.return_date_time
FOR EACH ROW
BEGIN
UPDATE renting
SET new.charge =(select m.charge_per_day
from movies m,renting as r
where (m.id=r.id_movie))*datediff(temp.return_date_time,temp.renting_date_time);
END
DATA DIAGRAM
I don't think you should run the update of the charge on that way; if you're wanting to set the values of a row in a trigger that is firing because the row has been updated, all you need to do is
SET new.columnname = somevalue
To set the value of columnname on the updated row. You don't kick off another update of the table within the update trigger that is firing upon update of the table.
Next, you seem to be joining all the rows in movies together with all the rows in renting, which will surely return hundreds or thousands of rows, and you're trying to set one value. This is broken logic: which of the thousands of movie rows do you want MySQL to pick? It won't choose; the logic is broken
Step back for moment and consider: this is an update trigger of the renting table. It fires for every row updated and the row being updated is accessible by the new. specifier. There is a new.movie_id property - that's the id of the movie being updated right now. If you want some data out of the movies table, select it based on the movie id in the new row (the row being updated) I.e. new.movie_id
You don't need the temp view either - if you want to know the return date, surely that is also part of the new. row
All in, this trigger should probably be a single line along the following idea:
SET new.charge = (select rate from movies where id = new.movie_id) * datediff(date_rented, date_returned, day)
As a side comment, I think the front end app should be doing this, not triggers in the database

How to insert the value derived from AUTO_INCREMENT into another column in the same INSERT query?

I have an id column which is a primary key with AUTO_INCREMENT. I need the value that is generated to be inserted into the id column, as well as another column (which isn't set to AUTO_INCREMENT, and isnt unique.
Currently I use the mysqld_isnert_id() function to get the id, and simply run an update query after the insert, but I was wondering if I could do this without running the 2nd update query.
after insert Trigger?
If I recall correctly, the automatically generated ID isn't even created until after the insert has been performed. Your two query way is probably the only way without diving into perhaps a stored procedure.
You could define a trigger along the lines of:
delimiter //
CREATE TRIGGER upd_check AFTER INSERT ON mainTable
FOR EACH ROW
BEGIN
UPDATE dependingTable
SET dependingTable.column = NEW.id
END;//
delimiter ;
I am not exactly sure WHEN the AUTO_INCREMENT value is generated, but you could try the following, since if it works it'll save you an update (If the column you want the value replicated to is in the same row as the inserted row):
CREATE TRIGGER upd_check BEFORE INSERT ON mainTable
FOR EACH ROW
SET NEW.column = NEW.id
The only way I can see you doing it with a single query is to use the information schema. In the information schema there is a table called 'tables', there you access the column auto_increment. That contains the NEXT insert id for that table, you can access this via a nested select, just give the user used to connect to the database read access to that table. This will only work with innodb engines as far as I can tell as that way the nested select you'll do to populate the second id field will be part of the greater transaction of the insert.
That's what your query might look like:
INSERT INTO fooTable VALUES (0, (SELECT AUTO_INCREMENT FROM information_schema.TABLES));
Also if you're worried about read access and security issues, just remember this is the same info you can get by running a show table status. Speaking of which, I tried to see if you could project the show commands/queries via a select and you can't, which totally sucks, because that would have been a much cleaner solution.