it-swarm.it

MySQL IS NULL / IS NOT NULL Si comporta male?

Dai un'occhiata a questa tabella:

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

Ora dai un'occhiata a queste domande:

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

I conteggi sopra non corrispondono. Mentre secondo la mia comprensione:

Conta con IS NULL e conta con IS NOT NULL dovrebbe essere uguale al conteggio quando interrogato senza la clausola where.

Qualche idea su cosa sta succedendo qui?

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

Aggiornamento il 17 febbraio 2012

Da allora, ho scoperto che molte persone si chiedono quale sia il valore di stima_data attualmente ha. Ecco la risposta:

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)

Come puoi vedere sopra stimata_data ha valori NULL o datetime validi. Non ci sono zeri o stringhe vuote "".

Questo (problema originale) può verificarsi se l'indice di stimata_data ha qualche problema?

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

Aggiornamento il 18 febbraio 2012

Ecco l'output della tabella show show:

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

Ancora una volta, posso solo sospettare l'indice su stimata_data qui.

Inoltre, la versione del server mysql è 5.5.12.

18
user1213259

Hai delle date zero? Valori datetime di 0000-00-00 00:00:00 sono considerati da MySQL per soddisfare simultaneamente is null e is not null:

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

[email protected]@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
[email protected]@localhost > select a from _tmp where a is null;
+---------------------+
| a                   |
+---------------------+
| 0000-00-00 00:00:00 |
+---------------------+
1 row in set (0.00 sec)

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

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

Questo è classificato come "non un bug". Suggeriscono una soluzione alternativa: utilizzare la modalità rigorosa, che convertirà l'avviso di inserimento in un errore.

Detto questo, questo da solo non può spiegare la variazione selvaggia dei risultati che stai ottenendo (la somma del is null e is not null i conteggi devono superare il conteggio illimitato) ...

6
araqnid

@ypercube:

Di recente mi è stato chiesto se il bug di regressione "SELECT COUNT (DISTINCT) si arresta in modo anomalo su InnoDB quando l'operando WHERE è in chiave primaria o indice univoco" potrebbe essere alla radice di questo.

Ecco la mia risposta (originariamente qui):

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

Non penso che questo sia lo stesso bug. Questo errore riguarda maggiormente l'arresto anomalo e richiede specificamente un SELECT COUNT (DISTINCT), più l'operando WHERE si trova nella chiave primaria o nell'indice univoco.

Il tuo bug/problema non ha DISTINCT, non si arresta in modo anomalo e l'indice nella colonna datetime non è una chiave primaria né univoca. Tuttavia, è un po 'strano, quindi ho fatto qualche ricerca e mi sono imbattuto in questo bug, che sembra più probabile che sia coinvolto/correlato:

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

In realtà, è designato come "non un bug", ma mostra/descrive come è possibile imbattersi in comportamenti strani quando si hanno date/orari con 'registrazione' e usando IS NULL e IS NOT NULL.

Mi chiedo se hai una di queste righe di "registrazione" che potrebbero influenzare i conteggi?

Nota il Dev che commenta nella segnalazione bug menziona anche questa pagina:

In caso contrario, consiglierei sicuramente di aggiornare e provare questo sull'ultima versione 5.5, che è la 5.5.21 (al 22/02/2012), dato che sono passati 9 mesi (e 9 versioni) dal 5.5.12 è stato rilasciato.

Nota che dovresti essere in grado di scaricare la tabella (e i dati) e importarlo in un'altra istanza di test, solo per testarlo. In questo modo non si influisce su una macchina di produzione e si può impostare un'istanza di prova in pochi minuti.

Quindi, se ciò non dovesse fare ancora la differenza, saresti in grado di testare alcuni altri elementi, come ad esempio convertire la tabella in MyISAM per vedere se il problema è globale o semplicemente specifico di InnoDB.

Oppure, ho notato che l'indice su "stimata_data" era:

KEY estimated_date_index (estimated_date) USANDO BTREE

Nota "USO DI BTREE". Forse provalo senza USARE BTREE e vedere se vedi ancora lo stesso comportamento. (O rimuovi del tutto l'indice solo per testarlo ... tutto ciò contribuirà a restringere il problema).

Spero che sia di aiuto.

3
Chris Calender

Vedo qualcosa di interessante nel layout della tabella che grida "Non ho voglia di contare". Quello che sto per dire è solo un sospetto.

Hai già eseguito questa query

select distinct date(estimated_date) from s_p;

Eseguilo come COUNT/GROUP BY

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

Ottieni i conteggi definitivi che stavi cercando.

Tuttavia, perché i conteggi per NULL e NOT NULL vengono calcolati correttamente? Ancora una volta, questa è solo un'ipotesi colta.

Hai la colonna estimated_date indicizzato. Ecco cosa voglio che provi:

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

Questo non è un errore di battitura. Voglio che corri SHOW INDEX FROM s_p; quattro (4) volte. Guarda la colonna Cardinality. Dal momento che la tabella s_p in InnoDB, mi aspetto che la colonna Cardinalità sia diversa ogni volta. Perché?

InnoDB ottiene il valore di cardinalità stimandolo (NO PUN INTENDED) contando tramite le voci della pagina BTREE. Controlla la variabile di sistema innodb_stats_on_metadata . Dovrebbe essere abilitato. Se è già abilitato, disabilitalo ed riesegui le tue query originali per vedere se le cose migliorano. FARE QUESTO SOLO COME ULTIMO RESORT !!!

Quindi, invece di queste query:

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

Provare

select count(estimated_date) from s_p;

Questo dovrebbe darti il ​​conteggio delle righe con una stima_data non nulla.

Un altro approccio che potresti voler sperimentare con questa query sulla forza bruta usando la funzione ISNULL :

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

Spero che questi suggerimenti siano d'aiuto !!!

1
RolandoMySQLDBA

Prova la query

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