2010-08-04 53 views
3

为什么这个查询返回0行?MySQL mystery:空值与非空字符串没有区别

select t.f1, t.f2 
from (select null f1, 'a' f2 from dual) t 
where t.f1<>t.f2; 

这是我有的复杂查询的蒸馏版本。我想比较包含一对一相关数据的两个表,我想选择那些包含特定字段不同值的行。但也可能出现这样的情况,其中一个表中缺少一行。 LEFT JOIN正确地为这些行返回空值,但是,WHERE子句不正确地(或意外)将这些行过滤掉。

为什么 - 在这种情况下 - 'null'是不是不同于任何非空值(如'a')?

什么让我发疯的是,这

select t.f1, t.f2 
from (select null f1, 'a' f2 from dual) t; 

返回1行(如我所料),但这

select t.f1, t.f2 
from (select null f1, 'a' f2 from dual) t 
where t.f1=t.f2; 

返回0行! 所以null不等于'a',null与'a'不同!

请...有人可以解释这一点吗?

+0

谢谢大家......是的,我已经通过向WHERE子句添加IS NULL条件解决了我的实际问题。然而,我很惊讶,'a'<> NULL和'a'= NULL都不能评估真实。 – tato 2010-08-04 11:17:48

+0

我确切地知道你的意思,这是使用SQL的一个流血的逻辑窍门。 :) 即使经过多年的SQL工作,我总是必须在我的大脑中做额外的开关以允许NULL。 – 2013-08-29 03:26:29

回答

5

没错。 NULL表示一个未知值,而不是任何特定值(它与C中的NULL不同,或者Ruby中的nil等不同)。在SQL中,如果将某些内容与未知值进行比较,则结果也是未知的。你不会得到WHERE条件未知的行。

试试这个:

SELECT NULL <> 2; 

,你会看到NULL作为结果。

试试这个:

SELECT * FROM t WHERE NULL; 

,并没有行会出来,即使表t是巨大的。

如果你真的需要你说你想要的(和我不提倡这样),你可以做这样的事情:

SELECT T.f1, T.f2 
FROM (SELECT NULL f1, 'a' f2) T 
WHERE ((T.f1 IS NULL OR T.f2 IS NULL) 
    AND (T.f1 IS NOT NULL OR T.f2 IS NOT NULL)) 
    OR T.f1 <> T.f2 
0

尝试做这个查询:

select * from dual where NULL = NULL 

它返回0行。这是因为要将值与空值进行比较,您必须执行IS NULLIS NOT NULL,否则将返回false

3

NULL的概念是混乱的新人到SQL公共源,他们经常认为NULL被视为其他值。

事实并非如此。从概念上讲,NULL的意思是“缺少一个未知值”,因此它的处理方式非常不同。

你所看到的很容易解释。考虑下面的例子:

CREATE TABLE mytb (id int, value int); 

INSERT INTO mytb VALUES (1, 100); 
INSERT INTO mytb VALUES (2, 200); 
INSERT INTO mytb VALUES (3, NULL); 
INSERT INTO mytb VALUES (4, 400); 

上面意味着对于id = 3行,该值是“未知”。它可能是300,或者它可能是100,或其他任何东西。

因此,当您请求如下:

SELECT * FROM mytb WHERE value <> 100; 
+------+-------+ 
| id | value | 
+------+-------+ 
| 2 | 200 | 
| 4 | 400 | 
+------+-------+ 
2 rows in set (0.00 sec) 

没有返回与id = 3行,因为NULL <> 100返回 “未知”。我们不知道id = 3行的值是否为100,因此表达式不会返回true。我也不会返回false。它返回“未知”(NULL)。

只有在表达式为true时才能满足WHERE子句的条件。当你比较NULL时,表达式永远不会成立。这将是“未知的”。

0

NULL值不算什么,它不能等于或不等于某物。 如果你想检查你的价值是零 - 使用“IS NULL”语句:

select t.f1, t.f2 
from (select null f1, 'a' f2 from dual) t 
where t.f1 IS NULL 

如果要检查你的值相等或不等于 - 你可以在空列使用COALESCE功能:

select t.f1, t.f2 
from (select null f1, 'a' f2 from dual) t 
where COALESCE(t.f1, '')<>COALESCE(t.f2, ''); 
+0

该函数被称为'COALESCE'。 – Amadan 2010-08-04 11:06:17

+0

哦,当然你是对的。但通过这种方式链接是正确的:) – 2010-08-04 11:41:56