MySQLの暗黙的な変換問題の解決

MySQLの暗黙的な変換問題の解決

1. 問題の説明

root@mysqldb 22:12: [xucl]> テーブル t1\G の作成を表示します
************************** 1. 行 ****************************
 表: t1
テーブルの作成: CREATE TABLE `t1` (
 `id` varchar(255) デフォルト NULL
) エンジン=InnoDB デフォルト文字セット=utf8
セット内の 1 行 (0.00 秒)
 
root@mysqldb 22:19: [xucl]> t1 から * を選択します。
+--------------------+
|id|
+--------------------+
| 204027026112927605 |
| 204027026112927603 |
| 2040270261129276 |
| 2040270261129275 |
| 100 |
| 101 |
+--------------------+
セット内の 6 行 (0.00 秒)

奇妙な現象:

root@mysqldb 22:19: [xucl]> id=204027026112927603 の t1 から * を選択します。
+--------------------+
|id|
+--------------------+
| 204027026112927605 |
| 204027026112927603 |
+--------------------+
セット内の 2 行 (0.00 秒)
640?wx_fmt=jpeg 

一体全体、204027026112927603 を確認したのに、なぜ 204027026112927605 も出てきたのでしょうか?

2. ソースコードの説明

コールスタックの関係は次のとおりです。

JOIN::exec()は実行エントリポイントであり、Arg_comparator::compare_real()は等価判定関数であり、以下のように定義される。

int Arg_comparator::compare_real()
{
 /*
 Bug#2338のもう一つの症状を修正。「Volatile」は次のように指示します。
 gccは80ビットIntel FPUレジスタからdouble値をフラッシュする前に
 比較を実行します。
 */
 揮発性ダブル val1、val2;
 val1 = (*a)->val_real();
 if (!(*a)->null_value)
 {
 val2 = (*b)->val_real();
 if (!(*b)->null_value)
 {
 (set_null)の場合
 所有者->null_value = 0;
 val1 < val2 の場合は -1 を返します。
 val1 == val2 の場合は 0 を返します。
 1 を返します。
 }
 }
 (set_null)の場合
 所有者->null_value = 1;
 -1 を返します。
}

比較手順を下図に示します。t1 テーブルの id 列が行ごとに読み取られ、val1 に格納されます。定数 204027026112927603 がキャッシュに存在し、その型は double (2.0402702611292762E+17) です。したがって、値が val2 に渡された後、val2=2.0402702611292762E+17 になります。

1行目までスキャンすると、204027026112927605をdouleに変換すると2.0402702611292762e17となり、等式が成立し、適格行であると判定され、スキャンが続行されます。同様に、204027026112927603も条件を満たしています。

文字列型数値から倍精度型数値への変換がオーバーフローするかどうかを検出するにはどうすればよいでしょうか。ここでテストしたところ、数値が16桁を超えると、倍精度型への変換は正確ではなくなりました。たとえば、20402702611292711は、20402702611292712と表現されます(図のval1に示すように)

MySQL 文字列を double に変換するための定義関数は次のとおりです。

{
 char buf[DTOA_BUFF_SIZE];
 ダブル解像度;
 DBUG_ASSERT(end != NULL && ((str != NULL && *end != NULL) ||
    (str == NULL && *end == NULL)) &&
  エラー != NULL);

 res = my_strtod_int(str、end、error、buf、sizeof(buf));
 戻り値 (*error == 0) ? res : (res < 0 ? -DBL_MAX : DBL_MAX);
}

実際の変換関数 my_strtod_int は dtoa.c にあります (複雑すぎるので、コメントを投稿してください)

/*
 IEEE 算術マシン用の strtod。
 
 このstrtodは入力された10進数に最も近い機械数を返します。
 文字列(またはerrnoをEOVERFLOWに設定)。同点の場合はIEEEラウンドイーブンで決着する。
 ルール。
 
 ウィリアム・D・クリンガーの論文「浮動小数点数の読み方」に大まかに触発されて
 「ポイント番号を正確に示す」[Proc. ACM SIGPLAN '90、pp. 92-101]。
 
 変更点:
 
 1. 必要なのは IEEE のみです (IEEE ダブル拡張ではありません)。
 2. 浮動小数点演算で済むケース
 クリンガーは失敗しました -- d * 10^n を計算しているとき
 小さな整数dと整数nが大きすぎる場合は
 22(kが最大となる整数)よりはるかに大きい
 10^kを正確に表現できる場合、
 1回の丸めだけで (d*10^k) * 10^(ek) を計算します。
 3. バイナリを少しずつ調整するのではなく
 その結果、難しいケースでは浮動小数点を使用する。
 調整を決定するための算術
 1ビット;本当に難しい場合にのみ、
 2番目の残差を計算します。
 4. 3. のため、10の累乗の大きな表は必要ありません。
 10の倍数(例えば10^kの小さな表)
 (0 <= k <= 22)。
*/

この場合、オーバーフローが発生しない場合をテストしてみましょう。

root@mysqldb 23:30: [xucl]> id=2040270261129276 の t1 から * を選択します。
+------------------+
|id|
+------------------+
| 2040270261129276 |
+------------------+
セット内の 1 行 (0.00 秒)
 
root@mysqldb 23:30: [xucl]> id=101 の場合、t1 から * を選択します。
+------+
|id|
+------+
| 101 |
+------+
セット内の 1 行 (0.00 秒)

結果は予想通りであり、この場合、正しい書き方は次のようになります。

root@mysqldb 22:19: [xucl]> id='204027026112927603' の t1 から * を選択します。
+--------------------+
|id|
+--------------------+
| 204027026112927603 |
+--------------------+
セット内の1行(0.01秒)

結論

暗黙的な型変換は避けてください。暗黙的な変換には、主に、一貫性のないフィールド型、in パラメータ内の複数の型、一貫性のない文字セット型または校正ルールなどが含まれます。

暗黙的な型変換により、インデックスが使用できなくなったり、クエリ結果が不正確になったりする可能性があるため、使用時には慎重に識別する必要があります。

フィールドを定義するときは、数値型を int または bigint として定義することをお勧めします。テーブルがリンクされている場合、関連付けられているフィールドは同じ型、文字セット、および照合ルールを維持する必要があります。

最後に、暗黙的な型変換についての公式サイトの説明を掲載しておきます。

1. 片方または両方の引数がNULLの場合、比較の結果はNULLになります(NULLセーフの場合を除く)。
<=> 等価比較演算子。NULL <=> NULL の場合、結果は true になります。変換は必要ありません。
2.比較演算の両方の引数が文字列の場合、それらは文字列として比較されます。
3. 両方の引数が整数の場合、整数として比較されます。
4. 16 進数値は、数値と比較されない場合はバイナリ文字列として扱われます。
5. 引数の1つがTIMESTAMPまたはDATETIME列で、もう1つの引数が
定数の場合、比較を実行する前に定数がタイムスタンプに変換されます。これは
これはODBCとの親和性を高めるために行われます。IN()の引数には適用されません。安全のため、常に
比較を行うときは、完全な日付時刻、日付、または時刻文字列を使用します。たとえば、最高の結果を得るには、
日付または時刻の値でBETWEENを使用する場合は、CAST()を使用して値を明示的に変換します。
必要なデータ型。
テーブルからの単一行のサブクエリは定数とはみなされません。たとえば、サブクエリが
DATETIME 値と比較する整数を返します。比較は 2 つの整数として行われます。
整数は時間値に変換されません。オペランドをDATETIME値として比較するには、
CAST() を使用して、サブクエリの値を DATETIME に明示的に変換します。
6. 引数の 1 つが 10 進数値の場合、比較は他の引数に依存します。
引数は、他の引数が小数または整数値の場合は小数値として比較され、
他の引数が浮動小数点値の場合、浮動小数点値になります。
7. それ以外の場合、引数は浮動小数点(実数)として比較されます。

要約する

上記は、編集者が紹介したMySQLの暗黙的な変換です。皆様のお役に立てれば幸いです。ご質問がある場合は、メッセージを残してください。編集者がすぐに返信します。また、123WORDPRESS.COM ウェブサイトをサポートしてくださっている皆様にも感謝申し上げます。
この記事が役に立ったと思われた方は、ぜひ転載していただき、出典を明記してください。ありがとうございます!

以下もご興味があるかもしれません:
  • MySQLの整数および文字列インデックスの無効化または暗黙的な変換に関する簡単な説明
  • MySQLの驚くべき暗黙の変換
  • MySQL 5.6 の「暗黙的な変換」によりインデックスが失敗し、データが不正確になる
  • MySQLの暗黙的な変換について話す
  • MySQL インデックス無効化の暗黙的な変換の問題

<<:  Centos7のシステム言語を簡体字中国語に変更する方法

>>:  jQuery カスタム虫眼鏡効果

推薦する

docker を使用して Kong クラスター操作を構築する

docker コンテナの下に kong クラスターを構築するのは非常に簡単です。公式サイトの紹介も非...

JavaScript の基本演算子

目次1. オペレーター要約する1. オペレーター演算子は、代入、比較、算術演算などの機能を実装するた...

MySQLに挿入する前にデータが存在するかどうかを確認する方法

ビジネスシナリオ: 訪問者の訪問状況を記録する必要があるが、繰り返し記録することはできない挿入する前...

Nginx コンテンツ キャッシュと共通パラメータ設定の詳細

使用シナリオ:プロジェクトのページでは、頻繁に変更されず、個別のカスタマイズも伴わない大量のデータを...

CSSアニメーションを使用して背景のシームレスな無限ループを実装する例

1. 需要絵が左から右へ無限ループで動く2. コードモバイルデバイスに適用されているため、rem 単...

MySQLパスワードを忘れた場合のいくつかの解決策

解決策1完全にアンインストールしてすべてのデータを削除します。まず、MySQLに関連するすべてのプロ...

最新のmysql-5.7.21のインストールと設定方法

1. ダウンロードしたMySQLの圧縮パッケージをインストールディレクトリに解凍します。 2. 新し...

Linux 7.6 バイナリに MySQL 8.0.27 をインストールする詳細な手順

目次1. 環境整備1.1 オペレーティング システムのバージョン1.2 ディスク容量1.3 ファイア...

MySQL 8.0 以降の一般的なコマンドの詳細な説明

リモートアクセスを有効にする次のコマンドを実行して、root ユーザーのリモート アクセス権を有効に...

Nginx proxy_pass の / スラッシュによって引き起こされた殺人事件の詳細な説明

背景nginx サーバー モジュールは 2 つのサーバーにプロキシする必要があるため、異なるサーバー...

Win7x64でのMySQL 5.7.18解凍版のインストール方法

関連記事: Win7 x64 に解凍版の mysql 5.7.18 winx64 をインストールする...

Node.js で Bash スクリプトを書くための究極のソリューション

目次序文zxライブラリ$`コマンド` CD()フェッチ()質問()寝る()スローしない()チョークフ...

Linuxで大きなファイルを素早くコピーする方法

データをコピーリモートでデータをコピーする場合、通常は rsync コマンドを使用しますが、小さなフ...

シームレスなカルーセル効果を実現するネイティブ js

参考までに、ネイティブjsでカルーセル効果(シームレススクロール)を実現しています。具体的な内容は以...

Vue でクラスとスタイルを使用して v-bind バインディングを使用するいくつかの方法

要素にクラスを追加/削除することは、プロジェクト開発では非常に一般的な動作です。たとえば、Web サ...