MySQL Trigger creation after update - mysql

I'm trying to create a trigger in MySQL in order to automatically generate an entry into a table Operations from another table Accounts.
Here my tables :
CREATE TABLE Accounts (
number INT CHECK (number >= 0),
balance REAL CHECK (balance >= 0),
PRIMARY KEY (number)
);
CREATE TABLE Operations (
date DATE,
number INT REFERENCES Accounts(number),
amount REAL,
PRIMARY KEY (date, number)
);
And below, the trigger I'm trying to make work, which generate an ERROR 1064 (42000) :
CREATE TRIGGER OperationTrig
AFTER UPDATE OF balance ON Accounts
REFERENCING OLD ROW AS ooo NEW ROW AS nnn
FOR EACH ROW
WHEN(nnn.balance <> ooo.balance)
INSERT INTO Operations
VALUES(CURDATE(), nnn.number, nnn.balance - ooo.balance);
Where am I wrong ?

I think it's that WHEN clause; I don't recognize it as valid MySQL syntax. Ditto for the REFERENCING clause. (Both are valid Oracle/PostgreSQL, however.)
Try this:
CREATE TRIGGER OperationTrig
AFTER UPDATE ON Accounts
FOR EACH ROW
BEGIN
IF (NEW.balance <> OLD.balance) THEN
INSERT INTO Operations
VALUES(CURDATE(), NEW.number, NEW.balance - OLD.balance);
END IF;
END;

Related

Insert row from a transactional database into a star schema through stored procedure

I need to transform data from transactional database into a star schema. I already created all dimension tables and one fact table (cinjenica) as shown on ERD picture here: left(star-schema) right(transactional database table)
I also created Unique index ON dimension tables so to limit maximum theoretical row count for example in dimenzija_platforma i have UNIQUE index on (widows, linux, mac) atributes, and those atributes can have values 1 or non-one (binary value) which yields 2^3=8 max theoretical rows. Same is done for other dimension tables that will be derived from transactional database table with 29k rows.
Table cinjenica(fact table) contains compound PK/FK and all foreign keys have ON UPDATE CASCADE.
Now to input the data inside my star schema i created following stored procedure:
CREATE DEFINER=`root`#`localhost` PROCEDURE `new_row_to_star_scheme`()
BEGIN
DECLARE i INTEGER;
DECLARE total_row_count INTEGER;
select appid from sppi.steam;
SELECT FOUND_ROWS() into total_row_count;
SET i = 0;
ponavljanje: LOOP
IF i > total_row_count THEN
LEAVE ponavljanje;
END IF;
INSERT INTO dimenzija_datum(ID,datum)
SELECT last_insert_id(),s.release_date
FROM steam as s LIMIT i,1
ON DUPLICATE KEY UPDATE ID=last_insert_id(ID);
SET #row_datum = last_insert_id();
INSERT INTO dimenzija_vlasnik(ID,developer,publisher)
SELECT last_insert_id(),s.developer, s.publisher
FROM steam as s LIMIT i,1
ON DUPLICATE KEY UPDATE ID=last_insert_id(ID);
SET #row_vlasnik = last_insert_id();
INSERT INTO dimenzija_platforme(ID,tekstualno)
SELECT last_insert_id(),s.platforms
FROM steam as s LIMIT i,1
ON DUPLICATE KEY UPDATE ID=last_insert_id(ID);
SET #row_platforme = last_insert_id();
INSERT INTO dimenzija_kategorija(ID,zanr_tagovi,tip_tagovi)
SELECT last_insert_id(),s.genres, s.categories
FROM steam as s LIMIT i,1
ON DUPLICATE KEY UPDATE ID=last_insert_id(ID);
SET #row_kategorija = last_insert_id();
INSERT INTO cinjenica(fk_datum,fk_vlasnik,fk_platforma,fk_kategorija,naziv,positive_rating,negative_rating,avg_playtime,median_playtime,cijena,owners)
SELECT #row_datum, #row_vlasnik, #row_platforme, #row_kategorija, s.name, s.positive_ratings, s.negative_ratings, s.average_playtime, s.median_playtime, s.price, s.owners
FROM steam as s LIMIT i,1
ON DUPLICATE KEY UPDATE ID=last_insert_id(ID);
SET i = i+1;
END LOOP;
END
All other data that isn't inserted into dimensions from shown procedure is created with BEFORE INSERT triggers on each dimension table.
With this stored procedure I can populate my star schema until any of dimension tables encounters duplicate row after that I get Error code 1054 Unknown column 'ID' in 'field list'. After each INSERT INTO if there is duplicate data - ON DUPLICATE KEY UPDATE should update primary key of dimension table(foreign key for fact table) and trigger ON UPDATE CASCADE so in theory it should work. How should I correct my procedure to populate my star schema ?

how to force insert statement to run without inserting any records in values clause

i have an situation where i have a following table.
create table Bulk_Data
(
id int identity(1,1),
UserName varchar(100) not null default 'default value ',
iPartitionID int null
)
also i have an Insert trigger on Bulk_Data.
create trigger dbo.Bulk_Data_IU on Bulk_Data
AFTER INSERT
AS Begin
Merge Bulk_Data p1
using Bulk_Data p2 on p1.id = p2.id
when matched then
update set p1.iPartitionID = right(p2.id,1);
end
condition in above table is like
i have 3 column which are not depended on any values.
1] id is identity auto increment column
2] UserName is set to be default values
3] iPartitionID is based on insert in Trigger.
so my question is how should i insert the records say suppose i do not required
to insert any values in #2 i.e. in column 2 so how should i fire the insert command on table.
because insert command is important for me as i have created Insert trigger.
when i run
insert Bulk_Data(UserName) values('Suraj Sheikh')
it works fine but what if i don't want to insert any UserName.
is this possible ?
please help me out here.
You can use a computed column instead of a trigger that updates all rows for each insert.
create table Bulk_Data
(
id int identity(1,1),
UserName varchar(100) not null default 'default value ',
iPartitionID as id % 10 persisted
)
Use "Instead OF" trigger rather than using "AFTER INSERT" trigger.
INSTEAD-OF triggers are very powerful objects in SQL Server. They allow the developer to divert the database engine to do something different than what the user is trying to do.
In this way, when an insert query is fired on that table, insertion will not happen and whatever statements are written in trigger will actually applied to db.
Limitation - INSTEAD OF DELETE/UPDATE triggers cannot be defined on a table that has a foreign key with a cascade on DELETE/UPDATE action defined but Insert Trigger can be defined.

remove gaps in auto increment

Say I have a MySQL table with an auto incrementing id field, then I insert 3 rows. Then, I delete the second row. Now the id's of the table go 1,3. Can I get MySQL to correct that and make it 1,2 without having to write a program to do so?
MySQL won't let you change the indexing of an Auto-Index column once it's created. What I do is delete the Auto-Index column and then add a new one with the same name, mysql will index the newly generated column with no gaps. Only do this on tables where the Auto-Index is not relevant to the rest of the data but merely used as a reference for updates and deletes.
For example I recently did just that for a table containing proverbs where the Auto-Index column was only used when I updated or deleted a proverb but I needed the Auto-Index to be sequential as the proverbs are pulled out via a random number between 1 and the count of the proverbs, having gaps in the sequence could have led to the random number pointing to a non-existant index.
HTH
Quoting from The Access Ten Commandments (and it can be extensible to other RDBMS: "Thou shalt not use Autonumber (or Auto Incremental) if the field is meant to have meaning for thy users".
The only alternative I can think of (using only MySQL) is to:
Create a trigger that adds the row number to a column (not the primary key)
Create a procedure to delete rows and update the row number (I couldn't make this work with triggers, sorry)
Example:
create table tbl_dummy(
id int unsigned not null auto_increment primary key,
row_number int unsigned not null default 0,
some_value varchar(100)
);
delimiter $$
-- This trigger will add the correct row number for each record inserted
-- to the table, regardless of the value of the primary key
create trigger add_row_number before insert on tbl_dummy
for each row
begin
declare n int unsigned default 0;
set n = (select count(*) from tbl_dummy);
set NEW.row_number = n+1;
end $$
-- This procedure will update the row numbers for the records stored
-- after the id of the soon-to-be-deleted record, and then deletes it.
create procedure delete_row_from_dummy(row_id int unsigned)
begin
if (select exists (select * from tbl_dummy where id = row_id)) then
update tbl_dummy set row_number = row_number - 1 where id > row_id;
delete from tbl_dummy where id = row_id;
end if;
end $$
delimiter ;
Notice that you'll be forced to delete the records one by one, and you'll be forced to get the correct primary key value of the record you want to delete.
Hope this helps

How to assign a foreign key value using a a before insert trigger

I have a scenario like this:
There are two tables table1 and table2. The table1 has a primary key pkey and table2 has a foreign key fkey now during an insert if the foreign key is provided the value should be inserted as it is. Otherwise, it has to get a primary key from table1 using some computation and determine the foreign key to be inserted. How do i do this??
I am using MySql 5.0
EDIT
In my scenario, table1 holds the billing details, that is, the table1 has the bills and the total amount to be paid. The customer pays some amount of the total outstanding balance or will pay for a particular bill. What i want to do here is. When i am not provided with a bill_id (which is primary key in table1 and foreign key in table2) i would like to search for the oldest bill that is due in table1 and deduct the amount due and further deduct the remaining amount if any from the next bill in the billed order. I want to do this in the database layer rather than the upper layer. So when an insert is being done without a value for the foreign key the value should be retrieved and placed by the trigger or otherwise directly inserted. How do i achieve this?
Using the answers provided here, i tried this:
CREATE DEFINER=`root`#`localhost` TRIGGER `inflow_pay_done_insert` BEFORE INSERT ON `inflow_pay_done` FOR EACH ROW BEGIN
DECLARE pkey INT;
SET pkey = (SELECT bill_id from inflow_bills where payment_stat = 0 and rs_id = NEW.rs_id order by time_stamp limit 1);
SET NEW.bill_id = IF(NEW.bill_id , NEW.bill_id , pkey);
UPDATE raw_mat_sup rms SET rms.outstanding_bal_payable = rms.outstanding_bal_payable - NEW.amount where rms.rs_id = NEW.rs_id;
END|
and i am getting the following error when i am trying to insert in inflow_pay_done:
/* SQL Error (1048): Column 'bill_id' cannot be null */
you could use a subquery in the BEFORE INSERT trigger for this..
DELIMITER |
DROP TRIGGER `inflow_pay_done_insert`|
CREATE TRIGGER `inflow_pay_done_insert` BEFORE INSERT ON `inflow_pay_done`
FOR EACH ROW
BEGIN
UPDATE raw_mat_sup rms
SET rms.outstanding_bal_payable = rms.outstanding_bal_payable - NEW.amount
WHERE rms.rs_id = NEW.rs_id;
NEW.bill_id = IF(NEW.bill_id,
/* if "bill_id" is provided in INSERT statement, use provided value */
NEW.bill_id,
/* if not, query other table for the correct value */
( /* this subquery is just an example, put your own query here*/
SELECT bill_id FROM inflow_bills
/* find customers newest bill based on newest date and customer id */
WHERE payment_stat = 0 AND rs_id = NEW.rs_id
ORDER BY time_stamp DESC LIMIT 1
)
);
END;
|
delimiter;
UPDATE
Because of a MySQL Bug, this will only work when the column is allowed to be NULL and there is no constraint on the column (-> foreign key). The reason is that MySQL, unlike other DBMS, checks for constraints before a BEFORE INSERT trigger is executed and effectively avoids the execution of the trigger which would correct the data to insert.
The only solution for this, until the behaviour of MySQL changes, is to use a STORED PROCEDURE instead of plain INSERT. The stored procedure is then called with the values that should be inserted. In the procedure, the data correction (like in this case: selecting the right bill_id) is done and then INSERT is executed from within the stored procedure.
UPDATE II
This bug seems to be fixed in 5.7.1. Changelog says:
If a column is declared as NOT NULL, it is not permitted to insert
NULL into the column or update it to NULL. However, this constraint
was enforced even if there was a BEFORE INSERT (or BEFORE UPDATE
trigger) that set the column to a non-NULL value. Now the constraint
is checked at the end of the statement, per the SQL standard.

help with mysql triggers (checking values before insert)

hi I'm quite new to mysql and I'm trying to figure out how to use triggers.
what I'm trying to do:
I have 2 tables, max and sub_max, when I insert a new row to sub_max I want to check if the SUM of the values with the same foreign_key as the new row are less than the value in the max table. I think this sounds confusing so here are my tables:
CREATE TABLE max(
number INT ,
MaxAmount integer NOT NULL)
CREATE TABLE sub_max(
sub_number INT ,
sub_MaxAmount integer NOT NULL,
number INT,
FOREIGN KEY ( number ) REFERENCES max( number ))
and here is my code for the trigger, I know the syntax is off but this is the best I could do from looking up tutorials.
CREATE TRIGGER maxallowed
after insert on sub_max
FOR EACH ROW
BEGIN
DECLARE submax integer;
DECLARE maxmax integer;
submax = select sum(sub_MaxAmount) from sub_max where sub_number = new.sub_number;
submax = submax + new. sub_MaxAmount;
maxmax = select MaxAmount from max where number = new.number ;
if max>maxmax
rollback?
END
I wanted to know if I'm doing this remotely correctly. Thanks in advance.
Caveat - I am also learning triggers.
For the section:
if max>maxmax
rollback?
Would the syntax be something like?:
IF max > maxmax THEN
DELETE the id of the new record?
ELSE
do nothing?
END IF;