Hi i have asked a question about how to transfer the sql code from Postgresql to Mysql.
Thanks to the kindly help i am now able to create the table. Now i have a following question of the query to transfer again from Postgresql to Mysql.
Create the table
CREATE TABLE public.`user` (
id varchar(10) NOT NULL,
chat_id varchar(10) NULL,
state INT NOT NULL,
create_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
update_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (id)
);
The query i'd like to transfer to mysql:
After insert a row of data, check if id has conflict. if we get a conflict we update chat_id and state
sql = '''INSERT INTO public.`user`
(id, chat_id, state)
VALUES(%s, %s, 1)
ON CONFLICT(id) DO UPDATE
SET chat_id = EXCLUDED.chat_id, state = EXCLUDED.state;
'''
PostgreSQL's ON CONFLICT is close to MySQL's ON DUPLICATE KEY UPDATE.
But your query do the next: if entered id value is absent in the table then the row is inserted, if it is present then old row state is preserved. For to do the same in MySQL you may use more simple
INSERT IGNORE INTO public.`user` (id, chat_id, state)
VALUES(%s, %s, 1);
I.e. if some error (including duplicate violation) detected then simply ignore the values to be inserted. Pay attention - any error, not only duplicate violation, will be ignored.
If it is not safe for you then use ODKU:
INSERT IGNORE INTO public.`user` (id, chat_id, state)
VALUES(%s, %s, 1)
ON DUPLICATE KEY UPDATE id=id;
In MySQL in ODKU clause only explicitly updated columns values are altered (rather than ON CONFLICT) - so id reassign to the same value is an analog of "ignore new values". Of course you may use ON DUPLICATE KEY UPDATE chat_id=chat_id, state=state, no difference.
Related
I have a table with schema like this:
CREATE TABLE `things` (
`thing_id` INT(11) NOT NULL AUTO_INCREMENT,
`name` VARCHAR(32) NOT NULL DEFAULT '',
PRIMARY KEY (`thing_id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8mb4;
And some sample data:
INSERT INTO things VALUES (NULL, 'thing 1'), (NULL, 'thing 2');
In my application, sometimes I want to bump the primary key of a row to be the highest in the table. For example, I want the thing_id of thing 1 changed to 3 (the table's current autoincrement value). Previously the table was MyISAM and this was accomplished as follows:
UPDATE things t
JOIN (SELECT MAX(thing_id) + 1 AS max_id FROM things) v
SET t.thing_id = v.max_id
WHERE thing_id = 1;
That part still works. However, now with InnoDB, the next insert fails since doing that update leaves the table's autoincrement value still pointing to the same thing. So now if I do an insert now like this:
INSERT INTO things VALUES (NULL, 'thing 3');
I will get an error like:
Duplicate entry '3' for key 'PRIMARY'
This was not an issue with MyISAM. How can I accomplish the same thing using InnoDB without running into this error? My guess is that there's a better way to change the thing_id which will keep the table's autoincrement value intact, but that's where I'm stumped.
And lastly, here's a dbfiddle:
https://www.db-fiddle.com/f/enJPVkwNN6hocjquw38BHD/0
Reference: Innodb Auto Increment Initialization
If you specify an AUTO_INCREMENT column for an InnoDB table, the table handle in the InnoDB data dictionary contains a special counter called the auto-increment counter that is used in assigning new values for the column. This counter is stored only in main memory, not on disk.
Update:
You can reset this counter using below query, works for InnoDB storage engine in MySQL 5.7
ALTER TABLE things AUTO_INCREMENT = 1;
Executing this is resetting auto-increment counter to Max + 1 value.
I am using MySQL 5.7 with Node JS 6.11.0 and am trying to update a UNIQUE MySQL column whenever I insert a conflicting row. However, when I try inserting a conflicting record, only the existing record is updated to NULL and no insert happens. Here is my code
pool.getConnection(function(err, connection) {
var newClass = req.body;
var query = `INSERT INTO classes SET ? ON DUPLICATE KEY UPDATE teacher_id = NULL`;
connection.query(query, newClass, function(err, result) {
console.log(result);
if(result.affectedRows >= 1) {
res.status(201).end();
res.json(result);
}
});
connection.release();
});`
I have to run the query twice for the row to be inserted; the first time the conflicting column is set to null then when I run the same query again, the row is inserted since there are no conflicts.
I have taken the SQL generated and directly run it from MySql console and I still have to run the query twice for the new row to be inserted. I do not understand why it is behaving this way.
Sql statement is
INSERT INTO classes SET `stream` = 'Red', `form` = '1', `teacher_id` = '7' ON DUPLICATE KEY UPDATE teacher_id = NULL
My create table SQL is
| classes | CREATE TABLE `classes` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`form` varchar(10) NOT NULL,
`stream` varchar(15) NOT NULL,
`teacher_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `teacher_id` (`teacher_id`),
CONSTRAINT `classes_ibfk_1` FOREIGN KEY (`teacher_id`) REFERENCES `teachers`
( `id` )
) ENGINE=InnoDB AUTO_INCREMENT=33 DEFAULT CHARSET=latin1 |`
Why is MySQL behaving this way?
INSERT INTO classes
SET stream = 'Red', form = '1',teacher_id = '7'
ON DUPLICATE KEY UPDATE teacher_id = NULL;
Is telling MySQL when there is a conflicting record (duplicate unique or primary key), to set the offending row's teacher_id column to null and stop.
You have a couple options to achieve your desired result.
First you can use the REPLACE syntax.
REPLACE INTO classes SET stream = 'Red', form = '1', teacher_id = '7';
Which will first delete the offending row with the UNIQUE or Primary Key constraint, and Insert a new row with the specified values. The drawback is any omitted column values would be lost and set to their default values. A new auto-generated (if any) id will also be created if not specified in the query.
If there is no offending record a normal INSERT will occur.
Otherwise, you can utilize VALUES within the UPDATE clause.
INSERT INTO classes
SET stream = 'Red', form = '1', teacher_id = '7'
ON DUPLICATE KEY UPDATE stream = VALUES(stream),
form = VALUES(form);
This will instead, update the records of the offending row to the values specified in the INSERT portion of the query.
References:
https://dev.mysql.com/doc/refman/5.7/en/replace.html
https://dev.mysql.com/doc/refman/5.7/en/insert-on-duplicate.html
Updated based on new intentions
In order to create a new record while leaving the previous row, you would need to first invalidate the UNIQUE constraint on the existing row, prior to using INSERT.
There is currently no method to do this with MySQL in a single query.
Due to limitations on INSERT, the easiest way to accomplish what you need is to run an UPDATE query prior to the INSERT query.
UPDATE classes
SET teacher_id = NULL
WHERE teacher_id = '7';
INSERT INTO classes
SET stream = 'Red',
form = '1',
teacher_id = '7';
This will first check for a conflicting teacher_id record and set it to null. If no conflicting record exists, no update will occur. The following INSERT would then create the desired record, without a conflict occurring.
If you want to insert data based on condition try this
INSERT INTO table1 SELECT 'Red',1,7 FROM table2 where teacher_id IS NULL;
else if you want to update based on trying this.
update table1 SET stream = 'Red', form = '1', teacher_id = '7' where teacher_id IS NULL;
Note: NULL = NULL is not really FALSE - it's NULL again. But it's not TRUE either, so IF(NULL = NULL) won't execute
For example, NULL will test FALSE even against NULL itself. So, working with NULL is only done with the aforementioned functions (and IS NULL()). Your query could be rewritten as above.
Is it possible to update only a single field with ON DUPLICATE KEY UPDATE in a table with multiple fields?
If I have a table with three fields; key, cats, dogs where key is the primary key is it possible to update a record on duplicate key, changing only one field, (for my example cats) without changing the value in the other non-key fields (dogs). This is without knowing what the value of dogs from outside of the database at the time of insertion (i.e. I have a variable holding cats value, but not one holding dogs value)
INSERT INTO `myTable` (`key`,`cats`) VALUES('someKey1','Manx') ON DUPLICATE KEY UPDATE `cats` = 'Manx';
At the moment when I run something like this and the key already exists in the table dogs is set to NULL even when it had a value previously.
Gordon is right, it does not work the way I described. If you see this, it is not caused by the ON DUPLICATE UPDATE statement, but something else. Here is the proof:
CREATE TABLE IF NOT EXISTS `myTable` (
`key` varchar(20) NOT NULL default '',
`cats` varchar(20) default NULL,
`dogs` varchar(20) default NULL,
PRIMARY KEY (`key`)
)
The run
INSERT INTO `myTable` (`key`, `cats`, `dogs`) VALUES
('someKey1', NULL, 'jack-russell');
Then run
INSERT INTO `myTable` (`key`,`cats`) VALUES
('someKey1','Manx') ON DUPLICATE KEY UPDATE `cats` = 'manx';
Then check the table
I think you should try to UPSERT.
Please examine this:
INSERT INTO `item` (`item_name`, items_in_stock) VALUES( 'A', 27)
ON DUPLICATE KEY UPDATE `new_items_count` = `new_items_count` + 27
MySQL UPSERT
In MySQL, you can insert a row and update the 'last insert ID' at the same time. I'm using this trick to be able to insert items conditionally (uniqueness) and still get the unique ID in all cases.
The code below works. The problem is that the ON DUPLICATE KEY statement also updates the Auto_increment value. Is it possible to avoid that?
CREATE TABLE T(
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
X VARCHAR(64) NOT NULL UNIQUE
) ENGINE = InnoDB DEFAULT CHARSET=utf8;
INSERT INTO T(X) VALUES ('x') ON DUPLICATE KEY UPDATE id = LAST_INSERT_ID(id);
SELECT LAST_INSERT_ID();
INSERT INTO T(X) VALUES ('x') ON DUPLICATE KEY UPDATE id = LAST_INSERT_ID(id);
SELECT LAST_INSERT_ID();
Now we have one entry with id=1. However, the next entry will have id=3 since the Auto_increment value was updated when the second INSERT failed. This can be checked as follows:
SELECT Auto_increment FROM information_schema.tables WHERE table_name = 't'
AND table_schema = DATABASE();
Q: Is it possible to use this trick (or equivalent) and keep the Auto_increment value? Obviously, in this case, it doesn't need to get updated.
I'm running the following query... the issue I'm running into is that it saves the id field properly with rows 0-27 but then it tries to duplicate row 0. Any thoughts on why this is trying to duplicate row 0?
INSERT INTO
deal (`id`, `site_id`)
VALUES (NULL, 1)
ON DUPLICATE KEY UPDATE `id` = NULL,`site_id` = 1
From the manual:
You can also explicitly assign NULL or 0 to the column to generate sequence numbers.
So, effectivly, entering (0,'whatever') is seen as ('the next autoincrement id','whatever'). No duplicate key issues as far as MySQL is concerned. You can bypass this by using NO_AUTO_VALUE_ON_ZERO. Note that using 0 as an identifier is not recommended practice.
A PRIMARY KEY can't be NULL in MySQL, so your UPDATE id = NULL is casted to UPDATE id = 0.
See also: http://www.xaprb.com/blog/2009/09/12/the-difference-between-a-unique-index-and-primary-key-in-mysql/