Related
I want to add a row to a database table, but if a row exists with the same unique key I want to update the row.
For example:
INSERT INTO table_name (ID, NAME, AGE) VALUES(1, "A", 19);
Let’s say the unique key is ID, and in my Database, there is a row with ID = 1. In that case, I want to update that row with these values. Normally this gives an error.
If I use INSERT IGNORE it will ignore the error, but it still won’t update.
Use INSERT ... ON DUPLICATE KEY UPDATE
QUERY:
INSERT INTO table (id, name, age) VALUES(1, "A", 19) ON DUPLICATE KEY UPDATE
name="A", age=19
Check out REPLACE:
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.
Example:
REPLACE INTO `tablename` (`id`, `name`, `age`) VALUES (1, "A", 19)
When using batch insert use the following syntax:
INSERT INTO TABLE (id, name, age) VALUES (1, "A", 19), (2, "B", 17), (3, "C", 22)
ON DUPLICATE KEY UPDATE
name = VALUES (name),
...
Any of these solution will work regarding your question:
INSERT IGNORE INTO table (id, name, age) VALUES (1, "A", 19);
or
INSERT INTO TABLE (id, name, age) VALUES(1, "A", 19)
ON DUPLICATE KEY UPDATE NAME = "A", AGE = 19;
or
REPLACE INTO table (id, name, age) VALUES(1, "A", 19);
Try this:
INSERT INTO table (id,name,age) VALUES('1','Mohammad','21') ON DUPLICATE KEY UPDATE name='Mohammad',age='21'
Note:
Here if id is the primary key then after first insertion with id='1' every time attempt to insert id='1' will update name and age and previous name age will change.
Try this out:
INSERT INTO table (id, name, age) VALUES (1, 'A', 19) ON DUPLICATE KEY UPDATE id = id + 1;
Hope this helps.
In case that you wanted to make a non-primary fields as criteria/condition for ON DUPLICATE, you can make a UNIQUE INDEX key on that table to trigger the DUPLICATE.
ALTER TABLE `table` ADD UNIQUE `unique_index`(`name`);
And in case you want to combine two fields to make it unique on the table, you can achieve this by adding more on the last parameter.
ALTER TABLE `table` ADD UNIQUE `unique_index`(`name`, `age`);
Note, just make sure to delete first all the data that has the same name and age value across the other rows.
DELETE table FROM table AS a, table AS b WHERE a.id < b.id
AND a.name <=> b.name AND a.age <=> b.age;
After that, it should trigger the ON DUPLICATE event.
INSERT INTO table (id, name, age) VALUES(1, "A", 19) ON DUPLICATE KEY UPDATE
name = VALUES(name), age = VALUES(age)
Just because I was here looking for this solution but for updating from another identically-structured table (in my case website test DB to live DB):
INSERT live-db.table1
SELECT *
FROM test-db.table1 t
ON DUPLICATE KEY UPDATE
ColToUpdate1 = t.ColToUpdate1,
ColToUpdate2 = t.ColToUpdate2,
...
As mentioned elsewhere, only the columns you want to update need to be included after ON DUPLICATE KEY UPDATE.
No need to list the columns in the INSERT or SELECT, though I agree it's probably better practice.
When using SQLite:
REPLACE into table (id, name, age) values(1, "A", 19)
Provided that id is the primary key. Or else it just inserts another row. See INSERT (SQLite).
In case, you want to keep old field (For ex: name). The query will be:
INSERT INTO table (id, name, age) VALUES(1, "A", 19) ON DUPLICATE KEY UPDATE
name=name, age=19;
In my case i created below queries but in the first query if id 1 is already exists and age is already there, after that if you create first query without age than the value of age will be none
REPLACE into table SET `id` = 1, `name` = 'A', `age` = 19
for avoiding above issue create query like below
INSERT INTO table SET `id` = '1', `name` = 'A', `age` = 19 ON DUPLICATE KEY UPDATE `id` = "1", `name` = "A",`age` = 19
may it will help you ...
Following are some of the possible approaches:
Using INSERT INTO
The INSERT statement allows you to insert one or more rows into a table
First, specify the table name and a list of comma-separated columns inside parentheses after the INSERT INTO clause.
Secondly, put a comma-separated list of values of the corresponding columns inside the parentheses following the VALUES keyword.
INSERT INTO table_name(column_name1, column_name2, column_name3) VALUES("col_value_1", "col_value_2", "col_value_3");
Using INSERT INTO with WHERE NOT EXISTS clause
INSERT INTO table_name (column_name_1, column_name_2, column_name_3)
SELECT * FROM (SELECT "col_value_1", "col_value_2","col_value_3") AS tmp_name
WHERE NOT EXISTS (
SELECT column_name2 FROM table_name WHERE column_name = "sample_name"
) LIMIT 1;
Using REPLACE INTO
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.
REPLACE INTO table_name(column_name1, column_name2, column_name3) VALUES("col_value_1", "col_value_2", "col_value_3");
I want to add a row to a database table, but if a row exists with the same unique key I want to update the row.
For example:
INSERT INTO table_name (ID, NAME, AGE) VALUES(1, "A", 19);
Let’s say the unique key is ID, and in my Database, there is a row with ID = 1. In that case, I want to update that row with these values. Normally this gives an error.
If I use INSERT IGNORE it will ignore the error, but it still won’t update.
Use INSERT ... ON DUPLICATE KEY UPDATE
QUERY:
INSERT INTO table (id, name, age) VALUES(1, "A", 19) ON DUPLICATE KEY UPDATE
name="A", age=19
Check out REPLACE:
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.
Example:
REPLACE INTO `tablename` (`id`, `name`, `age`) VALUES (1, "A", 19)
When using batch insert use the following syntax:
INSERT INTO TABLE (id, name, age) VALUES (1, "A", 19), (2, "B", 17), (3, "C", 22)
ON DUPLICATE KEY UPDATE
name = VALUES (name),
...
Any of these solution will work regarding your question:
INSERT IGNORE INTO table (id, name, age) VALUES (1, "A", 19);
or
INSERT INTO TABLE (id, name, age) VALUES(1, "A", 19)
ON DUPLICATE KEY UPDATE NAME = "A", AGE = 19;
or
REPLACE INTO table (id, name, age) VALUES(1, "A", 19);
Try this:
INSERT INTO table (id,name,age) VALUES('1','Mohammad','21') ON DUPLICATE KEY UPDATE name='Mohammad',age='21'
Note:
Here if id is the primary key then after first insertion with id='1' every time attempt to insert id='1' will update name and age and previous name age will change.
Try this out:
INSERT INTO table (id, name, age) VALUES (1, 'A', 19) ON DUPLICATE KEY UPDATE id = id + 1;
Hope this helps.
In case that you wanted to make a non-primary fields as criteria/condition for ON DUPLICATE, you can make a UNIQUE INDEX key on that table to trigger the DUPLICATE.
ALTER TABLE `table` ADD UNIQUE `unique_index`(`name`);
And in case you want to combine two fields to make it unique on the table, you can achieve this by adding more on the last parameter.
ALTER TABLE `table` ADD UNIQUE `unique_index`(`name`, `age`);
Note, just make sure to delete first all the data that has the same name and age value across the other rows.
DELETE table FROM table AS a, table AS b WHERE a.id < b.id
AND a.name <=> b.name AND a.age <=> b.age;
After that, it should trigger the ON DUPLICATE event.
INSERT INTO table (id, name, age) VALUES(1, "A", 19) ON DUPLICATE KEY UPDATE
name = VALUES(name), age = VALUES(age)
Just because I was here looking for this solution but for updating from another identically-structured table (in my case website test DB to live DB):
INSERT live-db.table1
SELECT *
FROM test-db.table1 t
ON DUPLICATE KEY UPDATE
ColToUpdate1 = t.ColToUpdate1,
ColToUpdate2 = t.ColToUpdate2,
...
As mentioned elsewhere, only the columns you want to update need to be included after ON DUPLICATE KEY UPDATE.
No need to list the columns in the INSERT or SELECT, though I agree it's probably better practice.
When using SQLite:
REPLACE into table (id, name, age) values(1, "A", 19)
Provided that id is the primary key. Or else it just inserts another row. See INSERT (SQLite).
In case, you want to keep old field (For ex: name). The query will be:
INSERT INTO table (id, name, age) VALUES(1, "A", 19) ON DUPLICATE KEY UPDATE
name=name, age=19;
In my case i created below queries but in the first query if id 1 is already exists and age is already there, after that if you create first query without age than the value of age will be none
REPLACE into table SET `id` = 1, `name` = 'A', `age` = 19
for avoiding above issue create query like below
INSERT INTO table SET `id` = '1', `name` = 'A', `age` = 19 ON DUPLICATE KEY UPDATE `id` = "1", `name` = "A",`age` = 19
may it will help you ...
Following are some of the possible approaches:
Using INSERT INTO
The INSERT statement allows you to insert one or more rows into a table
First, specify the table name and a list of comma-separated columns inside parentheses after the INSERT INTO clause.
Secondly, put a comma-separated list of values of the corresponding columns inside the parentheses following the VALUES keyword.
INSERT INTO table_name(column_name1, column_name2, column_name3) VALUES("col_value_1", "col_value_2", "col_value_3");
Using INSERT INTO with WHERE NOT EXISTS clause
INSERT INTO table_name (column_name_1, column_name_2, column_name_3)
SELECT * FROM (SELECT "col_value_1", "col_value_2","col_value_3") AS tmp_name
WHERE NOT EXISTS (
SELECT column_name2 FROM table_name WHERE column_name = "sample_name"
) LIMIT 1;
Using REPLACE INTO
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.
REPLACE INTO table_name(column_name1, column_name2, column_name3) VALUES("col_value_1", "col_value_2", "col_value_3");
If I have 2 tables and Table 1 has a primary key(userID) AutoIncrement and Table 2 has a foreign Key(userID) to Table 1's (userID)
When I insert a new row into Table 1 first row will have userID = 1
Then if I insert again, userID = 2.
So how do I go about keeping Table 2's userID the same when inserting in Table 1. For instance, in Table 2, I am adding the password into another table.
My question is should I add an AUTOINCREMENT to Table 2(userID) and insert a new value into both tables when I create a user OR is there another way?
You have to manually insert data with correct id in Table2. There is no such built-in functionality in MySQL.
The algorithm is as follows:
Insert row in Table1.
Get Id of the new row.
Insert row with new id in Table2.
I think what you are looking for is this ... Assuming your ID is PK and AI
INSERT INTO yourTable (var1, var2 etc etc) VALUES ('val1', 'val2' etc etc);
SELECT LAST_INSERT_ID();
To further it ..
INSERT INTO yourTable (var1, var2 etc etc) VALUES ('val1', 'val2' etc etc);
SET #last_id = LAST_INSERT_ID();
INSERT INTO yourTable2 (id, var1, var2) VALUES (#last_id, 'blah', 'blah');
I have a MySQL database with 9 tables, each table has an id with primary key set and auto increment and a row for data, one table let's call it 'X' from this database has also a primary key and one row for data and all primary keys from other tables as foreign keys, the question is can I insert into 'X' table not by id of other tables but by data row, for example some other tables has id -> 3 and data: apple , can I insert 'apple' that takes id 3, instead of id 3 that takes apple from data row ?
You can select and insert values in one step:
INSERT INTO X (fruit_id, ...)
VALUES ((SELECT fruit_id FROM fruits WHERE fruit = 'apple'), ...);
That will insert the fruit_id of apple.
Based on i486s comment, you can also directly insert a select result:
INSERT INTO X (fruit_id, tableb_id, tablec_id, datafiled)
SELECT fruit_id, b.tableb_id, c.tablec_id, 'some data'
FROM fruits a, tableb b, tablec c
WHERE a.fruit = 'apple',
AND b.tableb_data = 'some data from tableb'
AND c.tablec_data = 'some data from tablec';
No, mysql is no ms access where you could do this. You can implement an import function in mysql or even a GUI in another programming language that translates the value 'apple' into the foreign key of 3 by looking it up in the other table. That's exactly what ms access does in the background for you.
The purpose of this is to copy some rows from one environment to another without overwriting existing rows.
Sample DB:
INSERT INTO `school` (school_id,name) VALUES (15,'Middle');
INSERT INTO `class` (class_id,school_id,name) VALUES (12,15,'Sample');
The idea is school_id and class_id are auto-increments and class has a Foreign Key link back to school. But I want to dump just these rows and insert them into another database that already has a school_id of 15.
It might be something that could look like:
INSERT INTO `school` (name) VALUES ('Middle');
INSERT INTO `class` (school_id,name) VALUES (LAST_INSERT_ID(),'Sample');
But that would just be for this simple example. Imagine if I had 50 classes, 25 students in each, and a few hundred grades for each student/class combo. You could see how the LAST_INSERT_ID() might not work without storing it in a series of variables.
What would be the proper tool to do this kind of operation? Can mysqldump do anything this smart?
You can do this:
Find MAX school_id in the target school table -
SELECT MAX(school_id) INTO #max_school_id FROM school;
Change all school_id values in source tables (school, class) - add MAX school_id from the previous point -
UPDATE school SET school_id = school_id + #max_school_id + 1;
It might be very usefull to add ON UPDATE CASCADE action to the foreign key, it will help to change school_id in the child table automatically, e.g. -
ALTER TABLE class
DROP FOREIGN KEY FK_name;
ALTER TABLE class
ADD CONSTRAINT FK_name FOREIGN KEY (school_id)
REFERENCES school(school_id) ON UPDATE CASCADE;
Make dump and import.
Explanation and example:
Create source tables:
CREATE TABLE school(
school_id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(20)
);
INSERT INTO school (school_id, name) VALUES
(1, 'Middle1'),
(2, 'Middle2'),
(3, 'Middle3'),
(15, 'Middle');
CREATE TABLE class(
class_id INT(11) NOT NULL,
school_id INT(11) DEFAULT NULL,
name VARCHAR(20) DEFAULT NULL,
PRIMARY KEY (class_id),
CONSTRAINT FK_class_school_school_id FOREIGN KEY (school_id)
REFERENCES school (school_id) ON DELETE RESTRICT ON UPDATE CASCADE
)
ENGINE = INNODB;
INSERT INTO class (class_id, school_id, name) VALUES (11, 1, 'Sample1');
INSERT INTO class (class_id, school_id, name) VALUES (12, 15, 'Sample');
Create target tables:
CREATE TABLE school(
school_id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(20)
);
INSERT INTO school (school_id, name) VALUES
(1, 'Top'),
(2, 'Middle'),
(3, 'Bottom'),
(15, 'Top');
CREATE TABLE class(
class_id INT(11) NOT NULL,
school_id INT(11) DEFAULT NULL,
name VARCHAR(20) DEFAULT NULL,
PRIMARY KEY (class_id),
CONSTRAINT FK_class_school_school_id FOREIGN KEY (school_id)
REFERENCES school (school_id) ON DELETE RESTRICT ON UPDATE CASCADE
)
ENGINE = INNODB;
INSERT INTO class (class_id, school_id, name) VALUES (10, 2, 'Sample2');
INSERT INTO class (class_id, school_id, name) VALUES (12, 15, 'Sample');
Update source tables, increment id values:
We should update all unique values, in our case we have to update class_id in the class table and school_id in the school table.
Find max class_id for the TARGET class table
SELECT MAX(class_id) + 1000 FROM class; -- This will return => 1012
Increment all SOURCE class_id values class_id + 1012
UPDATE class SET class_id = class_id + 1012;
Find max school_id for the TARGET school table
SELECT max(school_id) + 1000 FROM school; -- This will return =>1015
Increment all SOURCE school_id values school_id + 1015
UPDATE school SET school_id = school_id + 1015;
That is all. We can dump source tables:
INSERT INTO school VALUES
(1016, 'Middle1'),
(1017, 'Middle2'),
(1018, 'Middle3'),
(1030, 'Middle');
INSERT INTO class VALUES
(1023, 1016, 'Sample1'),
(1024, 1030, 'Sample');
Now we can easily run this script against the target database.
Do you need to do this in SQL? Even the most basic of ETL tools would be better suited. Try pentaho or talend instead.
If you don't need a pure SQL solution you can very easily create a script that reads from the old database and writes to the new. I'm thinking PHP, Python, Ruby, Perl, ...
Here is a simple solution in PHP assuming you are using mysql and moving data between databases (untested, may contain errors):
$dbh1 = new PDO("mysql:host=db1.host;dbname=db1", "user1", "pass1");
$dbh2 = new PDO("mysql:host=db2.host;dbname=db2", "user2", "pass2");
$sth1 = $dbh1->query("
SELECT
school.school_id as school_id,
school.name as school_name,
class.name as class_name
FROM school
JOIN class ON (school.school_id = class.school_id)
");
$sth3 = $dbh2->prepare("INSERT INTO school (name) VALUES (:name)");
$sth4 = $dbh2->prepare("INSERT INTO class (school_id, name) VALUES (:school_id, :name)");
$schools = array();
// get schools and classes
while ($school = $sth1->fetch(PDO::FETCH_ASSOC)) {
$school_id = $school['school_id'];
$school_name = $school['school_name'];
$schools[$school_id]['school_name'] = $school_name;
$schools[$school_id]['classes'][] = array(
'class_name' => $school['class_name']
);
}
// insert schools and classes
foreach ($schools as $school_id => $school) {
// insert school
$sth3->bindParam(':name', $school['school_name'], PDO::PARAM_INT);
$sth3->execute();
$new_school_id = $dbh2->lastInsertId();
// a loop for classes
foreach ($school['classes'] as $class) {
// insert class
$sth4->bindParam(':school_id', $new_school_id, PDO::PARAM_INT);
$sth4->bindParam(':name', $class['class_name'], PDO::PARAM_STR);
$sth4->execute();
}
// a loop for another joined table
/*
foreach ($school['joined'] as $join) {
// insert join
$sth4->bindParam(':school_id', $new_school_id, PDO::PARAM_INT);
$sth4->bindParam(':name', $join['join_name'], PDO::PARAM_STR);
$sth4->execute();
}
*/
}
If you have temporary table privileges you could do:
CREATE TEMPORARY TABLE tmp_school LIKE school;
LOAD DATA LOCAL INFILE 'school.dat' INTO TABLE tmp_school;
CREATE TEMPORARY TABLE tmp_class LIKE class;
LOAD DATA LOCAL INFILE 'class.dat' INTO TABLE tmp_class;
INSERT INTO school (name) SELECT name FROM tmp_school;
INSERT INTO class (school_id,name)
SELECT school.school_id, class.name FROM school school JOIN tmp_school new
USING(name) JOIN tmp_class class ON new.school_id = class.school_id
I think this is right but it needs a bit of checking.
One really simple trick would be to just multiply the id's with -1. Negative id is as good as any id, and I assume that your auto_increment columns start with positive numbers anyways.
Export from one environment:
select -1*school_id, name from school into outfile 'school.out';
select -1*class_id, -1*school_id, name from class into outfile 'class.out';
Import into second:
load data infile 'school.out' into table school;
load data infile 'class.out' into table class;
Obviously this is not a generic solution to your problem, but how often do you need one? :)
Generic solution would be to use write the migration logic yourself, either in ETL tool or just as a standalone script, as others have stated. And make that tool/script to use INFORMATION_SCHEMA.TABLES and INFORMATION_SCHEMA.COLUMNS to dynamically find out the tables and columns that need to be adjusted.
I think the best way is to eliminate the ids from the transfer. assuming that the school names are unique, this seemed to work on deviant's schema, and for simplicity, having the two databases on the same server:
Copy the data into a table on the new database with no ids, just names:
CREATE TEMPORARY TABLE tmp AS SELECT s.name as school_name, c.name as class_name
FROM test.school s JOIN test.class c USING(school_id);
Add the new schools to the school table:
INSERT INTO school (name) SELECT DISTINCT school_name FROM tmp
LEFT JOIN school ON school_name = name WHERE name IS NULL;
Add the new classes (for both existing and new schools) to the class table:
INSERT INTO class (name, school_id) SELECT class_name, school_id
FROM tmp t JOIN school s ON s.name = t.school_name;
What semantics do you want if there are classes in the target databases for schools in the source that don't have that class? This is a union, if you want delete you will have to change it.
if you are using this command in php, then there is a simple function which will give you the last id of your insert query. i.e. mysql_insert_id().
code may be like this :
<?php
$query = mysql_query("INSERT INTO `school` (school_id,name) VALUES (15,'Middle')");
$last_id = mysql_insert_id();
INSERT INTO `class` (class_id,school_id,name) VALUES ('$last_id','Sample');
?>
if you are using in some other language I don't know what has to be done.