データベースインデックスの作成に関する感想

あけましておめでとうございます。陳です。
今日は、データベースのインデックスについてお話ししたいと思います。ご存じの通り、SQLインデックスはクエリの検索パフォーマンスを向上させ、データベースの効率を高めるためのものです。


インデックス作成

例:CREATE INDEX idx_name ON Student (name);

複合カラムインデックスもよく使われています。


例:CREATE INDEX idx_name_class ON Student (name, class);


インデックス削除

例:DROP INDEX idx_name ON Student; Indexを作成したら、EXPLAIN でindexの使用状況を確認できます。
実行したら、以下のような結果が得られます。
EXPLAIN SELECT * FROM Student WHERE name = 'John';

+----+-------------+---------+------------+------+---------------+----------+-----+------+------+----------+----------------+
| id | select_type | table | partitions | type | possible_keys | key   | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+----------------+
| 1 | SIMPLE | Student | NULL | ref | idx_name | idx_name | 767 | const| 1 | 100.00 | Using index|
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+----------------+


説明:

- id: クエリの選択部分の識別子。外部のクエリの場合は1です。
- select_type: SELECTのタイプ。この場合はSIMPLEで、サブクエリやUNIONのない単純なSELECT。
- table: アクセスされているテーブル。
- type: 使用されているアクセス方法のタイプ(例:フルテーブルスキャンの場合はALL)。
- possible_keys: 使用可能なインデックス。
- key: オプティマイザによって実際に選択されたインデックス。
- key_len: 使用されたキーの長さ。
- ref: インデックス名で指定された列または定数。
- rows: クエリを実行するためにMySQLが調査する行の推定数。
- filtered: テーブル条件によってフィルタリングされた行の割合。
- Extra: クエリの実行に関する追加情報。
 
初めてのindexなら、私はまずpossible_keysにこのindexが考えられるかどうかを確認します。次に、実際に選択されたindexとクエリを実行する情報を確認します。
 
最も重要なのは rows 列です。なぜなら、これはMySQLがクエリに回答するために処理する必要がある行の数を示しています。extra の情報もクエリの実行方法をより理解しやすくします。例えば、extra に ‘using index condition; using where;‘ と書かれている場合、インデックスを使用した後に結果をフィルタリングする必要があったことを示します。
 
複数の複合カラムインデックスを作成して、運用する時、考え通りに動かないことがありますね。explainを実行すると、possible_keysにはあるのに、使われていませんといったケースが発生することがあります。また、使われていても、クエリの効率が上がらないこともあります。
 
それは、複合カラムインデックスに設定されたカラムの順序のためかもしれません。
 
この順序と実行されたクエリ条件の順番が異なる場合、使われてもらっていない状況があります。そのため、複合カラムインデックスを作成する際は、順序が重要です。頻繁にクエリの条件として使用される列が左側にあるほど良いです。
 
また、列の一意性が高いほど、より多くの値をフィルタリングでき、検索の速度が向上します。列の分散性も考慮する必要があります。分散性は、各値の数量の差異を示します。
 
迷った時、Index Hints というものを使ってクエリにするINDEXを指定して違うIndexを使用してexplainで結果を考えることも便利と思います。
 
USE INDEX はindexを使用することを提案しますが、必ずしも指定通りには使用されません。FORCE INDEX は指定したINDEXのみを使用します。
例:
SELECT *
FROM Student USE INDEX (idx_age)
WHERE age > 20;
 
今回の感想は以上です。EXPLAIN に興味がある方は、以下の記事での説明が詳しく、理解しやすいので、お勧めさせていただきます。

Using EXPLAIN in MySQL to analyze and improve query performance:
https://medium.com/datadenys/using-explain-in-mysql-to-analyze-and-improve-query-performance-f58357deb2aa

“0”はTRUEかFALSEか

 初めまして、陳です。今日は、数ヶ月前に文字列処理を行った際のミスと感想を少しご紹介したいと思います。

 文字列の値を判定する際には、有効な文字列であるかどうかや空でないかどうかを気にします。通常、渡された値がNULLか空の文字列であるかどうかをチェックします。

 そのとき、最初に思い浮かぶのはIF条件文ですよね。

 IF条件式は、プログラミングの中で最もよく使われる便利なものだと思います。条件を設定し、結果がTRUEかFALSEかによって処理を分岐させることができます。パラメータの値を基本的にチェックする際には、ほとんどの場合、欲しい値に合致しているかどうかを判断するためにIF条件式を使用します。

 FALSEかの判定に対して、最も簡単で便利な方法は、プログラミング言語におけるデータ値のTRUEかFALSEの判定を利用することです。よく知られているJavaScriptとして、FALSEと判定されるデータ値は以下の通りです:
  • JavaScript:
    – undefined
    – null
    – ブール値の false
    – 数値のゼロ: 0
    – 浮動小数点数のゼロ: 0.0
    – NaN (Not a Number)
    – 空の文字列: “”
 その時、私はそのような認識を持ち、PHPで空の文字列の判定をif(!$string)で処理していました。そして、私は間違えました。

 JavaScriptでは、文字列に対して、””(空文字列)はFALSEと判定されることを知っています。パラメータの値が空文字列でない場合、TRUEと判定されます。したがって、”a”や”0″のいずれもTRUEとなります。

 しかし、PHPの世界では少し異なります。FALSEと判定されるデータ値は以下の通りです:
  • PHP:
    – null
    – ブール値のfalse
    – 数値のゼロ: 0
    – 浮動小数点のゼロ: 0.0
    – 空の配列: []
    – 空の文字列: “”
    – 文字列”0″
 PHPでは、””(空文字列)以外にも、”0″という文字列もFALSEと判定されます。そのため、if(!$string)という条件だけでチェックすると、判定が誤ってしまうことがあります。

 文字列が空値でないのに、FALSEと判定されることに違和感を覚えたので、原因を調べてみました:

 PHPは、ウェブリクエストでの使用を前提として設計されており、頻繁に文字列の入力(URLパラメータやブラウザのフォームからのPOSTリクエストなど)を扱います。そのため、PHPは文字列を他の型に自動的にキャストします。

 これの簡単な例として、’1′ + ‘2’は3を返し、エラーや’12’などの他の解釈は行いません。同じロジックで、文字列’0’は数値0として使用することができます。

 ’0’は数値の0として使用できると言っていますが、数値の0は「空」です。PHPでは、「ゼロである」という特性を「文字列としての特性」よりも重視するため、’0’は「偽」と見なされます。

 要するに‘0’ == 0 == false もしくは (bool)’0′ === (bool)(int)’0′

(資料参考URL

 つまり、’0’という文字列は実は数値0として扱われ、そのためにFALSEと判定されるのです。
そのため、PHPで空文字列かどうかを判定したい場合には、次のような条件式を直接記述することが最善です。
  • $string === “”と空文字列を比較する
  • empty($string) というPHPの組み込み関数を使用して空値かどうかを判定する
  • strlen( $string ) === 0 と文字列の長さが0かどうかをチェックする

 以上、今回、皆さんとシェアしたい内容でした。