以下のクエリの場合
SELECT MAX(CONCAT(date, ' ', last_entry)) AS LAST_LOG
FROM entry_log
WHERE TRIM(LEADING 0 FROM card_no)='2948'
私は索引を付けました
date
card_no
date,last_entry
date,last_entry,card_no
私の説明ショー
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE entry_log index NULL date_last_card 158 NULL 103766 Using where; Using index
私のExplain Extendedショー
id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE entry_log index NULL date_last_card 158 NULL 103766 100.00 Using where; Using index
削除/使用できるインデックスを知りたい、正しいパスにいるのか、上記のクエリの実行時間をどのように改善すべきですか?
CREATE TABLE `entry_log` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`card_no` varchar(50) NOT NULL,
`date` date NOT NULL,
`first_entry` time NOT NULL,
`last_entry` time NOT NULL,
`all_entry` text NOT NULL,
`entry_time` datetime NOT NULL,
`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=9308 DEFAULT CHARSET=latin1
_card_no
_および_log_entry
_のタイプがVARCHAR
またはCHAR
であると想定すると、最初に_(card_no, date, last_entry)
_にインデックスを追加します。
_ALTER TABLE entry_log
ADD INDEX card_no__date__last_entry__ix
(card_no, date, last_entry) ;
_
次に、次のクエリを使用します。
_SELECT CONCAT(date, ' ', last_entry) AS LAST_LOG
FROM entry_log
WHERE card_no = LPAD('2948', 32, '0')
ORDER BY date DESC, last_entry DESC
LIMIT 1 ;
_
IN
リストの値には、タイプで許可されている最大長までのすべての長さが含まれている必要があります。上記の例はVARCHAR(8)
の場合です。
更新:_card_no
_列は常に32文字で、左側にゼロが埋め込まれることが明確にされたため、クエリ条件は次のように簡略化されます。 にWHERE card_no IN ('2948', '02948', '002948', ..., '000...0002948')
WHERE card_no = RIGHT(CONCAT('000..00', '2948'), 32)
または単にWHERE card_no = LPAD('2948', 32, '0')
保存する前にデータをクレンジングしてください。そうでない場合、INDEXは役に立たない可能性があります。
この特定のケースでは、TRIM関数がcard_noを隠しているため、card_noのINDEXが役に立たなくなります。
これSELECTは、インデックスのためにはるかに速く実行されます:
_SELECT MAX(CONCAT(date, ' ', last_entry)) AS LAST_LOG
FROM entry_log
WHERE card_no = '2948'
_
OK、クレンジングのアドバイスは好きではありません。そして、あなたの_card_no
_は常に8桁の数字です。その場合、これはインデックスの使用を許可します:
_WHERE card_no = RIGHT(CONCAT('00000000', '2948'), 8)
_
もう推測する必要がないように_SHOW CREATE TABLE
_を入力してください。
編集
_card_no
_が単なる数値であるが、前にいくつかのゼロがある場合、INT
、BIGINT
、またはDECIMAL(...)
としてよりコンパクトに格納できます。 。次に、LPAD
を実行して、フェッチ時に必要なゼロをプレフィックスとして付けます。
SMALLINT
(2バイト)は、例のように4桁を処理できます。INT
(4バイト)は9桁を処理できます。BIGINT
(8バイト)は18桁を処理できます。DECIMAL(32,0)
(15バイト)は32桁を処理できます。完全な32が必要ない場合は、小さい数を使用できます。
最大の_card_no
_を処理できるサイズを選択する必要があります。
VARCHAR(50)
は2 + Nバイトを使用します。ここで、Nは桁数です。 _card_no
_が長さによって大きく異なる場合でも、これが最善の策である可能性があります。
Edit2
Card_noの行数が多い場合、次のようにすると、実行速度が大幅に向上します。
date
(日付)と_last_entry
_(時間)をDATETIME
データ型の単一の列に結合します。列を_last_log
_と呼びます。SELECT MAX(last_log) AS LAST_LOG
INDEX(card, last_log)
理論的根拠:INDEXは本質的にMAXのすべての作業を行うことができます-これははるかに速く実行されます。
(注:_last_log
_に関するこのコメントは、card_noの先行ゼロに関する説明とは無関係です。インデックスを使用するために、WHERE
句を_WHERE card_no = ...
_として必ず作成してください。)
編集
MySQL 8.0およびMariaDB 10の最新バージョンでは、「仮想」列にインデックスを付けることができます。