MySQL È NULL / NON È NULL Si comporta male?


18

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:

Count with IS NULLe Count with IS NOT NULLdovrebbero essere uguali a count 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.


3
A meno che la tabella non venga alimentata con nuove righe tra e durante l'esecuzione delle 3 query, ciò non può accadere!
ypercubeᵀᴹ

6
Sei sicuro che stai facendo un select count(*)e no select count(estimated_date)? Questi due restituiranno risultati diversi poiché i NULL vengono ignorati se questa è l'unica cosa che stai contando.

6
Non sono sicuro che in MySQL funzionerà quanto segue, ma puoi provare a eseguire: 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- che dovrebbe ottenere tutti i conteggi in una volta sola.
Damien_The_Unbeliever,

1
Sono queste le domande esatte che stai eseguendo?
gbn,

4
Inoltre, se questo è MyISAM, puoi eseguirlo CHECK TABLE? Considerando il numero di righe complete selvaggiamente più grande, immagino che da DELETEqualche parte sia diventato pazzo.
Naltharial,

Risposte:


6

Hai delle date zero? I valori datetime di 0000-00-00 00:00:00sono considerati da MySQL per soddisfare is nulle 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)

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

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 dei conteggi is nulle is not nulldovrebbe superare il conteggio senza restrizioni) ...


Il bug appare quando DATEo DATETIMEè definito come NOT NULL. Nella domanda qui, la colonna è definita come nullable. Questo bug, tuttavia, è un altro motivo per eseguire MySQL solo in modalità rigorosa.
ypercubeᵀᴹ

Ho aggiornato il post originale per mostrare i valori correnti nella colonna stimata_data. Non ha registrazione o stringhe vuote "".
user1213259,

1
@per o un motivo per scegliere un DBMS diverso ...
ErikE

1
@ErikE: A volte questa non è una scelta. E troverai sempre motivi per scegliere un altro DBMS, qualunque sia quello con cui stai lavorando.
ypercubeᵀᴹ

FYI ToadSQL mostra la registrazione 00:00:00 come {null}, confondendo ulteriormente le acque! Che incubo. FTR non abbiamo un indice nella colonna del problema. Questo è su 5.6.15-log.
sming

3

@ypercube:

Di recente mi è stato chiesto se il bug di regressione "SELECT COUNT (DISTINCT) si blocca in 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-1460

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 si utilizza 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:

TASTO estimated_date_index( estimated_date) UTILIZZANDO 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.


1

Prova la query

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

Non credo che tu capisca qual è la domanda.

2
La query sopra mostrerebbe le righe che si comportano male da cui è possibile trovare la soluzione.

1
Se la query restituisce righe, sarei seriamente preoccupato per l'integrità dei tuoi dati.
Naltharial,

@Naltharial Non sono i miei dati, la domanda sopra fornisce un output strano.

mysql> seleziona * da s_p dove estim_data è null e stim_data non è limite nullo 5; Set vuoto (0,00 sec)
user1213259

1

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_dateindicizzata. 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;

Non è un refuso. Voglio che corri SHOW INDEX FROM s_p;quattro (4) volte. Guarda la Cardinalitycolonna. Dal momento che la tabella s_pin 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 tua 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 di forza bruta utilizzando 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 !!!


-4

Questo è previsto. Per una colonna che è nullable a 0 == NULL = "" e così via. Quindi il primo controllo in realtà restituisce righe in cui non è stata impostata alcuna data o è percepito analogo a "0 / NULL"


2
0non è mai uguale a NULL. La stringa vuota ( '') non è la stessa delle NULLdue, a meno che tu non stia lavorando con Oracle.
ypercubeᵀᴹ
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.