MySQL IS NULL / IS NOT NULL行为不正确?


18

请查看此表:

mysql> desc s_p;

+-------------------------+------------------+------+-----+---------+----------------+    
| Field                   | Type             | Null | Key | Default | Extra          |
+-------------------------+------------------+------+-----+---------+----------------+
| id                      | int(10) unsigned | NO   | PRI | NULL    | auto_increment |
| s_pid                   | int(10) unsigned | YES  | MUL | NULL    |                |
| sm_id                   | int(10) unsigned | YES  | MUL | NULL    |                |
| m_id                    | int(10) unsigned | YES  |     | NULL    |                |
| created                 | datetime         | YES  |     | NULL    |                |
| s_date                  | datetime         | YES  |     | NULL    |                |
| estimated_date          | datetime         | YES  | MUL | NULL    |                |
+-------------------------+------------------+------+-----+---------+----------------+

现在看看这些查询:

mysql> select count(*) from s_p where estimated_date is null;
+----------+
| count(*) |
+----------+
|   190580 |
+----------+
1 row in set (0.05 sec)

mysql> select count(*) from s_p where estimated_date is not null;
+----------+
| count(*) |
+----------+
|    35640 |
+----------+
1 row in set (0.07 sec)

mysql> select count(*) from s_p;
+----------+
| count(*) |
+----------+
|  1524785 |
+----------+

上面的计数不匹配。根据我的理解:

当查询而没有where子句时,Count with IS NULL和Count with IS NOT NULL应该等于count。

对这里发生的事情有任何想法吗?

================================================== =

2012年2月17日更新

从那以后,我发现很多人都在问estimate_date当前具有的值的种类。答案是:

mysql> select distinct date(estimated_date) from s_p;

+----------------------+
| date(estimated_date) |
+----------------------+
| NULL                 |
| 2012-02-17           |
| 2012-02-20           |
| 2012-02-21           |
| 2012-02-22           |
| 2012-02-23           |
| 2012-02-24           |
| 2012-02-27           |
| 2012-02-28           |
+----------------------+
9 rows in set (0.42 sec)

如您所见,estimated_date具有NULL或有效的datetime值。没有零或空字符串“”。

如果estimate_date的索引有问题,是否可以发生(原始问题)?

================================================== =

2012年2月18日更新

这是显示创建表的输出:

 | s_p | CREATE TABLE `s_p` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `s_id` int(10) unsigned DEFAULT NULL,
  `sm_id` int(10) unsigned DEFAULT NULL,
  `m_id` int(10) unsigned DEFAULT NULL,
  `created` datetime DEFAULT NULL,
  `estimated_date` datetime DEFAULT NULL,
   PRIMARY KEY (`id`),
   KEY `sm_id` (`sm_id`),
   KEY `estimated_date_index` (`estimated_date`) USING BTREE,
  ) ENGINE=InnoDB AUTO_INCREMENT=1602491 DEFAULT CHARSET=utf8 |

同样,我只能在此处怀疑index_date上的索引。

另外,MySQL服务器版本为5.5.12。


3
除非在运行3个查询之间以及在运行3个查询期间向表提供新行,否则不会发生这种情况!
ypercubeᵀᴹ

6
您确定要执行a select count(*)和不执行select count(estimated_date)吗?如果这是唯一的计数,那么这两个将返回不同的结果,因为会忽略NULL。

6
我不确定以下内容是否可以在MySQL中运行,但是您可以尝试运行:SELECT COUNT(*),SUM(CASE WHEN estimated_date IS NULL THEN 1 ELSE 0 END),SUM(CASE WHEN estimated_date IS NOT NULL THEN 1 ELSE 0 END) from s_p-一次即可获得所有计数。
Damien_The_Unbeliever 2012年

1
这些是您正在运行的确切查询吗?
gbn 2012年

4
另外,如果这是MyISAM,您可以CHECK TABLE在其上运行吗?考虑到广较大的全行数,我猜一个DELETE疯了的地方。
Naltharial

Answers:


6

你有零个约会吗?0000-00-00 00:00:00MySQL认为datetime值可以同时满足is nullis not null

steve@steve@localhost > create temporary table _tmp (a datetime not null);
Query OK, 0 rows affected (0.02 sec)

steve@steve@localhost > insert into _tmp values ('');
Query OK, 1 row affected, 1 warning (0.00 sec)

Warning (Code 1264): Out of range value for column 'a' at row 1
steve@steve@localhost > select a from _tmp where a is null;
+---------------------+
| a                   |
+---------------------+
| 0000-00-00 00:00:00 |
+---------------------+
1 row in set (0.00 sec)

steve@steve@localhost > select a from _tmp where a is not null;
+---------------------+
| a                   |
+---------------------+
| 0000-00-00 00:00:00 |
+---------------------+
1 row in set (0.00 sec)

参见:http : //bugs.mysql.com/bug.php?id=940

这被分类为“不是错误”。他们建议一种解决方法:使用严格模式,它将插入警告转换为错误。

综上所述,仅此一项并不能解释您获得的结果的巨大差异(is nullis not null计数之和应超过无限制的计数)...


DATEDATETIME定义为时,将出现错误NOT NULL。在这里的问题中,该列定义为可为空。但是,此错误是仅在严格模式下运行MySQL的另一个原因。
ypercubeᵀᴹ

我已经更新了原始帖子,以便在estimate_date列中显示当前值。它没有注册文件或空字符串“”。
user1213259 '02

1
@yper或选择其他DBMS的理由...
ErikE 2012年

1
@ErikE:有时候,这不是一个选择。无论您使用哪种DBMS,您总会发现选择anotehr DBMS的理由。
ypercubeᵀᴹ

FYI ToadSQL将0000-00-00 00:00:00显示为{null},这进一步加剧了困境!什么样的恶梦。FTR我们在问题列上没有索引。这是在5。6。15日志上。
's

3

@ypercube:

最近有人问我是否认为回归错误“当WHERE操作数位于主键或唯一索引中时,SELECT COUNT(DISTINCT)使InnoDB崩溃”。

这是我的回复(最初是在这里):

http://www.chriscalender.com/?p=315&cpage=1#comment-1460

我不认为这是相同的错误。此错误更多是与崩溃有关,并且特别需要选择计数(DISTINCT),并且WHERE操作数位于主键或唯一索引中。

您的错误/问题没有DISTINCT,不会崩溃,并且datetime列上的索引不是主键也不是唯一的。但是,袖手旁观有点奇怪,因此我进行了一些搜索,并遇到了此错误,该错误似乎更可能涉及/相关:

http://bugs.mysql.com/bug.php?id=60105

实际上,它被指定为“不是bug”,但是它显示/描述了当日期/日期时间为'0000-00-00'并使用IS NULL和IS NOT NULL时如何遇到奇怪的行为。

我想知道您是否有任何可能影响计数的“ 0000-00-00”行?

请注意,在错误报告中评论的开发人员也提到了此页面:

如果不是这样,我肯定会建议升级并在最新的5.5(5.5.21(截至2012年2月22日))上进行尝试,因为距5.5.12已有9个月(和9个发行版)。被释放。

请注意,您应该能够转储表(和数据)并将其导入到另一个测试实例中,只是为了对其进行测试。这样,您就不会影响生产机器,并且可以在几分钟内设置测试实例。

然后,如果那仍然没有改变,您将可以测试其他项目,例如将表转换为MyISAM,以查看问题是否是全局问题,还是仅针对InnoDB。

或者,我注意到“ estimated_date”的索引是:

使用BTREE 键estimated_date_indexestimated_date

注意“使用BTREE”。也许在没有使用BTREE的情况下尝试一下,看看您是否仍然看到相同的行为。(或者完全删除索引只是为了测试..这将有助于缩小问题的范围)。

希望这可以帮助。


1

尝试查询

select * from s_p where estimated_date is null and estimated_date is not null limit 5;

我认为您不明白问题是什么。

2
上面的查询将显示行为不当的行,您可以从中找到解决方案。

1
如果该查询返回任何行,我将非常担心您的数据的完整性。
Naltharial

@Naltharial这不是我的数据,上面的问题给出了奇怪的输出。

mysql> select * from s_p,其中estimate_date为null,而estimate_date不为null 5;空集(0.00秒)
user1213259'2

1

我在表格布局中看到一些有趣的东西,喊着“我不想数数”。我要说的只是预感。

您之前运行过此查询

select distinct date(estimated_date) from s_p;

以COUNT / GROUP BY身份运行

select count(1) rowcount,date(estimated_date) from s_p group by date(estimated_date);

您将获得所需的确定数量。

但是,为什么正确计算NULL和NOT NULL的计数?同样,这只是有根据的猜测。

您已将该列estimated_date编入索引。这是我希望您尝试的方法:

SHOW INDEX FROM s_p;
SHOW INDEX FROM s_p;
SHOW INDEX FROM s_p;
SHOW INDEX FROM s_p;

那不是错字。我希望您运行SHOW INDEX FROM s_p;四(4)次。看一下Cardinality专栏。由于s_pInnoDB中的表,我希望Cardinality列每次都不同。为什么?

InnoDB通过通过BTREE页面条目进行计数来估算基数值(不需要插入)。检查系统变量innodb_stats_on_metadata。应该启用它。如果已启用它,请将其禁用,然后重新运行原始查询以查看是否有所改善。只能作为最后的手段!

因此,代替这些查询:

select count(*) from s_p where estimated_date is null;
select count(*) from s_p where estimated_date is not null;

尝试

select count(estimated_date) from s_p;

这应该为您提供估计值非空的行数。

您可能想使用ISNULL函数尝试使用这种蛮力查询的另一种方法:

select count(*) rowcount,isnull(estimated_date) IsItNull
from s_p group by isnull(estimated_date);

希望这些建议对您有所帮助!


-4

这是预期的。对于可为空的列,其值为0 == NULL =“”,依此类推。因此,第一项检查实际上返回未设置日期或其日期类似于“ 0 / NULL”的行


2
0永远都不等于NULL。除非使用Oracle,否则空字符串('')都不相同NULL
ypercubeᵀᴹ
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.