web-dev-qa-db-fra.com

MySQL IS NULL / IS NOT NULL Un mauvais comportement?

Veuillez regarder ce tableau:

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    |                |
+-------------------------+------------------+------+-----+---------+----------------+

Jetez maintenant un œil à ces requêtes:

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 |
+----------+

Les chiffres ci-dessus ne correspondent pas. Selon ma compréhension:

Compter avec IS NULL et compter avec IS NOT NULL doit être égal à count lorsqu'il est interrogé sans clause where.

Une idée de ce qui se passe ici?

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

Mise à jour du 17 février 2012

Depuis, j'ai constaté que beaucoup de gens se demandent quel genre de valeurs a actuellement la date estimée. Voici la réponse:

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)

Comme vous pouvez le voir ci-dessus, la date_estimée a une valeur NULL ou une valeur datetime valide. Il n'y a pas de zéros ou de chaînes vides "".

Est-ce que cela (problème d'origine) peut se produire si l'index de la date estimée a des problèmes?

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

Mise à jour du 18 février 2012

Voici la sortie show create table:

 | 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 |

Encore une fois, je ne peux que suspecter l'index à la date estimée ici.

De plus, la version du serveur mysql est 5.5.12.

18
user1213259

Avez-vous des dates nulles? Valeurs Datetime de 0000-00-00 00:00:00 sont considérés par MySQL comme satisfaisant simultanément is null et is 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)

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

Ceci est classé comme "pas un bug". Ils suggèrent une solution de contournement: utilisez le mode strict, qui convertira l'avertissement d'insertion en erreur.

Cela dit, cela ne peut à lui seul expliquer la variation sauvage des résultats que vous obtenez (la somme des is null et is not null le nombre doit dépasser le nombre illimité) ...

6
araqnid

@ypercube:

On m'a récemment demandé si je pensais que le bogue de régression "SELECT COUNT (DISTINCT) plante InnoDB lorsque l'opérande est dans la clé primaire ou l'index unique" pourrait être à l'origine de cela.

Voici ma réponse (à l'origine ici):

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

Je ne pense pas que ce soit le même bug. Ce bogue concerne davantage le plantage et nécessite spécifiquement SELECT COUNT (DISTINCT), plus l'opérande WHERE se trouve dans la clé primaire ou l'index Unique.

Votre bogue/problème n'a pas le DISTINCT, il ne plante pas et l'index sur la colonne datetime n'est pas une clé primaire ni unique. Cependant, c'est un peu étrange, donc j'ai fait quelques recherches et j'ai rencontré ce bug, qui semble plus susceptible d'être impliqué/lié:

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

En fait, il est désigné comme "pas un bug", mais il montre/décrit comment vous pouvez rencontrer un comportement étrange lorsque vous avez des dates/heures avec "0000-00-00" et en utilisant IS NULL et IS NOT NULL.

Je me demande si vous avez une de ces lignes "0000-00-00" qui pourrait affecter les comptes?

Notez que le développeur qui commente dans le rapport de bogue mentionne également cette page:

Si ce n'est pas le cas, je recommanderais certainement de mettre à niveau et d'essayer cela sur la dernière version 5.5, qui est 5.5.21 (au 22/02/2012), car cela fait 9 mois (et 9 versions) depuis 5.5.12 a été libéré.

Notez que vous devriez pouvoir vider la table (et les données) et l'importer dans une autre instance de test, juste pour la tester. De cette façon, vous n'affectez pas une machine de production et vous pouvez configurer une instance de test en quelques minutes.

Ensuite, si cela ne fait toujours pas de différence, vous seriez en mesure de tester d'autres éléments, tels que peut-être convertir la table en MyISAM pour voir si le problème est mondial ou spécifique à InnoDB.

Ou, j'ai remarqué que l'indice de la "date_estimée" était:

CLÉ estimated_date_index (estimated_date) UTILISATION DE BTREE

Notez la "UTILISATION DE BTREE". Peut-être essayez-le sans UTILISER BTREE et voyez si vous voyez toujours le même comportement. (Ou supprimez complètement l'index juste pour tester .. tout cela aidera à réduire le problème).

J'espère que cela t'aides.

3
Chris Calender

Je vois quelque chose d'intéressant dans la disposition du tableau qui crie "Je n'ai pas envie de compter". Ce que je vais dire n'est qu'un pressentiment.

Vous avez exécuté cette requête avant

select distinct date(estimated_date) from s_p;

Exécutez-le en tant que COUNT/GROUP BY

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

Vous devez obtenir le nombre définitif que vous recherchiez.

Pourtant, pourquoi les nombres pour NULL et NOT NULL seraient-ils calculés correctement? Encore une fois, ce n'est qu'une supposition éclairée.

Vous avez la colonne estimated_date indexé. Voici ce que je veux que vous essayiez:

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

Ce n'est pas une faute de frappe. Je veux que tu cours SHOW INDEX FROM s_p; quatre (4) fois. Regardez la colonne Cardinality. Depuis le tableau s_p dans InnoDB, je m'attends à ce que la colonne Cardinalité soit différente à chaque fois. Pourquoi?

InnoDB obtient la valeur de cardinalité en l'estimant (NO PUN INTENDED) en comptant via les entrées de la page BTREE. Vérifiez votre variable système innodb_stats_on_metadata . Il doit être activé. S'il est déjà activé, désactivez-le et réexécutez vos requêtes d'origine pour voir si cela améliore les choses. FAITES CECI UNIQUEMENT COMME DERNIER RESORT !!!

Donc, au lieu de ces requêtes:

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

Essayer

select count(estimated_date) from s_p;

Cela devrait vous donner le nombre de lignes avec une date estimée non nulle.

Une autre approche que vous voudrez peut-être expérimenter avec cette requête de force brute en utilisant la fonction ISNULL :

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

J'espère que ces suggestions aident !!!

1
RolandoMySQLDBA

Essayez la requête

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