MySQL É NULL / NÃO É NULL Comportamento incorreto?

18

Por favor, veja esta tabela:

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

Agora, dê uma olhada nessas 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 |
+----------+

As contagens acima não são correspondentes. Enquanto de acordo com o meu entendimento:

Contar com IS NULLe Contar com IS NOT NULLdeve ser igual a contar quando consultado sem a cláusula where.

Alguma idéia do que está acontecendo aqui?

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

Atualização em 17 de fevereiro de 2012

Desde então, descobri que muitas pessoas estão perguntando sobre o tipo de valores estimados atualmente na data. Aqui está a resposta:

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 você pode ver acima, o estimado_data possui valores NULL ou válidos para data e hora. Não há zeros ou cadeias vazias "".

Isso (problema original) pode acontecer se o índice na data_ estimada tiver algum problema / s?

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

Atualização em 18 de fevereiro de 2012

Aqui está o show create table output:

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

Mais uma vez, só posso suspeitar de índice na estimativa_data aqui.

Além disso, a versão do servidor mysql é 5.5.12.

user1213259
fonte
3
A menos que a tabela esteja sendo alimentada com novas linhas entre e durante a execução das 3 consultas, isso não pode acontecer!
ypercubeᵀᴹ
6
Você tem certeza de que está fazendo um select count(*)e não select count(estimated_date)? Esses dois retornarão resultados diferentes, pois os NULLs serão ignorados se essa for a única coisa que você está contando.
6
Não tenho certeza se o seguinte funcionará no MySQL, mas você pode tentar executar: 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- que deve obter todas as contagens de uma só vez.
Damien_The_Unbeliever
11
Essas são as consultas exatas que você está executando?
gbn 16/02/12
4
Além disso, se esse é o MyISAM, você pode executá CHECK TABLE-lo? Considerando a contagem de linhas inteiras muito maior, eu acho que a DELETEenlouqueceu em algum lugar.
Naltharial

Respostas:

6

Você tem algumas datas zero? Os valores de data e hora de 0000-00-00 00:00:00são considerados pelo MySQL para satisfazer simultaneamente 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)

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

Isso é classificado como "não é um bug". Eles sugerem uma solução alternativa: use o modo estrito, que converterá o aviso de inserção em erro.

Dito tudo isso, isso por si só não pode explicar a grande variação nos resultados que você está obtendo (a soma das contagens is nulle is not nulldeve exceder a contagem irrestrita) ...

araqnid
fonte
O erro aparece quando o DATEou DATETIMEé definido como NOT NULL. Na pergunta aqui, a coluna é definida como anulável. Este bug, no entanto, é outro motivo para executar o MySQL somente no modo estrito.
ypercubeᵀᴹ
Atualizei a postagem original para mostrar os valores atuais na coluna estimativa_data. Não possui 0000-00-00 ou cadeias vazias "".
user1213259
11
@yper ou uma razão para escolher um diferentes SGBD ...
ErikE
11
@ErikE: Isso, às vezes, não é uma escolha. E você sempre encontrará motivos para escolher outro DBMS, qualquer que seja o seu trabalho.
ypercubeᵀᴹ
FYI ToadSQL mostra 0000-00-00 00:00:00 como {null}, turvando ainda mais as águas! Que pesadelo. FTR, não temos um índice na coluna do problema. Isso está no 5.6.15-log.
sming
3

@ypercube:

Recentemente, fui perguntado se eu achava que o bug de regressão "SELECT COUNT (DISTINCT) trava o InnoDB quando o operando WHERE está na Chave Primária ou no Índice Único" pode estar na raiz disso.

Aqui está a minha resposta (originalmente aqui):

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

Eu não acho que esse seja o mesmo bug. Esse bug é mais sobre a falha e requer uma SELECT COUNT (DISTINCT) especificamente, além do operando WHERE no chave primária ou no índice exclusivo.

Seu bug / problema não possui o DISTINCT, não está travando e o índice na coluna datetime não é uma chave primária nem exclusiva. No entanto, é um pouco estranho, então eu fiz algumas pesquisas e encontrei esse bug, que parece mais provável de estar envolvido / relacionado:

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

Na verdade, ele é designado como "não é um bug", mas mostra / descreve como você pode ter um comportamento estranho quando possui datas / datas com '0000-00-00' e usa IS NULL e IS NOT NULL.

Gostaria de saber se você tem alguma dessas linhas '0000-00-00' que pode estar afetando as contagens?

Observe que o desenvolvedor que comenta no relatório de erros também menciona esta página:

Se não for esse o caso, eu certamente recomendaria atualizar e tentar isso no 5.5 mais recente, que é 5.5.21 (a partir de 22/2/2012), já que faz 9 meses (e 9 lançamentos) desde o 5.5.12 foi liberado.

Observe que você deve despejar a tabela (e os dados) e importá-la para outra instância de teste, apenas para testá-la. Dessa forma, você não afeta uma máquina de produção e pode ter uma instância de teste configurada em minutos.

Então, se isso ainda não fizer diferença, você poderá testar outros itens, como converter a tabela para MyISAM para ver se o problema é global ou apenas específico para o InnoDB.

Ou notei que o índice em 'estimativa_data' era:

KEY estimated_date_index( estimated_date) USANDO O BTREE

Observe o "USANDO O BTREE". Talvez tente sem o USING BTREE e veja se você ainda vê o mesmo comportamento. (Ou remova o índice completamente apenas para testar .. tudo isso ajudará a diminuir o problema).

Espero que isto ajude.

Chris Calender
fonte
1

Experimente a consulta

select * from s_p where estimated_date is null and estimated_date is not null limit 5;
Naveen Kumar
fonte
Eu não acho que você entenda qual é a pergunta.
2
A consulta acima mostraria as linhas com comportamento inadequado a partir das quais você pode encontrar a solução.
11
Se essa consulta retornar alguma linha, eu ficaria seriamente preocupado com a integridade dos seus dados.
Naltharial
@ Naltharial Não são meus dados, a pergunta acima fornece uma saída estranha.
mysql> select * from s_p onde a data_ estimada é nula e a data estimada não é o limite nulo 5; Conjunto vazio (0.00 seg)
user1213259
1

Vejo algo interessante no layout da tabela que grita 'Não sinto vontade de contar'. O que estou prestes a dizer é apenas um palpite.

Você executou esta consulta antes

select distinct date(estimated_date) from s_p;

Execute-o como COUNT / GROUP BY

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

Você deve obter as contagens definitivas que estava procurando.

No entanto, por que as contagens para NULL e NOT NULL são computadas corretamente? Novamente, este é apenas um palpite.

Você tem a coluna estimated_dateindexada. Aqui está o que eu quero que você tente:

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

Isso não é um erro de digitação. Quero que você corra SHOW INDEX FROM s_p;quatro (4) vezes. Olhe para a Cardinalitycoluna. Desde a tabela s_pno InnoDB, espero que a coluna Cardinalidade seja diferente a cada vez. Por quê?

O InnoDB obtém o valor de cardinalidade estimando-o (NO PUN INTENDED) contando através das entradas da página BTREE. Verifique a variável do sistema innodb_stats_on_metadata . Deve estar ativado. Se já estiver ativado, desative-o e execute novamente as consultas originais para ver se melhora as coisas. FAÇA ISSO SOMENTE COMO ÚLTIMO RECURSO !!!

Então, em vez dessas consultas:

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

Experimentar

select count(estimated_date) from s_p;

Isso deve fornecer a contagem de linhas com uma data estimada não nula.

Outra abordagem que você pode querer experimentar com essa consulta de força bruta usando a função ISNULL :

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

Espero que estas sugestões ajudem !!!

RolandoMySQLDBA
fonte
-4

Isso é esperado. Para uma coluna anulável, 0 == NULL = "" e assim por diante. Portanto, a primeira verificação retorna linhas onde nenhuma data foi definida ou sua percepção é análoga a "0 / NULL"


fonte
2
0nunca é igual a NULL. String vazia ( '') também não é a mesma NULL, a menos que você esteja trabalhando com o Oracle.
ypercubeᵀᴹ