MySQL trigger before insert update column with new auto-increment id - mysql

I'd like to update a column in my table with the country code and the new id which is an auto-increment value.
BEGIN
SET new.key = concat(new.countryCode,new.id);
END
countryCode works fine, but the id is always blank. How can I achieve something like this? The id comes from an autoincrement column.
I know it's not working because it's generated after the insert. So how can I do something like this?

AUTO_INCREMENT column are only set after insert.
If you need to access that value, you can only in an AFTER INSERT trigger. However, you cannot modify a column value in an AFTER UPDATE trigger...
In addition, you cannot perform an update on the table used in your AFTER INSERT trigger as (http://dev.mysql.com/doc/refman/5.0/en/stored-program-restrictions.html):
Within a stored function or trigger, it is not permitted to modify a
table that is already being used (for reading or writing) by the
statement that invoked the function or trigger.
Here the only reasonable solution would be to create a stored procedure to update the table, adjusting the relevant columns in a transaction to "emulate" you atomic insert statement.
That being said, in your particular case, the key column is redundant as that column is just the concatenation of two other columns of the same row.
Given its name, aren't you looking for a way to create a compound key instead? Something like that:
ALTER TABLE tbl ADD UNIQUE KEY (countryCode, id);

Related

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...

REPLACE versus INSERT in SQL

I am doing the following SQL tutorial: http://sql.learncodethehardway.org/book/ex11.html
and in this exercise the author says in the second paragraph:
In this situation, I want to replace my record with another guy but
keep the unique id. Problem is I'd have to either do a DELETE/INSERT
in a transaction to make it atomic, or I'd need to do a full UPDATE.
Could anyone explain to me what the problem is with doing an UPDATE, and when we might choose REPLACE instead of UPDATE?
The UPDATE code:
UPDATE person SET first_name = "Frank", last_name = "Smith", age = 100
WHERE id = 0;
Here is the REPLACE code:
REPLACE INTO person (id, first_name, last_name, age)
VALUES (0, 'Frank', 'Smith', 100);
EDIT: I guess another question I have is why would you ever do a DELETE/INSERT instead of just an UPDATE as is discussed in the quoted section?
According to the documentation, the difference is:
REPLACE works exactly like INSERT, except that if an old row in the table has the same value as a new row for a PRIMARY KEY or a UNIQUE index, the old row is deleted before the new row is inserted.
So what it does:
Try to match the row using one of the available indexes;
If the row doesn't exist already: add a new one;
If the row exists already: delete the existing row and add a new one afterwards.
When might using this become useful over separate insert and update statements?
You can safely call this, and you don't have to worry about existing rows (one statement vs. two);
If you want related data to be removed when inserting / updating, you can use replace: it deletes all related data too);
When triggers need to fire, and you expect an insert (bad reason, okay).
First Replace isn't widely understood in all database engines.
Second replace inserts/updates a record based on the primary key. While with update you can specify more elaborate conditions:
UPDATE person SET first_name = 'old ' + first_name WHERE age > 50
Also UPDATE won't create records.
UPDATE will have no effect if the row does not exist.
Where as the INSERT or REPLACE will insert if the row doesn't exists or replace the values if it does.
Update will change the existing records value in table based on particular condition. So you can change one or many records in single query.
Insert or Replace will insert a new record if records is not present in table else will replace. Replace will only work if and only if you provide the primary key value in the insert or replace query. If you forget to add primary key field value than a new record will created in table.
Case example:-
Update: You have a calculation of wages to be done based on a formula using the column values. In this case you will always use update query as using one single query you can update multiple records.
Insert or Replace: Already mentioned in the link you shared.
How the REPLACE INTO statement works:
AS INSERT:
REPLACE INTO table_name (column1name, column2name, ...)
VALUES (value1, value2, ...);
AS UPDATE:
REPLACE INTO table_name SET column1name = value, column2name = value, ... ;
The REPLACE statement checks whether the intended data record's unique key value already exists in the table before inserting it as a new record or updating it.
The REPLACE INTO statement attempts to insert a new record or modify an existing record. In both cases, it checks whether the unique key of the proposed record already exists in the table. Suppose a value of NO or FALSE is returne. In that case, the REPLACE statement inserts the record similar to the INSERT INTO statement.
Suppose the key value already exists in the table (in other words, a duplicate key). In that case, the REPLACE statement deletes the existing record of data and replaces it with a new record of data. This happens regardless of whether you use the first or the second REPLACE statement syntax.
Once the REPLACE INTO statement is used to insert or modify data, it determines first whether the new data record already exists in the table. It checks if the PRIMARY or the UNIQUE KEY matches one of the existing records.
If there is no matching key, the REPLACE works like a normal INSERT statement. Otherwise, it deletes the existing record and replaces it with the new one. This is considered a sort of modification or update of an existing record. However, it would be best if you were careful here. Suppose you do not specify a value for a column in the SET clause. In that case, the REPLACE statement uses the default value (if a default value has been set). Otherwise, it's set as NULL.

How to change the auto_increment field in a table trigger in MySQL?

There is a necessity when inserting into a table of values to change the auto-increment field to another that no two of the same id in these tables. It is necessary for the data output from the third table based on the recording and going to not add to the table a column indicating. Here's my trigger, but it does not work
CREATE TRIGGER `update_id` AFTER INSERT ON `table1`
FOR EACH ROW BEGIN
ALTER TABLE `table2` AUTO_INCREMENT = NEW.id;
END;
It's not entirely clear what problem you are trying to solve.
But it sounds as if you have two tables with an id column, and you want to ensure that the same value of id is not used in both tables. That is, if id value 42 exists in table1, you want to ensure that 42 is not used as an id value in table2.
Unforunately, MySQL does not provide any declarative constraint for this.
It sounds as if you want an Oracle-style SEQUENCE object. And unfortunately, MySQL doesn't provide an equivalent.
But what we can do is emulate that. Create an extra "sequence" table that contains an AUTO_INCREMENT column. The purpose of this table is to be used to generate id values, and to keep track of the highest generated id value:
CREATE TABLE mysequence (id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY);
Then, we'd remove the AUTO_INCREMENT attribute from the id columns of the two tables we want to generate distinct id values for.
For those tables, we'd create BEFORE INSERT triggers that will obtain distinct id values and assign it to the id column. To generate a unique value, we can insert a row to the new mysequence table, and then retrieve the auto_increment value using the LAST_INSERT_ID function.
Something like this:
CREATE TRIGGER table1_bi
BEFORE INSERT ON table1
FOR EACH ROW
BEGIN
DECLARE generated_id INT UNSIGNED;
-- do we need to generate a value for id column?
IF NEW.id IS NULL THEN
-- generate unique id value with insert into sequence table
INSERT INTO mysequence (id) VALUES (NULL);
-- retrieve inserted id value
SELECT LAST_INSERT_ID() INTO generated_id;
-- assign the retrieved value to the id columns of the row being inserted
SET NEW.id = generated_id;
END IF
END$$
(That's just a rough outline, likely there's at least one syntax error in there somewhere.)
You'd need to create a BEFORE INSERT trigger for each of the tables.
This is one approach to generating distinct values for the id columns.
Note that it wouldn't be necessary to keep ALL of the rows in the mysequence table, it's only necessary to keep the row with the largest id value.
Also note that this doesn't enforce any constraint on either tables; some session could supply a value for id that is already in the other table. To prevent that, the trigger could raise an error if a non-NULL id value is supplied. It might also be possible to allow non-NULL values, and to perform a query to check if the supplied id value already exists in the other table, and raise an error if it does. But that query would be subject to a race condition... two concurrent sessions doing inserts to the tables, and you'd need to implement some concurrency killing locking mechanisms to prevent concurrent inserts.

insert on duplicate key update

I would like to use "insert on duplicate key update" in a query to either insert a new row if it does not exist or update a row if it does. What I can not seem to figure out is how to use this if I do not have the unique id (because the row has not yet been created, and this ID will be autoincremented upon insert)
insert into foodchoices (unique,notunique) values (Idonthavethis,'test')
on duplicate key update notunique = 'stuff';
Now, in this example above, where it says "Idonthavethis", I do not have any unique value for this field, because it has not yet been inserted as a row. However, I do expect that this inserts somehow, I just dont know how. I expect it to act like this:
insert into foodchoices (notunique) values ('test')
BUT, if it is a field that does already exist, I WILL have that unique value. Is there some form of wildcard or something I can use for when I do not have the unique value?
I believe the answer to this is addressed in the MySQL docs:
If a table contains an AUTO_INCREMENT column and INSERT ... UPDATE inserts a row, the LAST_INSERT_ID() function returns the AUTO_INCREMENT value. If the statement updates a row instead, LAST_INSERT_ID() is not meaningful. However, you can work around this by using LAST_INSERT_ID(expr). Suppose that id is the AUTO_INCREMENT column. To make LAST_INSERT_ID() meaningful for updates, insert rows as follows:
INSERT INTO table (a,b,c) VALUES (1,2,3)
ON DUPLICATE KEY UPDATE id=LAST_INSERT_ID(id), c=3;
I think what you just might try to do is select the row with the value you have (if exists then update) otherwise insert. It's just one more sentence.
I just don't see how can you compare an existing value that you just don't have (the generated ID). Plus, if the ID is DB seeded how it'll be duplicated?
May be you need to alter your table structure for adding any constraint to "notunique" column. So you can:
insert into foodchoices (notunique) values ('test') on duplicate key update columntostoreyouruniqueid = unique;
if notunique has no constaint then it mean that you will have uniqueid as set. So it has to double query.

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.