Incrementing a value in an InnoDB table using MySQL REPLACE - mysql

I would like to know if it is possible to use REPLACE MySQL statement in an InnoDB table to see if the key in the table already exists, if it does then increment the value by certain number (for instance 50), else insert a new record with with a certain value (50). Something like this:
REPLACE INTO test(id, NumValue) VALUES (1, NumValue + 50)
This statement doesn't work. It inserts/updates to NULL value. If there is a way to do this using REPLACE statement what is the correct syntax? I know I could do it in couple steps, first using SELECT statement to find out if the record already exists, if it does then do an UPDATE, if it doesn't, then do an INSERT. But I was hoping it could be achieved by using just one step, using REPLACE. And also, I want to avoid using sub-queries.

Related

mysql/mariadb, is it a bad idea to check if row exists by trying to insert a new row and checking for errors? [duplicate]

This question already has answers here:
How can I do 'insert if not exists' in MySQL?
(11 answers)
Closed 1 year ago.
I found a not so nice method for checking if there a row already exists, and if it does exists, then it doesn't add it to avoid duplicates. Am i completely crazy to rely on this method or should i go old fashioned way where i check if it exists BEFORE trying to insert row in database?
The table is VERY simple :)
-ID [PK]
-Message
-Hashed_message [UNIQUE] (stored procedure, takes message and hashes it upon insert)
Now when i try to insert a new row i would say
*`insert into .... message = xxx
Upon insertion mysql will create a hash on message automatically, but since it's an unique column, incase the hash already exists in database, it will simply throw an error, and no duplicates will exist ever... i hope.
The reason for using hashes, is simply to avoid checking duplicates by scanning every large message, instead i though a short hash would be easier to check for duplicates.
So is this method bad for avoiding duplicates?
I mean i could before insert, manually create that hash of my message and check if that hash exists and THEN insert the message, but i would hope to avoid always trying to match the stored procedure function on PHP as well.
quick note: there is a similar thread about insert and then ignoring error on duplicate, but this one is related to how it is handled when a derived column(Stored procedure) is used to accomplish this
If the hashed message has to be unique, create a key on that column with the UNIQUE constrain: so there won't be two rows with the same hash.
Then, when you insert a new row modify your query with the following:
INSERT INTO table SET message='$message', hashed_message='$hashed_message'
ON DUPLICATE KEY id=id;
This will perform an insert if the hashed_message is unique. Otherwise will not do any update.
If you want to update something in case of duplicate your query will become:
INSERT INTO table SET message='$message', hashed_message='$hashed_message'
ON DUPLICATE KEY UPDATE message='$updated_message'
just to make an example.
Note that this method won't raise any exception in case of duplicate values: you need extra logic if you need to perform actions in your frontend in case of duplicates (i.e. message shown to the user).
More details here

In MySQL, how can I use the autoincrement value in another column at the time of the INSERT?

I am looking to have the automagically set autoincrement included in another column during the insert. For example in a table where ID is the autoincrement and Name is the other column, I'd like to do something like
`INSERT INTO Names (Name) VALUES (CONCAT("I am number ",ID));`
Currently, I do the INSERT without Name, then I have to immediately after do an UPDATE using $mysqli->insert_id.
I don't want to query the table in advance because, as small a time as it may be, another record could get inserted between getting the next autoincrement value and the insertion. A trigger could work, but it seems like overkill. I just want to know if I can reference the autoincrement within the insertion.
Many thanks!
The problem is not as easy as it seems. In a BEFORE INSERT trigger, the autoincrement value hasn't been generated yet (NEW.autoinc_column is 0), and in an AFTER INSERT trigger, it is not possible anymore to change the values to be inserted.
With MyISAM tables, you could check the table definition for the next AUTO_INCREMENT value:
DELIMITER //
CREATE TRIGGER inserName BEFORE INSERT ON name FOR EACH ROW
BEGIN
DECLARE next_ai INT;
SELECT auto_increment INTO next_ai
FROM information_schema.tables
WHERE table_schema = DATABASE() AND table_name = 'name';
SET NEW.name = CONCAT("I am number ", next_ai);
END //
DELIMITER ;
I believe this could work too with InnoDB tables if innodb_autoinc_lock_mode = 0 (not the case by default), but I am not sure (because of possible concurrency issues).
But if the value to concat is always the same, you probably had better using a view such as:
CREATE VIEW names_v AS SELECT id, CONCAT("I am number ", id) AS name FROM names;
I've also recently been facing this problem and although it might not be the best solution, what I did could be a viable alternative for your case. In my case it was sufficient. You could use a AFTER INSERT trigger to update the field with the value refering to the pseudo-variable NEW. This will probably give you a bit more flexibility.
I needed to fill a field with a string that was computed using the value from the auto increment column.
The trigger reads the NEW pseudo-variable, computes the necessary values and executes the UPDATE.
It still does require two high level write-acesses to the database, but all are done consecutively and without the need for further interaction with the client application (effectively, one single insert statement being sent from the client application, being followed by the implicit update).

Enforcing unique columns

If a column is made unique in a database table structure, is there any need to do a check to see if a new value to be inserted already exists in the table via script? Or would it be fine just to insert values letting the DBMS filter non-new values?
When you will try to insert a duplicate value in a unique column, your insert query will fail. So it might be a good idea to make sure you are checking to see if your insert queries went well or not. Althought regardless of the situation you should always check if your insert query went through or not :)
You should always validate your data before inserting it on the database. That being said, what will happen if you try to insert a non-unique value on a unique defined column is an SQLexception.
In order to validate this before insertion, you could for example do a
select 1
from mytable_with_unique_column
where my_unique_column = myNewValue
If the query returns anything, then simply do not try to insert as that will throw an SQLException.
Verification of unique constraint is definitely an overkill.
When you put unique constraint on your column, an implicit index is created for this column. Thus, DBMS can (and will) verify your data much faster. Unfortunately, when you try to insert duplicate value into your column, you will get constraint violation exception you have to deal with (but you have to deal with such error while using script verification either).
Good luck.
You can combine the insert statement and validation select into one statement:
insert into mytable_with_unique_column (...) values (...)
where not exists
(
select 1
from mytable_with_unique_column
where my_unique_column = myNewValue
)
This will only insert a new row if there isn't already a row with the given unique value.

Mysql - Get auto increment id on actual insert

Does anyone know if it is possible to get the auto increment id of a field in a mysql table and use it in the same insert?
e.g
assuming the new id here would be 2, an evaluated statement would look as follows
"insert into table (field1) values( 'random-2')"
I know it's possible for me to return this in the code and run another insert, but I wondered if there was a quicker way to 'compute' this during the insert?
1 thought I had was "insert into table (field1) values( 'random' + (select max(id) FROM table) + 1)"
but I'm worried about possible issues with multiple inserts occurring at the same time.
Thanks
It's not possible. You're gonna have to update the entry afterwards.
I'm worried about possible issues with multiple inserts occurring at the same time
That's only part of the problem - insert ids are only suposed to be unique - not contiguous.
If you want to do it in a single call from the application then use a stored procedure to encapsulate the insert+update or use a trigger to fire an update on insert. Or use a sequence generator instead of an autoincrement.

Performing an UPDATE or INSERT depending whether a row exists or not in MySQL

In MySQL, I'm trying to find an efficient way to perform an UPDATE if a row already exists in a table, or an INSERT if the row doesn't exist.
I've found two possible ways so far:
The obvious one: open a transaction, SELECT to find if the row exists, INSERT if it doesn't exist or UPDATE if it exists, commit transaction
first INSERT IGNORE into the table (so no error is raised if the row already exists), then UPDATE
The second method avoids the transaction.
Which one do you think is more efficient, and are there better ways (for example using a trigger)?
INSERT ... ON DUPLICATE KEY UPDATE
You could also perform an UPDATE, check the number of rows affected, if it's less than 1, then it didn't find a matching row, so perfom the INSERT.
There is another way - REPLACE.
REPLACE INTO myTable (col1) VALUES (value1)
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. See Section 12.2.5, “INSERT Syntax”.
In mysql there's a REPLACE statement that, I believe, does more or less what you want it to do.
REPLACE INTO would be a solution, it uses the UNIQUE INDEX for replacing or inserting something.
REPLACE INTO
yourTable
SET
column = value;
Please be aware that this works differently from what you might expect, the REPLACE is quite literally. It first checks if there is a UNIQUE INDEX collision which would prevent an INSERT, it removes (DELETE) all rows which collide and then INSERTs the row you've given it.
This, for example, leads to subtle problems like Triggers not firing (because they check for an update, which never occurs) or values reverted to the defaults (because you must specify all values).
If you're doing a lot of these, it might be worth writing them to a file, and then using 'LOAD DATA INFILE ... REPLACE ...'