I'm able to use MERGE statement in both Oracle and MSSQL. Right now I have to use MYSQL. Does MYSQL has similar statement to merge data.
Lets say I have two tables:
create table source
(
col1 bigint not null primary key auto_increment,
col2 varchar(100),
col3 varchar(50)
created datetime
)
create table destination
(
col1 bigint not null primary key auto_increment,
col2 varchar(100),
col3 varchar(50)
created datetime
)
Now I want move all data from "source" to "destination". If record already exists in "destination" by key I need update, otherwise I need insert.
In MSSQL I use the following MERGE statement, similar can be used in ORACLE:
MERGE destination AS TARGET
USING(SELECT * FROM source WHERE col2 like '%GDA%') AS SOURCE
ON
(TARGET.col1 = SOURCE.col1)
WHEN MATCHED THEN
UPDATE SET TARGET.col2 = SOURCE.col2,
TARGET.col3 = SOURCE.col3
WHEN NOT MATCHED THEN
INSERT INTO
(col2,col3,created)
VALUES
(
SOURCE.col2,
SOURCE.col3,
GETDATE()
)OUTPUT $action INTO $tableAction;
WITH mergeCounts AS
(
SELECT COUNT(*) cnt,
MergeAction
FROM #tableAction
GROUP BY MergeAction
)
SELECT #Inserted = (SELECT ISNULL(cnt,0) FROM mergeCounts WHERE MergeAction = 'INSERT'),
#Updated = (SELECT ISNULL(cnt,0) FROM mergeCounts WHERE MergeAction = 'UPDATE'),
#Deleted = (SELECT ISNULL(cnt,0) FROM mergeCounts WHERE MergeAction = 'DELETE')
so here I'm update records if exists and insert if new record. After MERGE statement I also able to count how many records was inserted, updated ...
Does it possible to have such implementation in MYSQL ??
Mysql has insert ... on duplicate key update ... syntax. use it like this:
insert into destination(col1, col2, col3, created)
select *
from source
on duplicate key update
col2 = values(col2),
col3 = values(col3),
created = values(created);
demo here
to get the number of affected rows, run select row_count() afterwards
Related
I have my_table with column1 column.
If there are rows with column1='old' I want to update those rows.
Else, I want to insert a new row.
Something like this:
IF(
(EXISTS(
SELECT * FROM my_table WHERE column1='old'
)),
(UPDATE my_table SET column1='new' WHERE column1='old'),
(INSERT INTO my_table (column1) VALUES ('new') )
)
I use MySql so I cannot use IF on the beginning of query.
column1 is not unique and not primary. So I cannot use ON DUPLICATE KEY UPDATE or REPLACE
In an attempt to copy all the columns of an existing row where only ONE column will change (called "field") and the auto generated primary key field (called "id") should be generated as usual by the insert statement, my naive approach is this:
drop table if exists temp1;
create table temp1 as
(
SELECT *
FROM original
WHERE field="old value"
)
;
# Update one of the many fields to a new value
UPDATE temp1
SET field = "new value"
;
# Getting rid of the primary key in the hope that a new one will be auto generated in the following insert
ALTER TABLE temp1 DROP id;
# Fails with syntax error. How can I specify "generate new id" ?
INSERT INTO original
SELECT NULL as id, *
FROM temp1
;
This fails with syntax error as it is not allowed to put NULL as id in the last select statement. So how would I go about doing this? Is there a simpler way?
SELECT * is antipattern. You should explicitly set columns:
INSERT INTO original(col1, col2, ...) --skip id column
SELECT col1, col2, ...
FROM temp1;
Or even better skip the temp1 table at all. Single statement solution:
INSERT INTO original(col1, col2, ..., field) -- skip id column
SELECT col1, col2, ..., 'new_value'
FROM original
WHERE field='old value';
To get what you want you need mechanism like Oracle DEFAULT Values On Explicit NULLs.
But even then you need to drop column from temp1, because:
INSERT INTO original -- only N column
SELECT NULL as id, * -- this will return N+1 columns
FROM temp1;
So you have to use:
ALTER TABLE temp1 DROP COLUMN id;
-- now columns match
INSERT INTO original -- only N column
SELECT NULL as id, * -- this will return N columns and
-- NULL is handled by DEFAULT ON NULL
FROM temp1;
I have a table in mysql like this (the id is primary key):
id | name | age
1 | John | 46
2 | | 56
3 | Jane | 25
Now I want to update the name only if this is empty. If the value is not empty it should duplicate the row with a new id else it should update the name.
I thought it could be done with an if-statement but it doesn't work.
if((select `name` from `table1` where `id` = 3) = '',
update `table1` set `name`='ally' where `id` = 3,
insert into `table1` (`id`,`name`,`age`) values
(4, 'ally', select `age` from `table1` where `id` = 3))
EDIT:
With Spencers answer I made it working using an if in the code. (However I would still like a way to do just a single mysql query).
db.set_database('database1')
cursor = db.cursor()
query = "select IF(CHAR_LENGTH(name)>0,1,0) from table1 where id = {0}".format(id)
cursor.execute(query)
val1 = cursor.fetchone()
if val1[0]:
query = "INSERT INTO `table1` (`id`,`name`,`age`) SELECT {0},{1},`age` FROM `table1` WHERE `id` = {2}".format(new_id, name, id)
cursor.execute(query)
else:
query = "update `table1` set `name` = '{0}' where `id` = {1}".format(name, id)
cursor.execute(query)
db.commit()
If you make like this :
select t.*,
if(
EXISTS(select n.name from table1 n where n.id = 2 and NULLIF(n.name, '') is null) ,
'true',
'false'
) from table1 t
if statement returns "true", becouse in your table exist row where id =2 and name is empty.
like this example, You can edit your query :
if(
EXISTS(select n.name from table1 n where n.id = 3 and NULLIF(n.name, '') is null),
update `table1` set `name`='ally' where `id` = 3,
insert into `table1` (`id`,`name`,`age`) values
(4, 'ally', select `age` from `table1` where `id` = 3)
)
IF is not a valid MySQL statement (outside the context of a MySQL stored program).
To perform this operation, you'll need two statements.
Assuming that a zero length string and a NULL value are both conditions you'd consider as "empty"...
To conditionally attempt an update of the name field, you could do something like this:
UPDATE table1 t
SET t.name = IF(CHAR_LENGTH(t.name)>0,t.name,'ally')
WHERE t.id = 3 ;
The IF expression tests whether the current value of the column is "not empty". If it's not empty, the expression returns the current value of the column, resulting in "no update" to the value. If the column is empty, then the expression returns 'ally'.
And you'd need a separate statement to attempt an INSERT:
EDIT
This isn't right, not after a successful UPDATE... of the existing row. The attempt to INSERT might need to run first,
INSERT INTO table1 (id,name,age)
SELECT 4 AS id, 'ally' AS name, t.age
FROM table1 t
WHERE t.id = 3
AND CHAR_LENGTH(t.name)>0;
We need a conditional test in the WHERE clause that prevents a row from being returned if we don't need to insert a row. We don't need to insert a row if the value 'ally' ...
The use of CHAR_LENGTH >0 is a convenient test for string that is not null and is not zero length. You could use different test, for however you define "empty". Is a single space in the column also considered "empty"?)
I'm inserting some_data (a unique key column), and then using the resulting user_id (the primary key auto-increment column) in a separate statement (not shown)
INSERT IGNORE INTO users (some_data) VALUES ('test');
SELECT LAST_INSERT_ID(); <--- I do stuff with this.
But, of course, if some_data already exists (happens very frequently), LAST_INSERT_ID() returns 0. What is the best way to get the user_id based on the unique key some_data, in this case? Of course I can do a separate WHERE query, but not sure that is the most efficient.
From http://dev.mysql.com/doc/refman/5.0/en/insert-on-duplicate.html
INSERT INTO users ( id, some_col ) VALUES (n,some_val)
ON DUPLICATE KEY UPDATE id=LAST_INSERT_ID(id), some_col=some_val;
Not an ignore but might do the job?
Edit:
To be clear, this will update some_col with some_val and then set the LAST_INSERT_ID to return the id of the duplicate row.
It could just as well be this if you didn't want to update any data on the duplicate but just set the LAST_INSERT_ID() call to give you what you want:
INSERT INTO users ( user_name ) VALUES ( 'bobloblaw' )
ON DUPLICATE KEY UPDATE id=LAST_INSERT_ID( id );
Edit 2:
Use a proc to do the work and get back the id
DELIMITER $$
CREATE PROCEDURE insert_test( val1 varchar(10), val2 varchar(10) )
BEGIN
INSERT INTO test.test_table ( col1, col2 ) SELECT val1, val2 FROM ( select 1 ) as a
WHERE NOT EXISTS (
select 1 from test.test_table t where t.col1 = val1
);
SELECT id FROM test.test_table where col1 = val1;
END $$
DELIMITER ;
Is there a way to accomplish a single table scan in MySQL with an UPDATE? The following is a standard example:
IF EXISTS (SELECT * FROM Table1 WHERE Column1='SomeValue')
UPDATE Table1 SET (...) WHERE Column1='SomeValue'
ELSE
INSERT INTO Table1 VALUES (...)
This is the ideal situation I'd like to happen in MySQL (But this is MsSQL):
UPDATE user SET (name = 'jesse') WHERE userid ='10001'
IF ##ROWCOUNT=0
INSERT INTO user (name) VALUES('jeeeeee')
It's sort of reversed in MySQL. You perform the insert, and if the key already exists, then update the row:
INSERT INTO Table1 (col1,col2,col3) VALUES (val1,val2,val3)
ON DUPLICATE KEY UPDATE col1 = val1, col2 = val2, col3 = val3;
This is predicated on you having a unique key for the table (which you do, right?)