Conditional insert based on LAST_INSERT_ID - mysql

A nearly identical question was asked here using an IF statement, but he didn't get an actionable answer, just suggested to go here where no IF statements are being used. I've tried to write both an IF statement and a conditional statement using the latter link but I'm stuck (see below).
I want to be able to conditionally insert a row only if the previous insert attempt actually inserted a row (ROW_COUNT > 0). The previous insert could have been duplicate data, so I'm deliberately setting it's LAST_INSERT_ID to null so no subsequent child inserts can occur with that LAST_INSERT_ID. The SQL script is created by a C# script, so it would be very possible that the LAST_INSERT_ID is not pointing to where you'd expect.
Here's a very small example of the script generated code (there are ~3 million rows in the final database):
SET #Vendors_Vendor_ID = (SELECT vendor_ID FROM VENDORS WHERE vendorName = 'PCA');
INSERT IGNORE INTO PCBID (PCBID, PCBDrawing, AssemblyDrawing, PONumber, Vendors_Vendor_ID)
VALUES (11001, '10405', '41606', '091557.5', #Vendors_Vendor_ID);
SET #eventType_ID = (SELECT EVENTTYPE_ID FROM EVENTTYPES WHERE EVENTTYPE = 'Creation');
SET #USER = 'CTHOMAS';
INSERT IGNORE INTO EVENTS (PCBID, EVENTTYPE_ID, DATETIME, USER)
VALUES (11001, #eventType_ID, '2009-06-15T13:15:27', #USER);
SET #EVENT_ID = IF(ROW_COUNT() > 0, LAST_INSERT_ID(), null);
-- THIS DOES NOT WORK
SELECT IF(#EVENT_ID != null,
INSERT INTO EVENTDETAILS (EVENT_ID, ITEMNAME, ITEMVALUE)
VALUES (#EVENT_ID, 'Notes', 'WO#89574'),
null);
-- THIS DOESN'T WORK EITHER
INSERT INTO EVENTDETAILS (EVENT_ID, ITEMNAME, ITEMVALUE)
SELECT #EVENT_ID, 'Notes', 'WO#89574'
WHERE #EVENT_ID != null;
The PCBID table is not a problem for duplicate data, and the Events table has a composite unique key which prevents duplicate data by using INSERT IGNORE:
CREATE TABLE IF NOT EXISTS `uniqueTest`.`events` (
`Event_ID` INT(11) NOT NULL AUTO_INCREMENT ,
`PCBID` INT(11) NOT NULL ,
`EventType_ID` INT(11) NOT NULL ,
`DateTime` DATETIME NOT NULL ,
`User` VARCHAR(45) NOT NULL ,
PRIMARY KEY (`Event_ID`) ,
UNIQUE KEY `PDU_Index` (`PCBID`, `DateTime`, `User`),
The Problem:
I need to be able to do a conditional insert based on the previous insert attempt into the Events table, if it was ignored (because it's duplicate data), don't insert any child rows either. There's currently no way to make any of the EventDetail data unique, there could be multiple rows of legitimate data based on a given Event_ID.
There are four levels deeper possible below the Events table depending on what type of data it is, if the event data doesn't get inserted because it's duplicate data, no child data gets written either (because it'll be duplicate as well).

Your second try was nearly right. You've got to check of NULL values with IS NOT NULL. So use
INSERT INTO EVENTDETAILS (EVENT_ID, ITEMNAME, ITEMVALUE)
SELECT #EVENT_ID, 'Notes', 'WO#89574' FROM DUAL
WHERE #EVENT_ID IS NOT NULL; -- instead of !=
or
INSERT INTO EVENTDETAILS (EVENT_ID, ITEMNAME, ITEMVALUE)
SELECT t.* FROM (
SELECT #EVENT_ID, 'Notes', 'WO#89574'
) t
WHERE #EVENT_ID IS NOT NULL; -- instead of !=
The first one cannot work:
-- THIS DOES NOT WORK
SELECT IF(#EVENT_ID != null,
INSERT INTO EVENTDETAILS (EVENT_ID, ITEMNAME, ITEMVALUE) ...
because the syntax of IF is
IF(expr1,expr2,expr3)
If expr1 is TRUE (expr1 <> 0 and expr1 <> NULL) then IF() returns expr2; otherwise it returns expr3. IF() returns a numeric or string value, depending on the context in which it is used.
Conditional execution of statements is only possible in stored routines. The IF syntax of stored routines would allow something like
IF #EVENT_ID IS NOT NULL THEN
INSERT INTO EVENTDETAILS (EVENT_ID, ITEMNAME, ITEMVALUE) ...
END IF
You've got to distinguish those both syntax versions.

Related

Comparing nulls during join

I read that null cannot be compared with null and the result is always false.
In the below link I am able to compare 2 nulls and the rows are returned.
CREATE TABLE user (id varchar(50), banstatus varchar(100));
INSERT INTO user (id, banstatus) VALUES ('1', '1');
INSERT INTO user values ('2', 'NULL');
CREATE TABLE banstatus (id varchar(50), texti varchar(100));
INSERT INTO banstatus VALUES('1', 'Banned');
Insert into banstatus values ('NULL' , 'NULL');
select * from user as u
join banstatus as b on u.banstatus=b.id
http://sqlfiddle.com/#!9/33f25/1/0
So what is the correct statement about comparison of nulls ?
The values you have inserted are strings. 'NULL' is a string and is a definite value. To insert NULL you shouldn't use the quotes, for example:
INSERT INTO table (field1, field2) VALUES ('foo', NULL)
DEMO
And you can't compare with NULL, to understand its meaning it's something like undefined. Although, You can test if a value is or is not NULL.
You are using 'NULL' (a string) not NULL .. so you are comparing two (same ) strings
In mysql there is a null safe opearator that compare as true both null values
<=>
NULL-safe equal. This operator performs an equality comparison like the = operator, but returns 1 rather than NULL if both operands are NULL, and 0 rather than NULL if one operand is NULL.
https://dev.mysql.com/doc/refman/5.7/en/comparison-operators.html#operator_equal-to
so for a correct null safe join you should use
select * from user as u
join banstatus as b on u.banstatus<=>b.id
In your answer you compare NULL as varchar, not plain NULL.

MySQL insert adds rows with id's that shouldn't exist

I'm facing something weird here, i have a database table from which i select certain rows, in total ~43.000, these fetched rows contain a field called cns_id (note, this is not the primary key/auto increment field), and are inserted into a diffrent table.
Although everything seems to run fine, each time i end up with 94 inserted rows that have a cns_id of 2147483647, an id which doesn't even exist inside the table from which i fetch the data.
Below is the stored procedure i'm using, although this issue also happens when i perform the same kind of query with Eloquent.
DELIMITER //
CREATE PROCEDURE FillCnsGroupWithRates
(
IN groupId INT(10)
)
BEGIN
SET #var1 = groupId;
INSERT INTO
cns_group_rates
(
cns_id,
other_cns_id,
ppc,
pps,
max_price,
notes,
cns_group_id,
created_at,
updated_at
)
SELECT
cns_id,
other_cns_id,
ppc,
pps,
max_price,
notes,
#var1,
now(),
now()
FROM
cns_rates
WHERE
cns_id NOT LIKE '319%'
AND
client_id IS NULL
AND
subordinates IS NULL
AND
valid_from IS NULL;
END //
DELIMITER ;

MySQL INSERT INTO ... SELECT or default value

I'm working on following query:
INSERT INTO a (id, value) VALUES (_id, (SELECT value FROM b WHERE b.id = _id));
Table a: id, value (has a default value)
Table b: id, value
Table b does not contain all requested values. So the SELECT query sometimes returns 0 rows. In this case it should use the default value of a.value.
Is this somehow possible?
Edit:
Solution for empty columns in comments of the post marked as solved.
you can wrap the value in coalesce(max(value), default_value)
INSERT INTO a (id, value)
VALUES (_id, (SELECT coalesce(max(value), default_value)) FROM b WHERE b.id = _id));
The following query would work. First the max(value) is looked up from table b for _id. It would be either NULL or equal to b.value. If it is NULL (checked using the COALESCE function), then the default value of the value column of table a is set as the value.
The default value of the value column of table a is accessed using the DEFAULT function (please refer Reference 1).
INSERT INTO a
SELECT
_id,
COALESCE(max(value), (SELECT DEFAULT(value) FROM a LIMIT 1)) value
FROM b
WHERE id = _id;
SQL Fiddle demo
Reference:
How to SELECT DEFAULT value of a field on SO
If MySQL follows other RDBMS behaviour, the default is only picked up when you don't even specify the field. This means that you need two different INSERT statements:
IF (EXISTS(SELECT * FROM b WHERE id = _id)) THEN
INSERT INTO a (id, value) SELECT _id, value FROM b WHERE id = _id;
ELSE
INSERT INTO a (id) SELECT _id;
END IF;
Or, possibly, something like this...
INSERT INTO a (id, value) SELECT _id, value FROM b WHERE id = _id;
IF ((SELECT ROW_COUNT()) = 0) THEN
INSERT INTO a (id) SELECT _id;
END IF;
Please note, this is conceptual. I've looked up the syntax for you, but I haven't tested it on MySQL.

MYSQL Check for duplicates and only insert if another field is a certain value

If I'm inserting data into a table with the following fields:
serialNumber active country
I need to only insert duplicate serialNumbers if active is no.
So for example: I want to insert a record with serialNumber 1234.
If the serial number doesn't already exist in the table go ahead and add it. If it does already exist, check the value of 'active' active is yes then don't add the new record, if it's no then do add the record.
Any ideas how to achieve this in MYSQL?
If the table lacks the necessary unique keys and you do not have permission, or don't want to set the keys you would need, you can use this alternative:
INSERT INTO `table1`
(`field1`,
`field2`)
SELECT value1,
value2
FROM (SELECT 1) t_1
WHERE NOT EXISTS (SELECT 1
FROM `table1`
WHERE `field1` = value1
AND `field2` = value2);
For yor question it could be written as
INSERT INTO `activity`
(`serialNumbers`,
`active`)
SELECT 1234,
'yes'
FROM (SELECT 1) t_1
WHERE EXISTS (SELECT 1
FROM `activity`
WHERE `serialNumbers` = 1234
AND `active` = 'no');
You can use the ON DUPLICATE KEY statement after an INSERT INTO query to update the row if it already exists. Documentation : https://dev.mysql.com/doc/refman/5.0/en/insert-on-duplicate.html
INSERT INTO table (serialNumber , active, country) VALUES (1010, 'no', 'FR')
ON DUPLICATE KEY UPDATE active='yes';
You can use the insert ... on duplicate key update in MySQL. It is similar to the MERGE used in other SQL databases, but MySQL does not provide the MERGE statement so this is the next best.
INSERT INTO TABLE (serialNumber, active, country)
VALUES (1234, 'active', 'GB') ON DUPLICATE KEY UPDATE country = 'ND';
Also, use INSERT IGNORE if you don't want to generate errors.

mysql triggers simulating assertions

Let’s consider table
Video(
IDvideo(PK),
Date,
Description,
User
)
with mysql I have no way of writing assertions.
Is it possible to simulate the following assertion using one or more triggers ?
create assertion asser1
check (0 =
( select count(*)
from Video
where Date >= DATE_SUB(current_date(),INTERVAL 1 YEAR )
&& Date<=current_date()
group by User
having count(*) > 200
)
)
how should I write that trigger?
Well, the problem is that MySQL doesn't have an equivalent of a STOP ACTION command. So basically, the work arounds are quite dirty:
One way is that you can violate a constraint inside the trigger to bubble an error and cancel the insert:
CREATE TABLE stop_action (
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(35),
UNIQUE KEY (id, name)
);
INSERT INTO stop_action (1, 'Assert Failure');
Then, in the trigger, just try to:
INSERT INTO stop_action (1, 'Assert Failure');
The benefit of that, is that the error that's returned will be a duplicate key error, and the text will include "Assert Failure".
So then your trigger would become:
delimiter |
CREATE TRIGGER asser1_before BEFORE INSERT ON test1
FOR EACH ROW BEGIN
SELECT count(*) INTO test FROM (select count(*)
from Video
where Date >= DATE_SUB(current_date(),INTERVAL 1 YEAR )
&& Date<=current_date()
group by User
having count(*) > 200);
IF test != 0 THEN
INSERT INTO stop_action (1, 'Assert Failure');
END IF;
END;
|
delimiter ;
Now, you'd need to do this before UPDATE as well, otherwise you could update the date into an invalid state. But otherwise, that should at least get you started...