MySQL НУЛЬНИЙ / НЕ НУЛЬНИЙ Недоброзичливник?


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 with IS NULLі Count with IS NOT NULLмає бути рівним підрахунку при запиті без пункту де.

Будь-яка ідея про те, що відбувається тут?

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

Оновлення 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 має або NULL, або допустимі значення дати. Немає нулів чи порожніх рядків "".

Чи може це статися (оригінальна проблема), якщо індекс у оціночному_данні має певні проблеми?

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

Оновлення 18 лютого 2012 року

Ось показ таблиці створення таблиці:

 | 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
Якщо таблиця не подається новими рядками між і під час виконання 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
Це точні запити, які ви виконуєте?
gbn

4
Крім того, якщо це MyISAM, чи можете ви CHECK TABLEна ньому побігати ? З огляду на диво більший підрахунок повного ряду, я б припустив, що DELETEкудись з’їхав божевільний.
Налтарій

Відповіді:


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 або порожніх рядків "".
користувач1213259

1
@yper або причина обрати іншу СУБД ...
ErikE

1
@ErikE: Іноді це не вибір. І ви завжди знайдете причини вибирати anotehr СУБД, з якою б не працювали.
ypercubeᵀᴹ

FYI ToadSQL показує 0000-00-00 00:00:00 як {null}, ще більше забруднюючи води! Який кошмар. FTR, у нашому стовпчику проблем немає індексу. Це на 5.6.15-лог.
посміхається

3

@ypercube:

Нещодавно мене запитали, чи я вважаю, що помилка регресії "SELECT COUNT (DISTINCT) виходить з ладу InnoDB, коли WHDE операнд знаходиться в первинному ключі або унікальному індексі" може бути в корені цього.

Ось моя відповідь (спочатку тут):

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 і НЕ NULL.

Цікаво, чи є у вас будь-які з цих рядків '0000-00-00', які могли б вплинути на підрахунок?

Зауважте, що розробник, який коментує у звіті про помилку, згадує і цю сторінку:

Якщо це не так, я, безумовно, рекомендую оновити та спробувати це на останньому 5.5, що є 5.5.21 (станом на 22.02.2012), оскільки минуло 9 місяців (і 9 версій) з 5.5.12 був звільнений.

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

Тоді, якщо це все-таки не змінить значення, ви зможете перевірити деякі інші елементи, наприклад, можливо, перетворити таблицю в MyISAM, щоб переконатися, що проблема є глобальною, або просто специфічною для InnoDB.

Або я помітив, що індекс на "оцінюваний_даний" був:

KEY 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 Це не мої дані. Питання вище дає дивні результати.

mysql> select * from s_p, де оцінюваний_date є нульовим, а оціночний_date не є нульовим обмеженням 5; Порожній набір (0,00 сек)
користувач1213259

1

Я бачу щось цікаве в макеті таблиці, яке кричить "я не відчуваю, як рахувати". Що я збираюся сказати, це лише придумка.

Ви вже виконували цей запит

select distinct date(estimated_date) from s_p;

Виконайте це як КОЛІЧКА / ГРУПА ПО

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

Ви повинні отримати остаточні підрахунки, які шукали.

Тим не менш, чому б рахунки для NULL, а НЕ 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, я очікую, що стовпець Cardinality буде кожен раз відрізнятися. Чому?

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.