web-dev-qa-db-ja.com

連結選択用のMySQLインデックス

以下のクエリの場合

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
3
farness

_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')

3
ypercubeᵀᴹ

保存する前にデータをクレンジングしてください。そうでない場合、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_が単なる数値であるが、前にいくつかのゼロがある場合、INTBIGINT、または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の最新バージョンでは、「仮想」列にインデックスを付けることができます。

3
Rick James