Reputation: 1375
There is structure:
CREATE TABLE `contents` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`title` VARCHAR(45) NULL,
PRIMARY KEY (`id`));
CREATE TABLE `content_values` (
`content_id` INT UNSIGNED NOT NULL,
`field_id` INT UNSIGNED NOT NULL,
`value` VARCHAR(45) NULL,
PRIMARY KEY (`content_id`, `field_id`));
INSERT INTO `contents` VALUES (1,'test-title-1'),(2,'test-title-2');
INSERT INTO `content_values` VALUES (1,4,'test-value');
http://sqlfiddle.com/#!9/028d0/5
And also there are two queries:
select contents.*, content_values.value
from contents
left join content_values on content_values.content_id=contents.id and
content_values.field_id = 4;
select contents.*, content_values.value
from contents
left join content_values on content_values.content_id=contents.id and
content_values.field_id = 4
where content_values.value != '123';
I'm wondering why, as a result of the second query, there is no row, in which there is NULL
for content_value.value
. After all, the condition reads != '123'
.
Who would explain this behavior to me.
Thanks in advance.
Upvotes: 1
Views: 3971
Reputation: 108500
A NULL
value is never "equal to" or "not equal to" a non-NULL value. SQL provides special "IS NULL" and "IS NOT NULL" operators for comparing to NULL values.
The condition in the WHERE clause of the second query negates the "outerness" of the join, by requiring content_value.value to have a non-NULL value. This renders the result equivalent to an INNER JOIN. (For the rows returned when no matching row is found, all of the columns from content_value
will be NULL.)
It seems like you are expecting that condition to be evaluated as if it were written like this:
where ( content_values.value IS NULL OR content_values.value != '123' )
If portability to other DBMS isn't a concern, then we can use the MySQL specific NULL-safe comparison <=>
(spaceship) operator, for example:
where NOT ( content_values.value <=> '123' )
I'm assuming that there is a reason this condition is specified in the WHERE clause rather than the ON clause of the outer join. We can generate a different result if we move the condition from the WHERE clause to the ON clause of the outer join.
ON content_values.content_id = contents.id
AND content_values.field_id = 4
AND content_values.value != '123'
Upvotes: 1
Reputation: 3683
In #2, if you change where->and, it gives same results as #1.
select contents.*, content_values.value
from contents
left join content_values on content_values.content_id=contents.id and content_values.field_id=4
**and** content_values.value != '123'
This means that the joins are getting applied after the DB engine evaluates your select .. from where .. clause.
A WHERE clause further restricts the rows returned by the join query.
More details - SQL join: where clause vs. on clause
Upvotes: 1
Reputation: 661
Use IS NOT NULL to compare NULL values because they are simply unknown.
select contents.*, content_values.value
from contents
LEFT join content_values
on content_values.content_id=contents.id and content_values.field_id=4
where content_values.value IS NULL OR content_values.value != '123';
Upvotes: 0