MySQL IS NULL / BUKAN NULL Tingkah Buruk?

18

Silakan lihat tabel ini:

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

Sekarang Lihatlah pertanyaan-pertanyaan ini:

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

Hitungan di atas tidak cocok. Sementara sesuai pemahaman saya:

Hitung dengan IS NULLdan Hitung dengan IS NOT NULLharus sama dengan menghitung saat ditanya tanpa klausa mana.

Ada ide tentang apa yang terjadi di sini?

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

Pembaruan pada 17 Februari 2012

Karena, saya menemukan bahwa banyak orang bertanya tentang jenis nilai yang diestimasi saat ini. Inilah jawabannya:

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)

Seperti yang Anda lihat di atas taksiran tanggal memiliki NULL atau nilai datetime yang valid. Tidak ada nol atau string kosong "".

Bisakah ini (masalah asli) terjadi jika indeks pada estim_date memiliki beberapa masalah?

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

Pembaruan pada 18 Februari 2012

Ini adalah show create output 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 |

Sekali lagi, saya hanya dapat menduga indeks pada taksiran tanggal di sini.

Juga, versi server mysql adalah 5.5.12.

pengguna1213259
sumber
3
Kecuali jika tabel diberi baris baru di antara dan selama menjalankan 3 kueri, ini tidak bisa terjadi!
ypercubeᵀᴹ
6
Anda yakin melakukan select count(*)dan tidak select count(estimated_date)? Keduanya akan menghasilkan hasil yang berbeda karena NULL diabaikan jika hanya itu yang Anda hitung.
6
Saya tidak yakin apakah yang berikut ini akan berfungsi di MySQL, tetapi dapatkah Anda mencoba menjalankan: 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- yang seharusnya mendapatkan semua jumlah sekaligus.
Damien_The_Unbeliever
1
Apakah ini pertanyaan yang persis Anda jalankan?
gbn
4
Juga, jika ini adalah MyISAM, dapatkah Anda menjalankannya CHECK TABLE? Mengingat liar jumlah baris penuh yang lebih besar, aku kira DELETEpergi ke suatu tempat gila.
Naltharial

Jawaban:

6

Apakah Anda memiliki beberapa tanggal nol? Nilai datetime 0000-00-00 00:00:00dianggap oleh MySQL untuk secara bersamaan memuaskan is nulldan 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)

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

Ini diklasifikasikan sebagai "bukan bug". Mereka menyarankan solusi: gunakan mode ketat, yang akan mengubah peringatan penyisipan menjadi kesalahan.

Setelah mengatakan semua itu, ini saja tidak dapat menjelaskan variasi liar dalam hasil yang Anda dapatkan (jumlah is nulldan is not nulljumlah harus melebihi jumlah yang tidak dibatasi) ...

araqnid
sumber
Bug muncul ketika DATEatau DATETIMEdidefinisikan sebagai NOT NULL. Dalam pertanyaan di sini, kolom didefinisikan sebagai nullable. Namun bug ini, adalah alasan lain untuk menjalankan MySQL dalam mode ketat saja.
ypercubeᵀᴹ
Saya telah memperbarui pos asli untuk menunjukkan nilai saat ini di kolom taksir date. Tidak memiliki 0000-00-00 atau string kosong "".
user1213259
1
@ lebih baik atau alasan untuk memilih DBMS yang berbeda ...
ErikE
1
@ErikE: Itu, terkadang, bukan pilihan. Dan Anda akan selalu menemukan alasan untuk memilih DBMS anotehr, apa pun yang sedang Anda kerjakan.
ypercubeᵀᴹ
FYI ToadSQL menunjukkan 0000-00-00 00:00:00 sebagai {null}, semakin memperkeruh perairan! Sungguh mimpi buruk. FTR kami tidak memiliki indeks pada kolom masalah kami. Ini ada di 5.6.15-log.
sming
3

@ypercube:

Saya baru-baru ini ditanya apakah saya pikir bug regresi "SELECT COUNT (DISTINCT) crash InnoDB ketika operan WHERE berada di Primary Key atau Unique Index" bisa menjadi akar dari ini.

Inilah jawaban saya (asal di sini):

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

Saya tidak berpikir ini adalah bug yang sama. Bug ini lebih mengarah pada crash, dan membutuhkan SELECT COUNT (DISTINCT) khusus, ditambah operan WHERE ada di Primary Key atau indeks Unik.

Bug / masalah Anda tidak memiliki DISTINCT, itu tidak menabrak, dan indeks pada kolom datetime bukan kunci utama atau unik. Namun, ini agak aneh, jadi saya melakukan pencarian, dan menemukan bug ini, yang sepertinya lebih terlibat / terkait:

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

Sebenarnya, ini ditetapkan sebagai "bukan bug", tetapi ini menunjukkan / menggambarkan bagaimana Anda dapat mengalami perilaku aneh ketika Anda memiliki tanggal / waktu dengan '0000-00-00 ′ dan menggunakan IS NULL dan IS NOT NULL.

Saya ingin tahu apakah Anda memiliki baris '0000-00-00 ′ yang dapat memengaruhi penghitungan?

Perhatikan Dev yang berkomentar dalam laporan bug menyebutkan halaman ini juga:

Jika bukan itu, saya pasti akan merekomendasikan untuk meningkatkan dan mencoba ini pada 5.5 terbaru, yaitu 5.5.21 (per 2/22/2012), karena sudah 9 bulan (dan 9 rilis) sejak 5.5.12 sudah diterbitkan.

Catatan Anda harus dapat membuang tabel (dan data) dan mengimpornya ke contoh pengujian lain, hanya untuk mengujinya. Dengan begitu Anda tidak memengaruhi mesin produksi, dan Anda dapat mengatur instans pengujian dalam hitungan menit.

Kemudian, jika itu tidak membuat perbedaan apa pun, Anda akan dapat menguji beberapa item lain, seperti mungkin mengonversi tabel ke MyISAM untuk melihat apakah masalahnya adalah masalah global, atau hanya khusus untuk InnoDB.

Atau, saya perhatikan indeks pada 'taksiran tanggal' adalah:

KEY estimated_date_index( estimated_date) MENGGUNAKAN BTREE

Perhatikan "MENGGUNAKAN BTREE". Mungkin cobalah tanpa MENGGUNAKAN BTREE dan lihat apakah Anda masih melihat perilaku yang sama. (Atau hapus indeks sama sekali hanya untuk menguji .. semuanya akan membantu mempersempit masalah).

Semoga ini membantu.

Chris Calender
sumber
1

Coba kueri

select * from s_p where estimated_date is null and estimated_date is not null limit 5;
Naveen Kumar
sumber
Saya tidak berpikir Anda mengerti apa pertanyaannya.
2
Kueri di atas akan menunjukkan baris berperilaku salah dari mana Anda dapat menemukan solusinya.
1
Jika kueri itu mengembalikan baris apa pun, saya akan sangat khawatir tentang integritas data Anda.
Naltharial
@Naltharial Ini bukan data saya, Pertanyaan di atas memberikan output yang aneh.
mysql> pilih * dari s_p di mana taksiran tanggal adalah nol dan taksir bukan batas nol 5; Set kosong (0,00 dtk)
user1213259
1

Saya melihat sesuatu yang menarik dalam tata letak tabel yang berteriak 'Saya tidak ingin menghitung'. Apa yang akan saya katakan hanyalah firasat.

Anda menjalankan kueri ini sebelumnya

select distinct date(estimated_date) from s_p;

Jalankan sebagai COUNT / GROUP BY

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

Anda harus mendapatkan jumlah pasti yang Anda cari.

Namun, mengapa penghitungan untuk NULL dan NOT NULL dapat dihitung dengan benar? Sekali lagi, ini hanya tebakan yang terpelajar.

Anda memiliki kolom yang estimated_datediindeks. Inilah yang saya ingin Anda coba:

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

Itu bukan kesalahan ketik. Saya ingin Anda menjalankan SHOW INDEX FROM s_p;empat (4) kali. Lihatlah Cardinalitykolomnya. Karena tabel s_pdi InnoDB, saya berharap kolom Kardinalitas berbeda setiap kali. Mengapa?

InnoDB mendapatkan nilai Kardinalitas dengan memperkirakannya (TIDAK ADA PUN YANG DIMAKSUDKAN) dengan menghitung melalui entri halaman BTREE. Periksa variabel sistem Anda innodb_stats_on_metadata . Itu harus diaktifkan. Jika sudah diaktifkan, nonaktifkan dan jalankan kembali pertanyaan awal Anda untuk melihat apakah ada peningkatan. LAKUKAN HANYA INI SEBAGAI RESOR TERAKHIR !!!

Jadi, bukannya pertanyaan ini:

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

Mencoba

select count(estimated_date) from s_p;

Ini akan memberi Anda hitungan baris dengan taksiran tanggal null.

Pendekatan lain yang Anda mungkin ingin bereksperimen dengan permintaan brute force ini menggunakan fungsi ISNULL :

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

Saya harap saran ini membantu !!!

RolandoMySQLDBA
sumber
-4

Ini diharapkan. Untuk kolom yang nol 0 == NULL = "" dan seterusnya. Jadi cek pertama benar-benar mengembalikan baris di mana tidak ada tanggal yang ditetapkan atau dianggap analog dengan "0 / NULL"


sumber
2
0tidak pernah sama dengan NULL. String kosong ( '') tidak sama dengan NULLbaik, kecuali Anda bekerja dengan Oracle.
ypercubeᵀᴹ