MySQL NULL / NOT NULL Неправильно себя ведет?


18

Пожалуйста, посмотрите на эту таблицу:

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

Теперь взгляните на эти запросы:

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

Количество выше не совпадают. Хотя по моему пониманию

Count с IS NULLи Count с IS NOT NULLдолжен быть равным count при запросе без предложения where.

Есть идеи о том, что здесь происходит?

================================================== знак равно

Обновление от 17 февраля 2012

С тех пор я обнаружил, что многие люди спрашивают о том, какие значения имеют оценочные значения в настоящее время. Вот ответ:

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)

Как вы можете видеть выше, сметное значение date_date либо NULL, либо допустимые значения даты и времени. Нет нулей или пустых строк "".

Может ли это (исходная проблема) произойти, если у индекса на оценочной дате есть какие-то проблемы?

================================================== знак равно

Обновление от 18 февраля 2012

Вот выходные данные 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 |

Опять же, я могу только подозревать индекс по оценочной дате здесь.

Также версия сервера mysql 5.5.12.


3
Если таблица не заполняется новыми строками между и во время выполнения трех запросов, это не может произойти!
ypercubeᵀᴹ

6
Вы уверены, что делаете, select count(*)а нет select count(estimated_date)? Эти два будут возвращать разные результаты, так как NULL игнорируются, если это единственное, что вы рассчитываете.

6
Я не уверен, что следующее будет работать в MySQL, но вы можете попробовать запустить: 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- который должен получить все подсчеты за один раз.
Damien_The_Unbeliever

1
Являются ли эти точные запросы вы бегаете?
ГБН

4
Кроме того, если это MyISAM, вы можете запустить CHECK TABLEна нем? Учитывая дико большее количество полной строки, я предполагаю, что DELETEсошел с ума , где - то.
Naltharial

Ответы:


6

У вас есть нулевые даты? Значения даты 0000-00-00 00:00:00и времени рассматриваются MySQL для одновременного удовлетворения is nullи 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)

Смотрите: http://bugs.mysql.com/bug.php?id=940

Это классифицируется как "не ошибка". Они предлагают обходной путь: используйте строгий режим, который преобразует предупреждение о вставке в ошибку.

Сказав все это, одно это не может объяснить дикие различия в результатах, которые вы получаете (сумма значений is nullи is not nullдолжна превышать неограниченное количество) ...


Ошибка появляется, когда DATEили DATETIMEопределяется как NOT NULL. В этом вопросе столбец определяется как обнуляемый. Эта ошибка, однако, является еще одной причиной для запуска MySQL только в строгом режиме.
ypercubeᵀᴹ

Я обновил исходный пост, чтобы показать текущие значения в столбце оценочная_дата. У него нет 0000-00-00 или пустых строк "".
user1213259

1
@yper или причина выбрать другую СУБД ...
ErikE

1
@ErikE: Иногда это не выбор. И вы всегда найдете причины выбрать другую СУБД, с какой бы вы ни работали.
ypercubeᵀᴹ

FYI ToadSQL показывает 0000-00-00 00:00:00 как {ноль}, еще больше мутит воду! Какой кошмар. FTR у нас нет индекса в нашей проблемной колонке. Это на 5.6.15-лог.
sming

3

@ypercube:

Меня недавно спросили, не думал ли я, что в основе этого лежит ошибка регрессии «SELECT COUNT (DISTINCT), вызывающая сбой InnoDB, когда операнд WHERE находится в первичном ключе или уникальном индексе».

Вот мой ответ (изначально здесь):

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

Я не думаю, что это та же ошибка. Эта ошибка больше касается сбоя и требует, в частности, SELECT COUNT (DISTINCT), плюс операнд WHERE находится в индексе первичного ключа или уникального индекса.

У вашей ошибки / проблемы нет DISTINCT, она не падает, и индекс в столбце datetime не является ни первичным ключом, ни уникальным. Тем не менее, это немного странно, поэтому я немного поискал и наткнулся на эту ошибку, которая, скорее всего, связана с этим:

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

На самом деле, он обозначен как «не ошибка», но показывает / описывает, как вы можете столкнуться со странным поведением, когда у вас есть даты / даты и время с «0000-00-00» и используются IS NULL и IS NOT NULL.

Интересно, есть ли у вас какие-либо из этих строк «0000-00-00», которые могут повлиять на счет?

Обратите внимание, что разработчик, который комментирует в отчете об ошибке, упоминает и эту страницу:

Если это не так, я бы определенно рекомендовал обновить и попробовать это на последней версии 5.5, то есть 5.5.21 (по состоянию на 22.02.2012), поскольку прошло 9 месяцев (и 9 выпусков) с 5.5.12. был выпущен.

Обратите внимание, что вы должны иметь возможность выгрузить таблицу (и данные) и импортировать ее в другой тестовый экземпляр, просто чтобы проверить его. Таким образом, вы не влияете на рабочую машину, и вы можете настроить тестовый экземпляр за считанные минуты.

Затем, если это все равно не поможет, вы сможете протестировать некоторые другие элементы, например, возможно, преобразовать таблицу в MyISAM, чтобы увидеть, является ли проблема глобальной или специфичной для InnoDB.

Или я заметил, что индекс для «оценочной даты» был:

КЛЮЧ estimated_date_index( estimated_date) ИСПОЛЬЗУЯ BTREE

Обратите внимание на «ИСПОЛЬЗОВАНИЕ BTREE». Возможно, попробуйте это без ИСПОЛЬЗОВАНИЯ BTREE и посмотрите, видите ли вы все то же поведение. (Или вообще уберите индекс, чтобы проверить ... это поможет сузить проблему).

Надеюсь это поможет.


1

Попробуйте запрос

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

Я не думаю, что вы понимаете, в чем вопрос.

2
Приведенный выше запрос покажет строки с неправильным поведением, по которым можно найти решение.

1
Если этот запрос вернет какие-либо строки, я буду серьезно обеспокоен целостностью ваших данных.
Naltharial

@Naltharial Это не мои данные, вопрос выше дает странный вывод.

mysql> select * from s_p, где оценочная_дата равна нулю, а оценочная_дата не равна нулю предел 5; Пустой набор (0,00 сек)
user1213259

1

Я вижу что-то интересное в макете стола, которое выкрикивает: «Мне не хочется считать». То, что я собираюсь сказать, это только догадка.

Вы выполняли этот запрос раньше

select distinct date(estimated_date) from s_p;

Запустите его как COUNT / GROUP BY

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

Вы должны получить точные счета, которые вы искали.

Тем не менее, почему значения для NULL и NOT NULL вычисляются правильно? Опять же, это просто обоснованное предположение.

У вас есть колонка estimated_dateпроиндексирована. Вот что я хочу, чтобы вы попробовали:

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

Это не опечатка. Я хочу, чтобы ты бежал SHOW INDEX FROM s_p;четыре (4) раза. Посмотри на Cardinalityколонку. Поскольку таблица s_pв InnoDB, я ожидаю, что столбец кардинальности будет отличаться каждый раз. Почему?

InnoDB получает значение Cardinality, оценивая его (NO PUN INTENDED) путем подсчета записей на странице BTREE. Проверьте вашу системную переменную innodb_stats_on_metadata . Это должно быть включено. Если он уже включен, отключите его и повторно запустите исходные запросы, чтобы посмотреть, улучшится ли ситуация. ДЕЛАЙТЕ ЭТО ТОЛЬКО КАК ПОСЛЕДНИЙ КУРОРТ !!!

Итак, вместо этих запросов:

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

Пытаться

select count(estimated_date) from s_p;

Это должно дать вам количество строк с ненулевым оценочным значением.

Другой подход, который вы можете поэкспериментировать с этим грубым запросом, используя функцию ISNULL :

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

Я надеюсь, что эти предложения помогут !!!


-4

Это ожидается. Для столбца, который обнуляется, 0 == NULL = "" и так далее. Таким образом, первая проверка фактически возвращает строки, в которых дата не была установлена ​​или воспринимается аналогично «0 / NULL»


2
0никогда не равен NULL. Пустая строка ( '') тоже не то же самое NULL, если вы не работаете с Oracle.
ypercubeᵀᴹ
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.