MySQL Upsert with Insert Only Column Value - mysql

In MySQL, is it possible to do an upsert but only set a column value on insert (and not set the column value on update).
For example, for a createdBy column, we only want to set the value on insert, we don't want to override that value on update (because we lose who originally inserted the column).
Note that we only know the currently logged in user. So updatedBy is simple -- always use the value of the logged in user. But createdBy is hard. Use the value of the logged in user but only for an insert -- don't override this on update.
Example schema:
CREATE TABLE `movie` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`name` NVARCHAR(100) NOT NULL,
`createdBy` NVARCHAR(100) NOT NULL,
`updatedBy` NVARCHAR(100) NOT NULL,
UNIQUE INDEX (`name`)
);
Example of a standard upsert:
INSERT INTO `movie` (`name`, `createdBy`, `updatedBy`)
VALUES ('The Matrix', 'Jill', 'Jill')
ON DUPLICATE KEY UPDATE
`id` = LAST_INSERT_ID(`id`),
`name` = VALUES(`name`),
`createdBy` = VALUES(`createdBy`),
`updatedBy` = VALUES(`updatedBy`)
;
Here's my attempt to only set the createdBy column on insert using IFNULL. But this doesn't work and results in createdBy always being null.
INSERT INTO `movie` (`name`, `createdBy`, `updatedBy`)
VALUES ('The Matrix', IFNULL(`createdBy`, 'Jill'), 'Jill')
ON DUPLICATE KEY UPDATE
`id` = LAST_INSERT_ID(`id`),
`name` = VALUES(`name`),
`createdBy` = VALUES(`createdBy`),
`updatedBy` = VALUES(`updatedBy`)
;
Results wanted:
Case 1: Jill runs an upsert that inserts a row.
id = 1
name = 'The Matrix'
createdBy = 'Jill' // Created by Jill
updatedBy = 'Jill' // Last updated by Jill
Case 2: Bob runs an upsert that updates the same row.
id = 1
name = 'The Matrix Reloaded'
createdBy = 'Jill' // Created by Jill (do not change value on update)
updatedBy = 'Bob' // Last updated by Bob

I created a fiddle guessing that Name is the Key, feel free to give it a try here.
This is the basic syntax:
INSERT INTO `movie` (`name`, `UpdatedBy`,`CreatedBy`)
VALUES ('Star wars', 'NameA','NameB')
ON DUPLICATE KEY UPDATE `UpdatedBy` = VALUES(`UpdatedBy`)
;
Notice: NameA and NameB can be the same so you dont get nulls on inserts
Hope it helps :)

Try this:
INSERT INTO `movie` (`name`, `createdBy`)
VALUES ('The Matrix', 'Jill')
ON DUPLICATE KEY UPDATE `name` = VALUES(`name`)
;

Related

Don't update if null is given in a procedure

I know an around-about way of accomplishing this but I would like to know the clean and best way to solve my problem. I am using an INSERT INTO with an ON DUPLICATE KEY UPDATE. Sometimes a value is not given but I still have to pass it into the parameter of the procedure otherwise it would fail. So I have been passing in a null value but this will update the field with nulls and I will lose data. So, I would like to "ignore" a field if it a null value gets passed into it. In other words just not update it or get the current value instead and pass that in.
I could use multiple IF statements to just check if a value is null or not but this procedure is about 20 values long and that would seem ridiculous and gratuitous. If there is a better way, I know that it can be done differently.
I'm only going to include part of my procedure for simplicity sake.
PROCEDURE `p_my_record_create`(
IN in_group varchar(255),
IN in_package varchar(255),
IN in_type enum('A', 'M'),
IN in_uid varchar(255),
IN in_member_id int(11),
IN in_first_name varchar(255)
)
BEGIN
INSERT INTO myDatabase.my_record
(`group`, `package`, `type`, `uid`, `member_id`, `first_name`)
VALUES
(in_group, in_package, in_type, in_uid, in_member_id, in_first_name)
ON DUPLICATE KEY UPDATE
`group` = in_group,
`package` = in_package,
`type` = in_type, #if this is passed in as null then I would like for it to be "ignored" or if any of them are.
`uid` = in_uid,
`client_member_id` = in_client_member_id,
`first_name` = in_first_name;
SELECT
record_id
FROM
myDatabase.my_record
WHERE
record_id = LAST_INSERT_ID();
END
If there is a simple way to accomplish this in MySQL, please enlighten me that would really help. Thanks.
PROCEDURE `p_my_record_create`(
IN in_group varchar(255),
IN in_package varchar(255),
IN in_type enum('A', 'M'),
IN in_uid varchar(255),
IN in_member_id int(11),
IN in_first_name varchar(255)
)
BEGIN
INSERT INTO myDatabase.my_record
(`group`, `package`, `type`, `uid`, `member_id`, `first_name`)
VALUES
(in_group, in_package, in_type, in_uid, in_member_id, in_first_name)
ON DUPLICATE KEY UPDATE
`group` = in_group,
`package` = COALESCE(in_package, `package`),
`type` = COALESCE(in_type, `type`),
`uid` = in_uid,
`client_member_id` = in_client_member_id,
`first_name` = COALESCE(in_first_name, `first_name`);
SELECT
record_id
FROM
myDatabase.my_record
WHERE
record_id = LAST_INSERT_ID();
END

SQL Syntax for checking duplicates prior to update (not DUPLICATE KEY UPDATE)

I have a syntactical question with attempting an UPDATE if 2 non key fields are matched. -- INSERT if not matched.
Let me start by saying I have a working query that involves a SELECT with an ON DUPLICATE KEY UPDATE. Now I am just curious, can it be done differently?
Out of sheer curiosity, I am trying this in a manner that will not require the primary key. This really is just an experiment to see if it can be done.
What I want is like ON DUPLICATE KEY UPDATE -- However:
let's pretend I don't know the key , and
let's pretend I can't get the key with a SELECT
Here is the data structure I have:
+--------------------------------------------------------------------------+
| id | contractor_id | email_type_id | email_address |
+--------------------------------------------------------------------------+
Table creation:
CREATE TABLE `email_list` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`contractor_id` int(11) NOT NULL,
`email_type_id` int(11) NOT NULL,
`email_address` varchar(45) COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `id_UNIQUE` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
Now what I am trying to do without a select and without ON DUPLICATE KEY UPDATE is -- If contractor_id and email_type_id are matched -- UPDATE the email_address -- else INSERT.
If have tried this -- (I know I am breaking my own rule of no SELECT):
IF EXISTS (SELECT * FROM email_list WHERE contractor_id = 1166)
UPDATE email_list SET (email_address='herky_jerky#snailmail.com')
WHERE contractor_id = 1166 AND email_type_id = 4
ELSE
INSERT INTO email_list VALUES (
contractor_id = 1166,
email_type_id = 4,
email_address = 'herky_jerky#snailmail.com');
I understand why this doesn't work .. I just don't know what the fix for it is -- It feels a little clunky too using an IF - ELSE statement. Also I don't want to use a SELECT -- So then I thought about using just an IF like:
UPDATE email_list SET email_address = 'herky#jerky.com'
WHERE contractor_id = 1166 AND email_type_id = 4
IF ##ROWCOUNT=0
INSERT INTO email_list VALUES (
contractor_id = 1166,
email_type_id = 4,
email_address = 'herky#jerky.com');
But I don't understand why that one doesn't work. This is just an exercise to see how creative one can be with this type of query. I think both of my ideas are doable -- Can anyone find a fix for either query to make it work?
I'd also love to see other, more creative, ways of attempting what I am asking as well!
I'd implement this using an UPDATE followed by a test of ROW_COUNT() and if no rows were updated, then INSERT.
drop table if exists t;
create table t (id int, x int, y int, str varchar(255));
insert into t (id, x, y, str) values (1, 2, 3, 'foo');
select * from t;
update t set str = 'bar'
where x = 2 and y = 3;
insert into t (id, x, y, str)
select 1, 2, 3, 'inserted'
from dual
where row_count() = 0;
select * from t;
update t set str = 'baz'
where x = 20 and y = 30;
insert into t (id, x, y, str)
select 10, 20, 30, 'baz'
from dual
where row_count() = 0;
select * from t;
drop table t;
You can see it in action here: https://rextester.com/FRFTE79537
The idea here is you do the UPDATE first, followed by an INSERT ... SELECT where the SELECT only returns a row if ROW_COUNT() = 0 is true, and that's only true if the UPDATE didn't match any rows.

Update if exists, otherwise insert (without unique keys)

I have a table that is created like this:
'CREATE TABLE `boss_kills` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`characterid` int(11) DEFAULT NULL,
`mobid` int(11) DEFAULT NULL,
`amount` int(11) NOT NULL DEFAULT ''0'',
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=25 DEFAULT CHARSET=latin1'
Goal: I'm trying to create a system where a player kills a boss in the game and it records the boss's ID and the player's ID in the table. I want to be able to write one query where it updates if the player is already logged with the specific boss and inserts if he or she isn't.
Research: I did a lot of research online and people suggest the INSERT INTO ... ON DUPLICATE KEY UPDATE, but that only works if you either know your primary key or have a unique key, none of which I have or know.
Tries: I've tried
IF EXISTS (...) THEN UPDATE (...) ELSE INSERT(...)
and
UPDATE (...) IF ##ROWCOUNT = 0 INSERT INTO (...)
but they don't work. I get syntax errors.
If needed I can provide the errors thrown by the above tries. My current code for trying to update is this (but it throws a SQL syntax error):
Attempt 1:
UPDATE boss_kills
SET amount = amount + 1
WHERE characterid = ? AND mobid = ?
IF ##ROWCOUNT = 0
INSERT INTO boss_kills (characterid, mobid, amount) VALUES (?, ?, 1)
Attempt 2:
IF NOT EXISTS (SELECT id FROM boss_kills WHERE characterid = ? AND mobid = ?)
THEN
INSERT INTO boss_kills VALUES (DEFAULT, ?, ?, 1)
ELSE
UPDATE boss_kills SET amount = amount + 1 WHERE characterid = ? AND mobid = ?
It seems like (characterid, mobid) could make a unique index constraint for your table, thus allowing you to use INSERT ... ON DUPLICATE KEY UPDATE.
Use below script to create your table
CREATE TABLE boss_kills (
id int(11) NOT NULL AUTO_INCREMENT,
characterid int(11) DEFAULT NULL,
mobid int(11) DEFAULT NULL,
amount int(11) NOT NULL DEFAULT 0,
PRIMARY KEY ( id ),
UNIQUE ( characterid, mobid )
) ENGINE=MyISAM AUTO_INCREMENT=25 DEFAULT CHARSET=latin1;
Note that I've removed backticks around your table and column names - they are not necessary.
Inserting a row
INSERT INTO boss_kills (characterid, mobid, amount)
VALUES (?, ?, 1)
ON DUPLICATE KEY UPDATE amount = amount + 1;

ON DUPLICATE KEY UPDATE with WHERE

I've got an INSERT ... ON DUPLICATE KEY UPDATE query and I am trying to add WHERE clause to it:
INSERT INTO `product_description` (
`product_id`,`language_id`,`name`,
`description`,`meta_description`,
`meta_keyword`,`tag`
) VALUES (
$getProductId, $languageId, '$pName', '$pDescription', '', '', ''
)
ON DUPLICATE KEY UPDATE
`name` = '$pName',
`description` = '$pDescription'
I want to restrict the UPDATE to those 2 conditions:
WHERE `model` = 'specific-model' AND `sku` NOT LIKE '%B15%'
If I add this part of query to the end of the original query I get a MySQL syntax error. What would be a working solution?
Update: Please note that model and sku are in another table, and the common key is product_id
I would suggest you to use some sort of prepared statement instead of concatenating strings, so you should do something like this:
INSERT INTO `product_description` (
`product_id`, `language_id`, `name`,
`description`, `meta_description`,
`meta_keyword`, `tag`
) VALUES (?, ?, ?, ?,'','','')
but this is not part of the question.
I was thinking of answering with a simple CASE WHEN but the challenging part of your question is that the restrict conditions are not in the product_description table but are from another table. So I think we can just use a TRIGGER:
CREATE TRIGGER product_description_upd
BEFORE UPDATE ON product_description
FOR EACH ROW
IF NOT EXISTS(SELECT * FROM models
WHERE product_id=new.product_id
AND model='Abc' AND `sku` NOT LIKE '%B15%') THEN
SET new.name=old.name;
SET new.description=old.description;
END IF;
//
then you can use an INSERT query like:
INSERT INTO `product_description` (col1, col2, ...)
VALUES (..., ..., ...)
ON DUPLICATE KEY
UPDATE name=VALUE(name),description=VALUE(description)
Please see a fiddle here.
The only thing to note here is that even a standard UPDATE query will be affected.
CREATE TABLE product_description (
product_id INT PRIMARY KEY,
name VARCHAR(100),
description VARCHAR(100)
);
CREATE TABLE models (
product_id INT,
model VARCHAR(100),
sku VARCHAR(100)
);
INSERT INTO models VALUES
(1, "Abc", "ZZZ"),
(2, "Abc", "B15");
INSERT INTO product_description VALUES
(1, "Car", "Red"),
(2, "Truck", "Pink");
INSERT INTO `product_description` VALUES (1, "NewCar", "DeepRed")
ON DUPLICATE KEY UPDATE name=VALUES(name), description=VALUES(description);
Assuming, product_id must be in models.
INSERT INTO `product_description` (product_id, name, description)
SELECT models.product_id, "SuperCar" as name, "DarkRed" as description
FROM `models` WHERE model="Abc" AND `sku` NOT LIKE "%B15%"
ON DUPLICATE KEY UPDATE name="UpdatedCar", description="UpdatedRed";
refer to http://sqlfiddle.com/#!9/69624e/1
Hopefully this solves the problem. You can play with SELECT query for different result.

Inserting new random uuid() in two tables in every insert

I have created two tables which i want to insert similar data in.
CREATE TABLE one(
one_id INT NOT NULL AUTO_INCREMENT,
name VARCHAR(50) NOT NULL,
PRIMARY KEY (one_id)
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE two(
two_id INT NOT NULL AUTO_INCREMENT,
name VARCHAR(50) NOT NULL,
PRIMARY KEY (two_id)
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
every time in run insert.
To do that,i am using transactions
START TRANSACTION;
SET #name = uuid();
INSERT INTO one(one_id,name) VALUES (Null,#name);
INSERT INTO two(two_id, name) VALUES (Null, #name);
COMMIT;
This does not produce new values on new inserts.It however inserts the same data in the field name as i wanted.
How can i make this work?.
I don't see a need to move to transactions in order to do that, just add an before insert trigger to the table .
Something like :
CREATE TRIGGER `ONE_TABLE_TRIGG` BEFORE INSERT ON `one`
FOR EACH
ROW BEGIN
SET NEW.name= UUID( );
END ;
You can check if it's null before doing that. do this on both tables and you're good or add insert to the other table on 1 trigger.
I solved it without much complexities by having several having several transaction statements in the same file
START TRANSACTION;
SET #name = uuid();
INSERT INTO one(one_id,name) VALUES (Null,#name);
INSERT INTO two(two_id, name) VALUES (Null, #name);
COMMIT;
START TRANSACTION;
SET #name = uuid();
INSERT INTO one(one_id,name) VALUES (Null,#name);
INSERT INTO two(two_id, name) VALUES (Null, #name);
COMMIT;
/*
Etc
*/