MySQL ES NULO / NO ES NULO ¿Mal comportamiento?

18

Por favor, mire esta tabla:

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

Ahora eche un vistazo a estas consultas:

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

Los recuentos anteriores no coinciden. Mientras que según mi entendimiento:

Contar con IS NULLy Contar IS NOT NULLdebe ser igual a contar cuando se consulta sin la cláusula where.

¿Alguna idea de lo que está pasando aquí?

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

Actualización el 17 de febrero de 2012

Desde entonces, descubrí que muchas personas preguntan sobre el tipo de valores que tiene estimada_fecha actualmente. Aquí está la respuesta:

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)

Como puede ver arriba, Estimated_date tiene valores NULL o de fecha y hora válidos. No hay ceros ni cadenas vacías "".

¿Puede suceder esto (problema original) si el índice en Estimated_date tiene algún problema?

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

Actualización el 18 de febrero de 2012

Aquí está la salida de la tabla show create:

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

Nuevamente, solo puedo sospechar el índice en la fecha estimada aquí.

Además, la versión del servidor mysql es 5.5.12.

usuario1213259
fuente
3
A menos que la tabla se alimente con nuevas filas entre y durante la ejecución de las 3 consultas, ¡esto no puede suceder!
ypercubeᵀᴹ
66
¿Estás seguro de que estás haciendo un select count(*)y no select count(estimated_date)? Estos dos devolverán resultados diferentes ya que los NULL se ignoran si eso es lo único que está contando.
66
No estoy seguro de si lo siguiente funcionará en MySQL, pero ¿puede intentar ejecutarlo ?: lo 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_pque debería obtener todos los recuentos de una vez.
Damien_The_Unbeliever
1
¿Son estas las consultas exactas que está ejecutando?
gbn
44
Además, si este es MyISAM, ¿puedes ejecutarlo CHECK TABLE? Teniendo en cuenta el recuento de filas completas enormemente más grande, supongo que se DELETEvolvió loco en alguna parte.
Naltharial

Respuestas:

6

¿Tienes algunas fechas cero? 0000-00-00 00:00:00MySQL considera que los valores de fecha y hora satisfacen is nully 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)

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

Esto se clasifica como "no es un error". Sugieren una solución alternativa: use el modo estricto, que convertirá la advertencia de inserción en un error.

Habiendo dicho todo eso, esto solo no puede explicar la variación salvaje en los resultados que está obteniendo (la suma de los conteos is nully is not nulldebe exceder el conteo sin restricciones) ...

araqnid
fuente
El error aparece cuando DATEo DATETIMEse define como NOT NULL. En la pregunta aquí, la columna se define como anulable. Sin embargo, este error es otra razón para ejecutar MySQL solo en modo estricto.
ypercubeᵀᴹ
He actualizado la publicación original para mostrar los valores actuales en la columna estimada_fecha. No tiene 0000-00-00 o cadenas vacías "".
user1213259
1
@yper o una razón para elegir un DBMS diferente ...
ErikE
1
@ErikE: Eso, a veces, no es una elección. Y siempre encontrará razones para elegir un DBMS anotehr, con el que esté trabajando.
ypercubeᵀᴹ
Para su información, ToadSQL muestra 0000-00-00 00:00:00 como {null}, lo que enturbia aún más las aguas. Qué pesadilla. FTR no tenemos un índice en nuestra columna de problemas. Esto está en 5.6.15-log.
Sming
3

@ypercube:

Recientemente me preguntaron si pensaba que el error de regresión "SELECT COUNT (DISTINCT) bloquea InnoDB cuando WHERE operand in Primary Key or Unique Index" podría estar en la raíz de esto.

Aquí está mi respuesta (originalmente aquí):

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

No creo que este sea el mismo error. Este error es más acerca de la caída, y requiere un RECUENTO SELECCIONADO (DISTINCT) específicamente, más el operando WHERE está en la clave primaria o índice único.

Su error / problema no tiene DISTINCT, no se bloquea y el índice en la columna de fecha y hora no es una clave principal ni única. Sin embargo, es un poco extraño, así que hice un poco de búsqueda y me encontré con este error, que parece más probable que esté involucrado / relacionado:

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

En realidad, se designa como "no es un error", pero muestra / describe cómo puede encontrarse con un comportamiento extraño cuando tiene fechas / horas con '0000-00-00' y usa IS NULL y IS NOT NULL.

Me pregunto si tiene alguna de estas filas '0000-00-00' que podrían estar afectando los recuentos.

Tenga en cuenta que el desarrollador que comenta en el informe de errores también menciona esta página:

Si no es así, sin duda recomendaría actualizar y probar esto en la última versión 5.5, que es 5.5.21 (a partir del 22/02/2012), ya que han pasado 9 meses (y 9 versiones) desde 5.5.12 fue lanzado.

Tenga en cuenta que debería poder volcar la tabla (y los datos) e importarla a otra instancia de prueba, solo para probarla. De esa forma, no afecta a una máquina de producción y puede configurar una instancia de prueba en minutos.

Entonces, si eso no marca la diferencia, estaría en condiciones de probar algunos otros elementos, como quizás convertir la tabla a MyISAM para ver si el problema es global o específico para InnoDB.

O noté que el índice en 'Estimated_date' era:

CLAVE estimated_date_index( estimated_date) USANDO BTREE

Tenga en cuenta el "USO DE BTREE". Tal vez intente sin USAR BTREE y vea si todavía ve el mismo comportamiento. (O elimine el índice por completo solo para probar ... todo ayudará a reducir el problema).

Espero que esto ayude.

Chris Calender
fuente
1

Prueba la consulta

select * from s_p where estimated_date is null and estimated_date is not null limit 5;
Naveen Kumar
fuente
No creo que entiendas cuál es la pregunta.
2
La consulta anterior mostraría las filas de mal comportamiento desde las cuales puede encontrar la solución.
1
Si esa consulta devuelve ninguna fila, estaría seriamente preocupado por la integridad de sus datos.
Naltharial
@Naltharial No son mis datos, la pregunta anterior da una salida extraña.
mysql> select * from s_p, donde estimado_fecha es nula y estimado_fecha no es nulo límite 5; Conjunto vacío (0.00 seg)
usuario1213259
1

Veo algo interesante en el diseño de la mesa que grita "No tengo ganas de contar". Lo que voy a decir es solo una corazonada.

Ejecutó esta consulta antes

select distinct date(estimated_date) from s_p;

Ejecútelo como COUNT / GROUP BY

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

Debes obtener los recuentos definitivos que estabas buscando.

Sin embargo, ¿por qué los recuentos para NULL y NOT NULL se calcularían correctamente? De nuevo, esto es solo una suposición educada.

Tienes la columna estimated_dateindexada. Esto es lo que quiero que pruebes:

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

Eso no es un error tipográfico. Quiero que corras SHOW INDEX FROM s_p;cuatro (4) veces. Mira la Cardinalitycolumna Desde la tabla s_pen InnoDB, espero que la columna de Cardinalidad sea diferente cada vez. ¿Por qué?

InnoDB obtiene el valor de Cardinalidad estimándolo (SIN PUNTO INTENCIONADO) contando a través de las entradas de la página BTREE. Verifique su variable de sistema innodb_stats_on_metadata . Debería estar habilitado. Si ya está habilitado, deshabilítelo y vuelva a ejecutar sus consultas originales para ver si mejora las cosas. ¡HAGA ESTO SOLO COMO ÚLTIMO RECURSO!

Entonces, en lugar de estas consultas:

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

Tratar

select count(estimated_date) from s_p;

Esto debería darle el recuento de filas con una fecha estimada no nula.

Otro enfoque que puede experimentar con esta consulta de fuerza bruta utilizando la función ISNULL :

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

Espero que estas sugerencias ayuden !!!

RolandoMySQLDBA
fuente
-4

Esto se espera. Para una columna que es anulable, 0 == NULL = "" y así sucesivamente. Por lo tanto, la primera comprobación en realidad devuelve filas donde no se estableció ninguna fecha o se percibe de forma análoga a "0 / NULL"


fuente
2
0nunca es igual a NULL. La cadena vacía ( '') tampoco es lo mismo NULL, a menos que esté trabajando con Oracle.
ypercubeᵀᴹ