web-dev-qa-db-ja.com

MySQL IS NULL / IS NOT NULL不正な動作?

次の表をご覧ください。

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

上記のカウントは一致しません。私の理解によると:

IS NULLを使用したカウントとIS NOT NULLを使用したカウントは、where句を使用せずにクエリを実行した場合のカウントと等しくなければなりません。

ここで何が起こっているかについて何か考えはありますか?

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

2012年2月17日の更新

それ以来、推定された日付が現在持っている値の種類について多くの人が質問していることがわかりました。ここに答えがあります:

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)

上記からわかるように、estimated_dateにはNULLまたは有効な日時値があります。ゼロまたは空の文字列 ""はありません。

これは、推定された日付のインデックスに問題がある場合に発生しますか?

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

2012年2月18日の更新

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です。

18
user1213259

ゼロ日はありますか? MySQLは0000-00-00 00:00:00の日時値を考慮して、is nullis 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=94

これは「バグではない」として分類されます。彼らは回避策を提案します:strictモードを使用してください。これは挿入警告をエラーに変換します。

とはいえ、これだけでは、得られる結果の変動を説明することはできません(is nullカウントとis not nullカウントの合計が無制限のカウントを超える必要があります)...

6
araqnid

@ypercube:

私は最近、「WHEREオペランドが主キーまたは一意のインデックスにある場合にSELECT COUNT(DISTINCT)がInnoDBをクラッシュさせる」という回帰バグがその原因であると考えられるかどうか尋ねられました。

これが私の返答です(元々ここにあります):

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

これは同じバグではないと思います。このバグはクラッシュの詳細であり、特にSELECT COUNT(DISTINCT)が必要です。さらに、WHEREオペランドは主キーまたは一意のインデックスにあります。

バグ/問題にはDISTINCTがなく、クラッシュもせず、日時列のインデックスは主キーでも一意でもありません。しかし、それは袖口から少し奇妙なので、私はいくつかの検索を行いましたが、このバグに遭遇しました。

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

実際には「バグではない」と指定されていますが、日付/日時が「0000-00-00」でISを使用している場合に、奇妙な動作が発生する可能性がある方法を示していますNULLおよびIS NOT NULL。

カウントに影響を与える可能性のあるこれらの「0000-00-00」行のいずれかがあるのでしょうか。

バグレポートでコメントしている開発者がこのページについても言及していることに注意してください。

そうでない場合は、5.5.12から9か月(および9回のリリース)となっているため、最新の5.5(5.5.21(2012年2月22日現在))でアップグレードして試してみることをお勧めします。解放された。

テーブル(およびデータ)をダンプし、別のテストインスタンスにインポートして、テストするだけでよいことに注意してください。そうすれば、本番マシンに影響を与えず、テストインスタンスを数分でセットアップできます。

次に、それでも違いが生じない場合は、おそらくテーブルをMyISAMに変換して、問題がグローバルなものか、それともInnoDBに固有のものかを確認するなど、他のいくつかの項目をテストすることができます。

または、「estimated_date」のインデックスが次のとおりであることに気づきました:

キーestimated_date_indexestimated_date)BTREEの使用

「USING BTREE」に注意してください。おそらくUSING BTREEなしで試して、同じ動作が続くかどうかを確認してください。 (または、単にテストするためにインデックスを完全に削除します。これはすべて、問題を絞り込むのに役立ちます)。

お役に立てれば。

3
Chris Calender

テーブルのレイアウトに「数えたくない」と叫ぶ興味深いものが表示されます。私が言おうとしているのは、予感に過ぎません。

以前にこのクエリを実行した

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; four(4)回。 Cardinality列を見てください。テーブルs_p InnoDBでは、[カーディナリティ]列が毎回異なることを期待しています。どうして?

InnoDBは、BTREEページエントリを介してカウントすることで、カーディナリティ値を推定します(PUNは意図されていません)。システム変数 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;

これにより、null以外の見積もられた日付を持つ行の数が得られます。

[〜#〜] isnull [〜#〜] 関数を使用して、このブルートフォースクエリを試すことができる別のアプローチ:

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

これらの提案がお役に立てば幸いです!!!

1
RolandoMySQLDBA

クエリを試す

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