trigger for a compostite key and update another column - mysql

I have two tables: threads and threadreply.
threads has a composite primary key, (thread_id,reply_id).
When I insert a new row into threadreply, I need to:
update my column threads.reply_count by adding +1 to the previous value; and
insert data into threadreply by adding +1 to the MAX(reply_id) with same thread_id.
Using the following trigger, I was able to add+1 to my reply_id, but couldn't get to update my reply_count:
CREATE TRIGGER addone BEFORE INSERT ON threadreply
FOR EACH ROW BEGIN
SET NEW.reply_id = (
SELECT IFNULL(
(
SELECT MAX(reply_id) + 1
FROM threadreply
WHERE thread_id = NEW.thread_id
),
1
)
);
END
How can I solve this?

I assume that there is a typo in the question and the composite key (thread_id,reply_id) exists in the threadreply table, not in the threads table.
The trigger might look like:
CREATE TRIGGER addone BEFORE INSERT ON threadreply
FOR EACH ROW BEGIN
SET NEW.reply_id = (
SELECT IFNULL(
(
SELECT MAX(reply_id) + 1
FROM threadreply
WHERE thread_id = NEW.thread_id
), 1
)
);
UPDATE threads SET reply_count = reply_count + 1
WHERE thread_id = NEW.thread_id;
END
/
Take a look at this demo: --> http://www.sqlfiddle.com/#!2/1e7bb/2
The trigger and insert statements are on the left side in the schema window, below are shown results of the demo.

Related

update a table after inserting a value in the same table using triggers [duplicate]

I am running a MySQL Query. But when a new row is added from form input I get this error:
Error: Can't update table 'brandnames' in stored function/trigger because it is
already used by statement which invoked this stored function/trigger.
From the code:
CREATE TRIGGER `capital` AFTER INSERT ON `brandnames`
FOR EACH
ROW UPDATE brandnames
SET bname = CONCAT( UCASE( LEFT( bname, 1 ) ) , LCASE( SUBSTRING( bname, 2 ) ) )
What does this error mean?
You cannot change a table while the INSERT trigger is firing. The INSERT might do some locking which could result in a deadlock. Also, updating the table from a trigger would then cause the same trigger to fire again in an infinite recursive loop. Both of these reasons are why MySQL prevents you from doing this.
However, depending on what you're trying to achieve, you can access the new values by using NEW.fieldname or even the old values --if doing an UPDATE-- with OLD.
If you had a row named full_brand_name and you wanted to use the first two letters as a short name in the field small_name you could use:
CREATE TRIGGER `capital` BEFORE INSERT ON `brandnames`
FOR EACH ROW BEGIN
SET NEW.short_name = CONCAT(UCASE(LEFT(NEW.full_name,1)) , LCASE(SUBSTRING(NEW.full_name,2)))
END
The correct syntax is:
FOR EACH ROW SET NEW.bname = CONCAT( UCASE( LEFT( NEW.bname, 1 ) )
, LCASE( SUBSTRING( NEW.bname, 2 ) ) )
A "BEFORE-INSERT"-trigger is the only way to realize same-table updates on an insert, and is only possible from MySQL 5.5+. However, the value of an auto-increment field is only available to an "AFTER-INSERT" trigger - it defaults to 0 in the BEFORE-case. Therefore the following example code which would set a previously-calculated surrogate key value based on the auto-increment value id will compile, but not actually work since NEW.id will always be 0:
create table products(id int not null auto_increment, surrogatekey varchar(10), description text);
create trigger trgProductSurrogatekey before insert on product
for each row set NEW.surrogatekey =
(select surrogatekey from surrogatekeys where id = NEW.id);
#gerrit_hoekstra wrote: "However, the value of an auto-increment field is only available to an "AFTER-INSERT" trigger - it defaults to 0 in the BEFORE-case."
That is correct but you can select the auto-increment field value that will be inserted by the subsequent INSERT quite easily. This is an example that works:
CREATE DEFINER = CURRENT_USER TRIGGER `lgffin`.`variable_BEFORE_INSERT` BEFORE INSERT
ON `variable` FOR EACH ROW
BEGIN
SET NEW.prefixed_id = CONCAT(NEW.fixed_variable, (SELECT `AUTO_INCREMENT`
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA = 'lgffin'
AND TABLE_NAME = 'variable'));
END
I have the same problem and fix by add "new." before the field is updated. And I post full trigger here for someone to want to write a trigger
DELIMITER $$
USE `nc`$$
CREATE
TRIGGER `nhachung_province_count_update` BEFORE UPDATE ON `nhachung`
FOR EACH ROW BEGIN
DECLARE slug_province VARCHAR(128);
DECLARE slug_district VARCHAR(128);
IF old.status!=new.status THEN /* neu doi status */
IF new.status="Y" THEN
UPDATE province SET `count`=`count`+1 WHERE id = new.district_id;
ELSE
UPDATE province SET `count`=`count`-1 WHERE id = new.district_id;
END IF;
ELSEIF old.province_id!=new.province_id THEN /* neu doi province_id + district_id */
UPDATE province SET `count`=`count`+1 WHERE id = new.province_id; /* province_id */
UPDATE province SET `count`=`count`-1 WHERE id = old.province_id;
UPDATE province SET `count`=`count`+1 WHERE id = new.district_id; /* district_id */
UPDATE province SET `count`=`count`-1 WHERE id = old.district_id;
SET slug_province = ( SELECT slug FROM province WHERE id= new.province_id LIMIT 0,1 );
SET slug_district = ( SELECT slug FROM province WHERE id= new.district_id LIMIT 0,1 );
SET new.prov_dist_url=CONCAT(slug_province, "/", slug_district);
ELSEIF old.district_id!=new.district_id THEN
UPDATE province SET `count`=`count`+1 WHERE id = new.district_id;
UPDATE province SET `count`=`count`-1 WHERE id = old.district_id;
SET slug_province = ( SELECT slug FROM province WHERE id= new.province_id LIMIT 0,1 );
SET slug_district = ( SELECT slug FROM province WHERE id= new.district_id LIMIT 0,1 );
SET new.prov_dist_url=CONCAT(slug_province, "/", slug_district);
END IF;
END;
$$
DELIMITER ;
Hope this help someone

How can I change SELECT statement to IF statement in Mysql?

In mysql, I tried to print '-1' if 3 conditions are satisfied.
SELECT '-1'
WHERE not exists(select * from acq_staff where acq_person_id = staffID)
OR not exists(select * from acq_training_course_session where training_course_id = course_id)
OR exists(select * from acq_training_enrolment where acq_staff_acq_person_id = staffID);
But how can I change this SELECT statement to IF statement so that if either those 3 conditions are satisfied, print -1 otherwise I am going to insert a data.
Sorry for not enough information
MySQL INNER JOIN, along with WHERE NOT EXISTS, can be used to determine if there's an existing course, and existing staff, and that staff is enrolled in that course, and if not, INSERT the staff and course id in the enrollment table.
-- create
CREATE TABLE acq_staff (
acq_person_id INTEGER PRIMARY KEY
);
CREATE TABLE acq_training_course_session (
training_course_id INTEGER PRIMARY KEY
);
CREATE TABLE acq_training_enrolment (
training_course_id INTEGER NOT NULL,
acq_staff_acq_person_id INTEGER NOT NULL
);
-- insert
INSERT INTO acq_staff VALUES (1), (2), (3);
INSERT INTO acq_training_course_session VALUES (1), (2), (3), (4);
INSERT INTO acq_training_enrolment VALUES (1,1), (1,2), (2,1), (3,1);
-- fetch
INSERT INTO acq_training_enrolment (training_course_id, acq_staff_acq_person_id)
SELECT 3, 1 WHERE NOT EXISTS
(SELECT *
FROM acq_training_course_session
INNER JOIN acq_training_enrolment
ON acq_training_course_session.training_course_id = acq_training_enrolment.training_course_id
INNER JOIN acq_staff ON acq_training_enrolment.acq_staff_acq_person_id = acq_staff.acq_person_id
WHERE acq_training_course_session.training_course_id = 3
AND acq_staff.acq_person_id = 1)
;
Try it here: https://onecompiler.com/mysql/3yk7xynkg
I guess you can do something like this: How can I simulate a print statement in MySQL?
`mysql>SELECT 'some text' as '';
+-----------+
| |
+-----------+
| some text |
+-----------+
1 row in set (0.00 sec)`
and just instead of some text set -1.
And one more thing i noticed in your question, that part "if those 3 conditions are satisfied" if you want all 3 conditions to be satisfied you need to change OR to AND. Because in your case, with OR, there needs to be satisfied only 1 condition, but with AND all 3 of them need to be satisfied.
maybe you can try that
select
if(
acq_staff.acq_person_id = staffID , '-1' ,
if(
acq_training_course_session.training_course_id = course_id , '-1' ,
if(acq_training_enrolment.acq_staff_acq_person_id = staffID , '-1' , 'not exist')
)
) as "check" from acq_staff , acq_training_course_session , acq_training_enrolment limit 1
The question is about how to conditionally execute an insert query. In pseudo-code, the question asks how to do the following
flag = (SELECT ... WHERE <all the conditions are met>)
IF flag == 1
INSERT INTO ....
ELSE IF flag == -1
DO NOTHING
Now think about it this way
result_set = (SELECT ... WHERE <all the conditions are met>)
# result_set here is the actual rows we want to insert
# length(result_set) > 0 if conditions are met
# length(result_set) == 0 if conditions are not met
INSERT INTO ... (result_set)
or simply
INSERT INTO ... (SELECT ... WHERE <all the conditions are met>)
When <all the conditions are met>, the insert will actually have something to insert. Otherwise, it will have an empty result set so no rows will be inserted.
So use INSERT INTO ... SELECT ... WHERE <all the conditions are met> Syntax to achieve desired results. Unfortunately, this solution does not have a way to return back -1.

mysql trigger error with 2 conditions

I want to add an after insert trigger which will do the following.
The first IF condition works normally, but when it comes to the second everything stops.
Any ideas?
USE `Syslog`;
DELIMITER $$
CREATE TRIGGER `SystemEventsR_AINS` AFTER INSERT ON SystemEventsR FOR EACH ROW
IF
(exists
(select syslogtag from SystemEventsRcounter where syslogtag=
new.syslogtag)
AND
(select simpledate from SystemEventsRcounter
where syslogtag=new.syslogtag)=new.simpledate)
THEN
UPDATE SystemEventsRcounter
SET records=records+1
WHERE SystemEventsRcounter.syslogtag=new.syslogtag;
ELSE INSERT SystemEventsRcounter (simpledate, syslogtag, records) values (new.simpledate,new.syslogtag,1);
END IF
UPDATED:
What you need is INSERT INTO ... ON DUPLICATE KEY.
CREATE TRIGGER `SystemEventsR_AINS`
AFTER INSERT ON SystemEventsR
FOR EACH ROW
INSERT INTO SystemEventsRcounter (simpledate, syslogtag, records)
VALUES (NEW.simpledate, NEW.syslogtag, 1)
ON DUPLICATE KEY UPDATE records = records + 1;
In order for it to work you need to create a unique composite index on (simpledate, syslogtag)
CREATE UNIQUE INDEX idx_u_simpledate_syslogtag
ON SystemEventsRcounter (simpledate, syslogtag);
Here is SQLFiddle demo.
If you wanted it your way then it might look like
DELIMITER $$
CREATE TRIGGER `SystemEventsR_AINS`
AFTER INSERT ON SystemEventsR
FOR EACH ROW
BEGIN
IF (
SELECT COUNT(*) simpledate
FROM SystemEventsRcounter
WHERE syslogtag = NEW.syslogtag
AND simpledate = NEW.simpledate
) > 0 THEN
UPDATE SystemEventsRcounter
SET records = records + 1
WHERE SystemEventsRcounter.syslogtag = NEW.syslogtag;
ELSE
INSERT INTO SystemEventsRcounter (simpledate, syslogtag, records)
VALUES (NEW.simpledate, NEW.syslogtag, 1);
END IF;
END$$
DELIMITER ;
Here is SQLFiddle demo.

Update Trigger : Supertype/subtypes tables

I need some help about triggers. I’m currently developing a platform and a database in order to manage exams at my university. Here is my problem:
I have 1 supertype table, which contains all the persons registered on the platform. I have to be able to make the distinction for each person between the functions “Candidate” and “Examiner”. So I have my 2 subtype tables, one for all the candidates and one for all the examiners. To achieve that, I’m using insert triggers.
In addition, a person can be both Candidate and Examiner, but not at the same time. So after updating the supertype table, I also need a trigger to be able to delete a specific row from one of the two-subtype table and insert the user information on the other.
Here is a simplified design of these 3 tables:
My INSERT trigger :
ALTER TRIGGER [dbo].[role_insert]
ON [dbo].[alemp_persons]
FOR INSERT
AS
DECLARE #random_number int
SELECT #random_number = CAST(CAST(rand() as binary(2)) as int)
BEGIN
INSERT INTO dbo.alemp_candidates
(
id_person, random_number
)
SELECT id_person, # random_number
FROM INSERTED
WHERE function='Candidate'
INSERT INTO dbo.alemp_examiners
(
id_person
)
SELECT id_person
FROM INSERTED
Where function='Examiner'
END
GO
My UPDATE trigger :
ALTER TRIGGER [dbo].[role_update] ON [dbo].[alemp_persons]
AFTER UPDATE
AS
DECLARE #id_person int
DECLARE #newFunction int SELECT #newFunction=function FROM inserted
DECLARE #random_number int SELECT # random_number = CAST(CAST(rand() as binary(2)) as int)
IF #newFunction = 'Candidate'
BEGIN
DELETE
FROM dbo.alemp_examiners
WHERE id_person=#id_person
END
BEGIN
SET IDENTITY_INSERT dbo.alemp_candidates ON;
INSERT INTO dbo.alemp_candidates
(
id_person, random_number
)
SELECT #id_person, random_number
SET IDENTITY_INSERT dbo.alemp_candidates OFF;
END
IF #newFunction = 'Examiner'
BEGIN
DELETE
FROM dbo.alemp_candidates
WHERE id_person=#id_person
END
BEGIN
SET IDENTITY_INSERT dbo.alemp_examiners ON;
INSERT INTO dbo.alemp_examiners
(
id_person
)
SELECT #id_person
SET IDENTITY_INSERT dbo.alemp_examiners Off;
END
GO
As I said above, my INSERT trigger works as I want. However when I want to update the function of one person, I got an error :
Explicit value must be specified for identity column either when IDENTITY_INSERT is set
to ON or when a replication user is inserting into a NOT FOR REPLICATION identity column.
Some simple notes:
1) You should follow Mitch Wheat's advice and rewrite these triggers because inserted and deleted tables could have more than one row. For example, your trigger will have a bad behavior when is executed the next statement UPDATE [dbo].[alemp_persons] SET function = CASE WHEN id_person = 1 THEN 'Candidate' ELSE 'Examiner' END WHERE id_person IN (1,2) if the first person's function is 'Examiner' and the second person's function is 'Candidate'.
2) [dbo].[alemp_persons].function's data type should be [tiny]int or char(1) and not varchar(something greater than 1) (Where function='Examiner').
3) [dbo].[alemp_persons].function column should disallow Nulls.
4) [dbo].[alemp_persons].function column should has a CHECK constraint:
ALTER TABLE [dbo].[alemp_persons]
ADD CONSTRAINT CK_alemp_persons_function_Verify CHECK ( function IN ('Candidate', 'Examiner') );
5) It would be nice to add
a function column to [dbo].[alemp_candidates] and [dbo].[alemp_examiners] tables,
two check constraints on [dbo].[alemp_candidates] (function = 'Candidate') and [dbo].[alemp_examiners] (function = 'Examiner'),
an UNIQUE index on [dbo].[alemp_persons](id_person, function),
two FKs between [dbo].[alemp_candidates/examiners](id_person, function) and [dbo].[alemp_persons](id_person, function).
This way, you can be sure that [dbo].[alemp_candidates] table has only candidates and [dbo].[alemp_examiners] has only examiners and every person can be only candidate or examiner at one time.
6) You should disallow IDENTITY property for id_person columns in [dbo].[alemp_candidates] and [dbo].[alemp_examiners] table (SET IDENTITY_INSERT dbo.alemp_candidates ...).
8) And this statement IF #newFunction = 'Candidate' should raise an error because #newFunction data type is 'INT'.
9) And the AFTER UPDATE trigger on [dbo].[alemp_persons] table will move data between candidates and examiners tables (not tested):
ALTER TRIGGER [dbo].[role_update]
ON [dbo].[alemp_persons]
FOR UPDATE
AS
BEGIN
DECLARE #selected_rows TABLE (
id_person INT PRIMARY KEY, -- or BIGINT, look at alemp_person.id_person data type
new_function VARCHAR(50) NOT NULL -- look at alemp_person.function column data type
);
INSERT #selected_rows (id_person, new_function)
SELECT new.id_person, new.function
FROM inserted as new INNER JOIN deleted as old ON new.id_person = old.id_person
WHERE new.function <> old.function;
MERGER dbo.alemp_candidates AS dest
USING #selected_rows AS src ON dest.id_person = src.id_person
WHEN MATCHED THEN
DELETE
WHEN NOT MATCHED BY TARGET AND src.new_function = 'Candidate' THEN
INSERT (id_person, random_number)
VALUES (src.id_person, CONVERT(BINARY(2), CHECKSUM(NEWID()));
MERGER dbo.alemp_examiners AS dest
USING #selected_rows AS src ON dest.id_person = src.id_person
WHEN MATCHED THEN
DELETE
WHEN NOT MATCHED BY TARGET AND src.new_function = 'Examiner' THEN
INSERT (id_person)
VALUES (src.id_person);
END

Addition in MySQL trigger not working as expected

I'm doing some addition as part of a mysql trigger. The added value is added a to a column on the table. The code is as follows;
BEGIN
IF NEW.Status = 'processed' AND NEW.Success = 1 THEN
INSERT INTO crm_master
(msisdn, last_action_date, source, contract_type, revenue,inc)
VALUES
(new.msisdn,NOW(), 'INC5', new.Contract_Type, revenue = revenue+5, 1)
ON DUPLICATE KEY UPDATE last_action_date = NOW(),
contract_type = new.Contract_Type,
revenue = revenue+5,
inc = 1;
END IF;
END
The column revenue in the table crm_master is set to default of 0
The problem is that I'm getting unexpected results with incorrect values and in some cases 0 even though there should never be a 0 value.
I don't think it's valid reference to default value revenue = revenue+5 in your insert statement. It should look like
INSERT INTO crm_master
(msisdn, last_action_date, source, contract_type, revenue,inc)
VALUES
(new.msisdn,NOW(), 'INC5', new.Contract_Type, DEFAULT(revenue) +5, 1)
ON DUPLICATE KEY UPDATE ....
Or you can simply do
INSERT INTO ....
VALUES
(new.msisdn,NOW(), 'INC5', new.Contract_Type, 5, 1) ...
*Your update part of INSERT ... ON DUPLICATE KEY UPDATE is ok.
INSERT INTO sometable( column ) VALUES ( column = column + 5 );
is equivalent to
INSERT INTO sometable( column ) VALUES ( 0 );
because (column = column+5) evaluates to 0 as column is never equal to column+5.
This syntax is ok for UPDATE query, but for INSERT you should provide an explicit value, like
INSERT INTO sometable( id, column ) VALUES ( 1, 5 )
ON DUPLICATE KEY UPDATE column = column + 5;
which would insert value 5 if there is no row with given id and add 5 to column if there is one.