MySQL IS NULL / IS NOT NULL Niewłaściwe zachowanie?

18

Proszę spojrzeć na tę tabelę:

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

Teraz spójrz na te zapytania:

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

Powyższe liczby nie są zgodne. Chociaż według mojego zrozumienia:

Liczyć za pomocą IS NULL i Liczby z IS NOT NULLpowinny być równe, aby liczyć, gdy są pytane bez klauzuli where.

Masz pomysł na to, co się tutaj dzieje?

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

Aktualizacja 17 lutego 2012 r

Odkąd odkryłem, że wiele osób pyta o wartości, jakie ma obecnie szacowana data. Oto odpowiedź:

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)

Jak widać powyżej, szacunkowa data ma NULL lub prawidłowe wartości daty i godziny. Nie ma zer ani pustych ciągów „”.

Czy ten problem (oryginalny problem) może się zdarzyć, jeśli indeks w szacowanej dacie ma jakieś problemy?

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

Aktualizacja 18 lutego 2012 r

Oto dane wyjściowe tabeli tworzenia tabeli:

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

Znów mogę podejrzewać indeks tutaj tylko w przybliżoną datę.

Ponadto wersja serwera mysql to 5.5.12.

użytkownik1213259
źródło
3
O ile tabela nie będzie zasilana nowymi wierszami między i podczas wykonywania 3 zapytań, to nie może się zdarzyć!
ypercubeᵀᴹ
6
Czy jesteś pewien, że robisz select count(*)a nie select count(estimated_date)? Te dwa zwracają różne wyniki, ponieważ wartości NULL są ignorowane, jeśli to jedyna rzecz, którą się liczy.
6
Nie jestem pewien, czy poniższe działania będą działać w MySQL, ale czy możesz spróbować uruchomić: 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- który powinien uzyskać wszystkie liczby za jednym razem.
Damien_The_Unbeliever
1
Czy to są dokładnie te zapytania, które uruchamiasz?
gbn
4
Ponadto, jeśli jest to MyISAM, możesz CHECK TABLEna nim uruchomić ? Biorąc pod uwagę dziko większą liczbę pełny wiersz, bym odgadnąć DELETEoszalał gdzieś.
Naltharial

Odpowiedzi:

6

Czy masz jakieś zero dat? Wartości Datetime 0000-00-00 00:00:00są uważane przez MySQL za jednocześnie spełniające is nulli 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)

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

Jest to klasyfikowane jako „nie błąd”. Sugerują obejście: użyj trybu ścisłego, który przekształci ostrzeżenie wstawiania na błąd.

Powiedziawszy to wszystko, samo to nie może wyjaśnić dzikiej zmienności wyników, które otrzymujesz (suma is nulli is not nullliczby powinny przekraczać nieograniczoną liczbę) ...

araqnid
źródło
Błąd pojawia się, gdy DATElub DATETIMEjest zdefiniowane jako NOT NULL. W pytaniu tutaj kolumna jest zdefiniowana jako nullable. Ten błąd jest jednak kolejnym powodem, aby uruchomić MySQL tylko w trybie ścisłym.
ypercubeᵀᴹ
Zaktualizowałem oryginalny post, aby wyświetlić bieżące wartości w kolumnie szacowana data. Nie ma 0000-00-00 ani pustych ciągów „”.
user1213259
1
@yper lub powód, aby wybrać inny DBMS ...
ErikE
1
@ErikE: Czasami to nie jest wybór. I zawsze znajdziesz powody, aby wybrać inny DBMS, niezależnie od tego, z którym pracujesz.
ypercubeᵀᴹ
FYI ToadSQL pokazuje 0000-00-00 00:00:00 jako {null}, jeszcze bardziej mętniając wody! Co za koszmar. FTR nie mamy indeksu w kolumnie problemów. To jest na 5.6.15-log.
sming
3

@ypercube:

Niedawno zapytano mnie, czy myślę, że błąd regresji „WYBIERZ LICZBĘ (DISTINCT) powoduje awarię InnoDB, gdy operand jest w kluczu podstawowym lub indeksie unikatowym” może być przyczyną tego.

Oto moja odpowiedź (pierwotnie tutaj):

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

Nie sądzę, że to ten sam błąd. Ten błąd dotyczy bardziej awarii i wymaga w szczególności SELECT COUNT (DISTINCT), a także operand WHERE znajduje się w kluczu podstawowym lub indeksie Unique.

Twój błąd / problem nie ma DISTINCT, nie ulega awarii, a indeks w kolumnie daty i godziny nie jest kluczem podstawowym ani unikalnym. Jednak jest to trochę dziwne poza mankietem, więc trochę przeszukałem i natknąłem się na ten błąd, który wydaje się być bardziej zaangażowany / związany:

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

W rzeczywistości jest oznaczony jako „nie jest to błąd”, ale pokazuje / opisuje, w jaki sposób możesz popaść w dziwne zachowanie, kiedy masz daty / godziny danych z „0000-00-00” i używając IS NULL i IS NOT NULL.

Zastanawiam się, czy masz któryś z tych wierszy „0000-00-00”, który może mieć wpływ na liczbę?

Zwróć uwagę na autora, który komentuje w raporcie o błędzie, również wspomina o tej stronie:

Jeśli tak nie jest, zdecydowanie polecam aktualizację i wypróbowanie tego w najnowszej wersji 5.5, czyli w wersji 5.5.21 (stan na 22.02.2012), ponieważ minęło 9 miesięcy (i 9 wydań) od 5.5.12 został wydany.

Pamiętaj, że powinieneś być w stanie zrzucić tabelę (i dane) i zaimportować ją do innej instancji testowej, aby ją przetestować. W ten sposób nie wpływasz na maszynę produkcyjną i możesz skonfigurować instancję testową w kilka minut.

Następnie, jeśli to nie robi różnicy, będziesz w stanie przetestować inne elementy, na przykład przekonwertować tabelę na MyISAM, aby sprawdzić, czy problem jest globalny, czy tylko specyficzny dla InnoDB.

Zauważyłem też, że indeks „data_szacowania” to:

KLUCZ estimated_date_index( estimated_date) ZA POMOCĄ BTREE

Zwróć uwagę na „UŻYWANIE BTREE”. Być może spróbuj tego bez USING BTREE i sprawdź, czy nadal widzisz to samo zachowanie. (Lub usuń indeks całkowicie tylko po to, aby przetestować .. to wszystko pomoże zawęzić problem).

Mam nadzieję że to pomoże.

Chris Calender
źródło
1

Wypróbuj zapytanie

select * from s_p where estimated_date is null and estimated_date is not null limit 5;
Naveen Kumar
źródło
Nie sądzę, że rozumiesz, o co chodzi.
2
Powyższe zapytanie pokaże niepoprawne wiersze, z których można znaleźć rozwiązanie.
1
Jeśli to zapytanie zwróci jakiekolwiek wiersze, byłbym poważnie zaniepokojony integralnością danych.
Naltharial
@Naltharial To nie moje dane, powyższe pytanie daje dziwny wynik.
mysql> wybierz * z s_p, gdzie szacowana data jest zerowa, a szacowana data nie ma limitu 5; Pusty zestaw (0,00 s)
1213259
1

W układzie stołu widzę coś interesującego, co woła: „Nie mam ochoty liczyć”. Mam zamiar powiedzieć tylko przeczucie.

Uruchomiłeś już to zapytanie

select distinct date(estimated_date) from s_p;

Uruchom jako COUNT / GROUP BY

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

Otrzymasz ostateczne liczby, których szukałeś.

Dlaczego jednak liczby dla wartości NULL i NOT NULL są obliczane poprawnie? Ponownie jest to tylko wykształcone przypuszczenie.

Masz estimated_dateindeksowaną kolumnę . Oto, co chcę, abyś spróbował:

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

To nie jest literówka. Chcę, żebyś biegł SHOW INDEX FROM s_p;cztery (4) razy. Spójrz na Cardinalitykolumnę. Ponieważ tabela s_pw InnoDB oczekuje, że kolumna Kardynalność będzie za każdym razem inna. Dlaczego?

InnoDB uzyskuje wartość Kardynalności poprzez jej oszacowanie (NIE PRZEZNACZENIE PUNKTU) poprzez zliczanie za pomocą wpisów strony BTREE. Sprawdź swoją zmienną systemową innodb_stats_on_metadata . Powinien być włączony. Jeśli jest już włączony, wyłącz go i ponownie uruchom oryginalne zapytania, aby sprawdzić, czy coś się poprawi. ZRÓB TO TYLKO JAKO OSTATNI OŚRODEK !!!

Zamiast tych zapytań:

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

Próbować

select count(estimated_date) from s_p;

Powinno to dać ci liczbę wierszy z niepustą datą szacunkową.

Inne podejście, które możesz chcieć eksperymentować z tym zapytaniem dotyczącym siły brutalnej przy użyciu funkcji ISNULL :

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

Mam nadzieję, że te sugestie pomogą !!!

RolandoMySQLDBA
źródło
-4

Jest to oczekiwane. W przypadku kolumny, która ma wartość zerową, 0 == NULL = "" i tak dalej. Tak więc pierwsze sprawdzenie faktycznie zwraca wiersze, w których nie ustawiono daty lub jej postrzeganie jest analogiczne do „0 / NULL”


źródło
2
0nigdy nie jest równy NULL. Pusty ciąg ( '') nie jest taki sam jak NULLoba, chyba że pracujesz z Oracle.
ypercubeᵀᴹ