Comparing nulls during join - mysql

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.

Related

How to create unique key that also matches null or empty fields?

I want to create some kind of "unique constraint" that counts null fields as a match.
CREATE TABLE person (
id int,
firstname varchar,
lastname varchar,
dob date,
primary key (id)
);
I want to prevent creating duplicates that who match either the exact values or an empty field.
Example:
INSERT (john, doe, 2000-01-01);
INSERT (john, null, null); //should not be possible, there is already a 'john'
INSERT (null, doe, null); //should not be possible, there is already a 'doe'
INSERT (jane, doe, null); //should be possible, as there is no jane doe yet.
On persist, I want to check if there is already an entry that matches:
WHERE (firstname='john' or firstname is null) and (lastname = 'doe' or lastname is null) and (dob = '2000-01-01' or dob is null)...
And if there is a match: prevent the insertion.
I know this is not a real unique key, but I'm probably missing the correct term here?
Question: is there some kind of generic solution for this type of problem?
you can try a trigger, something like that
CREATE TRIGGER [dbo].[person_Dup_Trigger]
ON [dbo].[person]
AFTER INSERT
AS
BEGIN
SET NOCOUNT ON;
IF EXISTS (
SELECT 1
FROM dbo.person S
INNER JOIN Inserted I ON
-- Test for a duplicate
S.firstname = I.firstname
OR S.lastname = I.lastname
-- But ensure the duplicate is a *different* record - assumes a unique ID
AND S.ID <> I.ID
)
BEGIN
THROW 51000, 'should not be possible, there is already a record', 1;
END;
END;
Notice that condition you want to check
WHERE (firstname='john' or firstname is null) and (lastname = 'doe' or lastname is null) and (dob = '2000-01-01' or dob is null)...
forbids any null insertion, for example it would be impossible to insert
INSERT (john, null, null);
at all, even if it is the first insert in person table. Is this what you want to achieve?
If you only what to prevent multiple null values I'd try this:
create unique index idx1 on person(coalesce(firstname, 'null'), coalesce(lastname, 'null'), coalesce(dob, 'null'));
Edit: Above solution works only with assumption that we do not expect literal value 'null' to represent firstName neither lastName or dob :) because it will not be allowed to insert.
On the other hand if you want only not-null values to be unique (every null is unique for MySQL DB) then index
create unique index idx1 on person(firstname, lastname, dob);
should do the job.

how to insert data in only few column leaving other columns empty or as they are in a mysql table record?

I have created a table named 'students'. It has following fields :
roll_no <- type:Integer Not Null,
course_name <- type:varchar(40) Not Null,
std_surname <- type:varchar(40) Not Null,
std_firstname <- type:varchar(40) Not Null,
emailid <- type:varchar(40) ,
address <- type:varchar(40) Not Null,
income <- type:Integer,
gender <- type:varchar(10) Not Null,
experience <- type:Integer,
in the above fields mentioned some accept null values and most of them don't accept null values or null values are not allowed.
What I want to do is I want to insert some information or data into the columns that do not accept null values and then I want to insert remaining data into the remaining columns later. How can I achieve this?
More specifically how can I insert only few data into specific fields in a record at a time using insert query leaving other fields empty and then insert remaining field values like email or experience ???
Please help me with this problem?
Insert into selected fields
for ex
INSERT INTO
students
(roll_no, course_name, dob, gender)
VALUES
(111, 'MBA', '1991-01-01', 'F');
for inserting NULL values,
have a look at
Insert NULL value into INT column
First insert, you can insert an empty string into the NOT NULL fields, later you can update those fields.
INSERT INTO yourTable (field1, field2, field3) VALUES ("information1", "", "");
UPDATE yourTable SET field2 = "information2", field3 = "information3" WHERE rowId = x;
Forget inserting the empty strings, MySQL will do it by default when you do it as Hytool wrote.

Conditional insert based on LAST_INSERT_ID

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.

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 - insert into and a null value, how to avoid?

I have a SQL query, it looks like that:
insert into tableA(order_id, contractnotenumber, shipmentdate, comment)
values ((select entity_id from tableb where increment_id = '12345678'),
'', '1970-01-01', '');
Now, if the subquery ((select entity_id from tableb where increment_id = '12345678')) returns null, the insert does not work. What I want is an easy way to say that if the subquery returns null, dont do anything. I tried insert ignore, but this inserts the row with 0 instead of null, which works, but its not what I want. In SQL Server I used if not exists(select...), but this does not work with MYSQL.
Ideas?
Thanks!
insert into tableA(order_id, contractnotenumber, shipmentdate, comment)
select entity_id, '', '1970-01-01', '' from tableb where increment_id = '12345678'
This won't insert anything if the row isn't found in tableb
You could also try mysql> insert ignore