Don't update if null is given in a procedure - mysql

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

Related

MySQL Upsert with Insert Only Column Value

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`)
;

MySql if else statement, if not valid in this position

I have a vet table and a medical table with a 1 to many relationship, and the ID's are auto incremented.
CREATE TABLE vet(
vetID INT NOT NULL AUTO_INCREMENT,
vetPractice varchar(35),
Address varchar(150),
contactNumber varchar (15),
PRIMARY KEY (VetID)
);
CREATE TABLE medical(
medicalID INT NOT NULL AUTO_INCREMENT,
medication VARCHAR (200),
PRIMARY KEY (medicalID),
FOREIGN KEY (vetID) REFERENCES vet(vetID)
);
Users can enter details of a vet, i want a query to determine;
if the the vet details entered already exist, then update the foreign key in vetID(medical) with the entered vetID.
else if the vet does not exist create a new vet and update the foreign key in vetID(medical) with the newly created vetID.
I have the following query
IF EXISTS(SELECT * FROM vet WHERE vetPractice = "inputValue")
THEN
UPDATE medical set value vetID = (Select max(vetID) from vet)
ELSE
INSERT INTO vet values (null, "newVetPractice", "NewAddress", "newContactNumber", "NewEmergencyNumber" );
Then
update medical set value vetID = (Select max(vetID) from vet);
END IF;
However, i am not familiar with if else's in mySQL is this the correct format, i have seen somethings about stored procedures.
Any help would be appreciate.
I'm not really clear about your logic; but it seems like you wanted it in a stored procedure format.
CREATE PROCEDURE 'sp_Med' (IN 'in_vetPractice' VARCHAR(35))
LANGUAGE SQL
NOT DETERMINISTIC
CONTAINS SQL
SQL SECURITY DEFINER
BEGIN
DECLARE ckExists int;
SET ckExists = 0;
SELECT count(*) INTO ckExists from vet WHERE vetPractice = in_vetPractice;
IF (ckExists > 0) THEN
UPDATE medical SET vetID = (Select max(vetID) FROM vet WHERE vetPractice = in_vetPractice)
ELSE
INSERT INTO vet VALUES (NULL, "newVetPractice", "NewAddress", "newContactNumber", "NewEmergencyNumber");
UPDATE medical SET vetID = LAST_INSERT_ID();
END IF;
END;
Execute it like
CALL sp_Med('newPractice')
I think you have to update your query, and this is the general syntax you have to use rather tha n yours:-
INSERT INTO `tableName` (`a`,`b`,`c`) VALUES (1,2,3)
ON DUPLICATE KEY UPDATE `a`=VALUES(`a`), `b`=VALUES(`b`), `c`=VALUES(`c`);
This query will insert records if they are not present, and on presence it will update them.
So use this rather than your approach

MySQL stored procedure anomaly: One line gets executed, another skipped?

I have a stored procedure, that checks if there is a user with the same e-mail address as the input, if not, then registeres one.
Here is the table:
CREATE TABLE IF NOT EXISTS `overkill`.`accounts` (
`accountID` INT NOT NULL AUTO_INCREMENT ,
`email` VARCHAR(64) NOT NULL ,
`firstName` VARCHAR(32) NOT NULL ,
`lastName` VARCHAR(32) NOT NULL ,
`passSaltedSHA` BINARY(20) NOT NULL ,
`salt` BINARY(20) NOT NULL ,
`gender` ENUM('m','f') NOT NULL ,
`birthDate` DATE NOT NULL ,
`regTime` TIMESTAMP NOT NULL ,
PRIMARY KEY (`accountID`) )
ENGINE = InnoDB;
Here is the stored procedure:
DELIMITER $$
CREATE PROCEDURE `overkill`.`registerUser` (
IN emailIN VARCHAR(64),
IN passwordIN VARCHAR(16),
IN firstNameIN VARCHAR(32),
IN lastNameIn VARCHAR(32),
IN birthIN DATE,
IN genderIN ENUM('f','m'))
BEGIN
DECLARE existingMailAccLOG INT DEFAULT NULL;
DECLARE saltLOC CHAR(40);
DECLARE regSuccessLOC BOOLEAN DEFAULT FALSE;
SELECT COUNT(*) INTO existingMailAccLOG FROM `overkill`.`accounts` WHERE `accounts`.`email` = emailIN;
IF existingMailAccLOG = 0 THEN
SET saltLOC = SHA1(rand());
SET regSuccessLOC = TRUE;
INSERT INTO `overkill`.`accounts` (`email`, `firstName`, `lastName`, `passSaltedSHA`, `salt`, `gender`, `birthDate`) VALUES(emailIN, firstNameIN, lastNameIn, UNHEX(SHA1(CONCAT(passwordIN, saltLOC))), UNHEX(saltLOC), genderIN, birthIN);
END IF;
SELECT regSuccessLOC AS `registered`, saltLOC AS `salt`;
END
If I call:
CALL registerUser("abc#def.com", "pass", "firstn", "lastn", "2012-01-01", "f");
It inserts a line into the accounts table, but forgets to return the proper values that I set inside the IF
SET saltLOC = SHA1(rand());
SET regSuccessLOC = TRUE;
How is it even possible? Why are theese lines skipped and INSERT still gets executed, without mistake?
Try to add "#" in front of your variable names after DECLARE keyword. It can cause some confusion, as it is described here: MySQL: #variable vs. variable. Whats the difference? (Part2) and here: MySQL: #variable vs. variable. Whats the difference?

mysql trigger not working on insert

Table: items
Create Table:
CREATE TABLE `items` (
`ite_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`itemName` varchar(40) DEFAULT NULL,
`itemNumber` int(10) unsigned NOT NULL,
PRIMARY KEY (`ite_id`),
UNIQUE KEY `itemName` (`itemName`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1
delimiter |
create trigger item_beforeinsert before insert on items
for each row begin
if new.itemNumber < 50 then
set new.ite_id = null;
end if;
end;
|
now the following command doesn't cause a trigger
insert items( itemname, itemnumber) values ( 'xyz', 1 );
any help would be very much appreciated, thanks!
Your ite_ID is not null and you want to set it null with your trigger, beside that it's auto increment, so you wont be able to 'control' all the values to assign to that field, I.E it wont overwrite values
It'd be
insert INTO items( itemname, itemnumber) values ( 'xyz', 1 );
also, since you have set ite_id as NOT NULL, you can't use a set new.ite_id = null;
For auto incremented primary key fields you can pass NULL value while inserting. MySQL automatically assigns auto generated value. It is not an error setting up NULL to it BEFORE insert. And hence trigger didn't fire an error.
Example:
insert into items( ite_id, ... ) values ( null, ... );
The above statement is valid and works, since ite_id field is primary key with auto increment.

MySQL Stored Procedure DELETEs all Rows instead of just one

I'm trying out stored procedures for the first time, and I can't figure out what I'm doing wrong.
Here's the table definition:
CREATE TABLE `answers` (
`anid` int(11) unsigned NOT NULL auto_increment,
`uid` int(11) NOT NULL,
`dtid` int(11) NOT NULL,
`answer` text NOT NULL,
PRIMARY KEY (`anid`),
KEY `uid` (`uid`),
KEY `dtid` (`dtid`)
) ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
anid is the primary key, uid is user id, dtid is datum id, and answer is the answer provided.
Whenever I get a new answer for a given datum (question) and user id, I want to first delete any old answer to that same question by that same user, and then insert the new one.
Here's the procedure declaration:
DELIMITER //
CREATE PROCEDURE new_answer(uid INT(11),dtid INT(11),answer TEXT)
BEGIN
DELETE FROM `answers` WHERE `uid` = uid AND `dtid` = dtid;
INSERT INTO `answers` SET `uid` = uid, `dtid` = dtid, `answer` = answer;
END//
However, whenever I CALL new_answer ALL existing rows are deleted, and that one new answer is now the only row in the table.
Hope it's something simple, thanks for your help.
Rename your parameters:
DELIMITER //
CREATE PROCEDURE new_answer(p_uid INT(11),p_dtid INT(11),p_answer TEXT)
BEGIN
DELETE FROM `answers` WHERE `uid` = p_uid AND `dtid` = p_dtid;
INSERT INTO `answers` SET `uid` = p_uid, `dtid` = p_dtid, `answer` = p_answer;
END//
You should probably try naming procedure arguments different than table columns.
Anyway, it looks like all you need is a single INSERT ... ON DUPLICATE KEY UPDATE query.
I'm not familiar with stored procedures, but what about renaming your function parameters to x and y instead of the very same as the column names?