0

のプライマリ複合インデックスがあるとしlast_name,first_nameます。次に、 の検索を実行しましたWHERE first_name LIKE 'joh%' AND last_name LIKE 'smi%'

last_name 条件で使用されるワイルドカードは、MySQL がインデックスを見つけるのをさらに支援するために first_name 条件が使用されないことを意味しますか? つまり、last_name 条件にワイルドカードを配置すると、MySQL は部分的なインデックス ルックアップのみを実行します (そして、last_name の右側にある列で指定された条件を無視します)?

私が求めていることのさらなる明確化

例-1: 主キーはlast_name, first_nameです。
例-2: 主キーはlast_nameです。

この WHERE 句を使用するWHERE first_name LIKE 'joh%' AND last_name LIKE 'smi%'と、例 1 は例 2 よりも高速になりますか?

アップデート

ここにsqlfiddleがあります: http://sqlfiddle.com/#!9/6e0154/3

CREATE TABLE `people1` (
    `id` INT(11),
    `first_name` VARCHAR(255) NOT NULL,
    `middle_name` VARCHAR(255) NOT NULL,
    `last_name` VARCHAR(255) NOT NULL,
    PRIMARY KEY (`id`),
    INDEX `name` (`last_name`(15), `first_name`(10))
  )
COLLATE='latin1_swedish_ci'
ENGINE=InnoDB;

CREATE TABLE `people2` (
    `id` INT(11),
    `first_name` VARCHAR(255) NOT NULL,
    `middle_name` VARCHAR(255) NOT NULL,
    `last_name` VARCHAR(255) NOT NULL,
    PRIMARY KEY (`id`),
    INDEX `name` (`last_name`(15))
  )
COLLATE='latin1_swedish_ci'
ENGINE=InnoDB;

INSERT INTO `people1` VALUES
(1,'John','','Smith'),(2,'Joe','','Smith'),(3,'Tom','','Smith'),(4,'George','','Washington');
INSERT INTO `people2` VALUES
(1,'John','','Smith'),(2,'Joe','','Smith'),(3,'Tom','','Smith'),(4,'George','','Washington');

# Query 1A
EXPLAIN SELECT * FROM `people1` WHERE `first_name` LIKE 'joh%' AND `last_name` LIKE 'smi%';
# Query 1B
EXPLAIN SELECT * FROM `people1` WHERE `first_name` LIKE 'joh%' AND `last_name` LIKE 'john';

# Query 2A
EXPLAIN SELECT * FROM `people2` WHERE `first_name` LIKE 'joh%' AND `last_name` LIKE 'smi%';
# Query 2B
EXPLAIN SELECT * FROM `people2` WHERE `first_name` LIKE 'joh%' AND `last_name` LIKE 'john';
4

3 に答える 3

1

@Drewが言ったことは事実上すべて、インデックスが「カバー」していることを前提としています。

INDEX(last_name, first_name)

の「カバー」インデックスです。

SELECT COUNT(*)   FROM t WHERE first_name LIKE 'joh%' AND last_name LIKE 'smi%'.
SELECT last_name  FROM t WHERE first_name LIKE 'joh%' AND last_name LIKE 'smi%'.
SELECT id         FROM t WHERE first_name LIKE 'joh%' AND last_name LIKE 'smi%'. -- if the table is InnoDB and `id` is the `PRIMARY KEY`.

しかし、それは「カバー」ではありません

SELECT foo ...
SELECT foo, last_name ...
etc.

これfooは、インデックスに含まれていないためです。カバーされていない状況の場合、答えは根本的に異なります。

Q1: [タイトルの質問] 複合インデックスの一番左の列のワイルドカードは、インデックスの残りの列がインデックス ルックアップ (MySQL) で使用されないことを意味しますか?

A1:はいそうです。

Q2: last_name 条件で使用されるワイルドカードは、MySQL がインデックスを見つけるのをさらに支援するために first_name 条件が使用されないことを意味しますか?

A2: 漠然としています。オプティマイザーは、問題のインデックスだけでなく、すべてのインデックスを調べます。それは「最高」を選びます。

Q3: つまり、last_name 条件にワイルドカードを配置すると、MySQL は部分的なインデックス ルックアップのみを実行します (そして、last_name の右側にある列に指定された条件を無視します)?

A3:はい。これはQ1の重複のようです。

Q4: ...例 1 は例 2 よりも高速ですか?

A4:いいえ。極端な状況でINDEX(last_name)は、 より遅くなりINDEX(last_name, first_name)ます。どちらの例でも、インデックスの最初の部分 (last_name) のみが使用されます。ただし、複合インデックスはディスク上でより大きくなります。巨大なテーブルの場合、これによりキャッシュされる割合が少なくなり、ディスク ヒットが増えて遅くなる可能性があります。

于 2015-11-30T02:05:04.227 に答える
1

ここにあなたの質問があります。複数。それらを(「言い換えれば」で)言い換えると、それらは単なる異なる質問です。そうすることで、レスポンダーが必ずしも簡単になるわけではありません。それどころか。

Q1: [タイトルの質問] 複合インデックスの一番左の列のワイルドカードは、インデックスの残りの列がインデックス ルックアップ (MySQL) で使用されないことを意味しますか?

A1: いいえ、そうではありません。


Q2: last_name 条件で使用されるワイルドカードは、MySQL がインデックスを見つけるのをさらに支援するために first_name 条件が使用されないことを意味しますか?

A2: いいえ、そうではありません。さらに、その質問の末尾はあいまいです。使用するインデックスが、そのような曖昧さに対する 1 つの派生的な答えになる可能性があることは既にわかっています。


Q3: つまり、last_name 条件にワイルドカードを配置すると、MySQL は部分的なインデックス ルックアップのみを実行します (そして、last_name の右側にある列に指定された条件を無視します)?

A3: いいえ。最も右側の列は、データ ページ ルックアップの遅さから恩恵を受けるカバリング インデックス戦略と同様に、インデックスから提供されます。


Q4: ...例 1 は例 2 よりも高速ですか?

A4: はい。それらの列に関するカバーインデックスです。カバリング インデックスを参照してください。

Q4について余談ですが。PK か非 PK かは関係ありません。PK がアプリケーションにとって恐ろしいものになる理由は、おそらく十数個あります。


以下の元の回答:

あなたが言及したように、複合キーと クエリのみを使用して(last_name,first_name)

WHERE first_name LIKE 'joh%'

... インデックスをまったく使用しません。テーブルスキャンを実行します。不在のため、

  • の単一列キーfirst_name
  • 一番左の複合キーfirst_name

それでは、テーブルスキャンを始めましょう。

詳細については、マニュアル ページの複数列インデックスを参照してください。left-mostそして、そのコンセプトに焦点を当てます。実際、そのページに移動し、単語を検索してくださいleft

mysqlのExplain機能に関するマニュアル ページを参照してください。また、記事Using Explain to Write Better Mysql Queriesも参照してください。


編集

1、2 時間前にここに来てから、質問にいくつかの編集がありました。以下に残します。説明を通じて実際のクエリを実行し、Using Explain ...上記のリンクまたは別の参照を通じて解読します

drop table myNames;
create table myNames
(   id int auto_increment primary key,
    lastname varchar(100) not null,
    firstname varchar(100) not null,
    col4 int not null,
    key(lastname,firstname)
);
truncate table myNames;
insert myNames (lastName,firstName,col4) values
('Smith','John',1),('Smithers','JohnSomeone',1),('Smith3','John4324',1),('Smi','Jonathan',1),('Smith123x$FA','Joh',1),('Smi3jfif','jkdid',1),('r3','fe2',1);

insert myNames (lastName,firstName,col4) select lastname,firstname,col4 from mynames;
insert myNames (lastName,firstName,col4) select lastname,firstname,col4 from mynames;
insert myNames (lastName,firstName,col4) select lastname,firstname,col4 from mynames;
insert myNames (lastName,firstName,col4) select lastname,firstname,col4 from mynames;
insert myNames (lastName,firstName,col4) select lastname,firstname,col4 from mynames;
insert myNames (lastName,firstName,col4) select lastname,firstname,col4 from mynames;
insert myNames (lastName,firstName,col4) select lastname,firstname,col4 from mynames;
insert myNames (lastName,firstName,col4) select lastname,firstname,col4 from mynames;
insert myNames (lastName,firstName,col4) select lastname,firstname,col4 from mynames;
insert myNames (lastName,firstName,col4) select lastname,firstname,col4 from mynames;
insert myNames (lastName,firstName,col4) select lastname,firstname,col4 from mynames;
insert myNames (lastName,firstName,col4) select lastname,firstname,col4 from mynames;
insert myNames (lastName,firstName,col4) select lastname,firstname,col4 from mynames;
insert myNames (lastName,firstName,col4) select lastname,firstname,col4 from mynames;
insert myNames (lastName,firstName,col4) select lastname,firstname,col4 from mynames;
insert myNames (lastName,firstName,col4) select lastname,firstname,col4 from mynames;

select count(*) from myNames; 
-- 458k rows

select count(*)
from myNames
where lastname like 'smi%';
-- 393216 rows

select count(*)
from myNames
where lastname like 'smi%' and firstname like 'joh%';
-- 262144 rows

Explainのブードゥー教の数字をレンダリングしrowsます。ブードゥー?はい、クエリは 1 時間実行される可能性があるためexplain、ファジー カウントを実行するのではなく、2 秒以内にその答えを返すように求めています。なしで実際に実行する場合、これらを基準の実際のカウント # と見なさないでくださいexplain

explain 
select count(*) 
from myNames 
where lastname like 'smi%';
+----+-------------+---------+-------+---------------+----------+---------+------+--------+--------------------------+
| id | select_type | table   | type  | possible_keys | key      | key_len | ref  | rows   | Extra                    |
+----+-------------+---------+-------+---------------+----------+---------+------+--------+--------------------------+
|  1 | SIMPLE      | myNames | range | lastname      | lastname | 302     | NULL | 233627 | Using where; Using index |
+----+-------------+---------+-------+---------------+----------+---------+------+--------+--------------------------+

explain 
select count(*) 
from myNames 
where lastname like 'smi%' and firstname like 'joh%' and col4=1;
+----+-------------+---------+-------+---------------+----------+---------+------+--------+--------------------------+
| id | select_type | table   | type  | possible_keys | key      | key_len | ref  | rows   | Extra                    |
+----+-------------+---------+-------+---------------+----------+---------+------+--------+--------------------------+
|  1 | SIMPLE      | myNames | range | lastname      | lastname | 604     | NULL | 233627 | Using where; Using index |
+----+-------------+---------+-------+---------------+----------+---------+------+--------+--------------------------+


-- the below chunk is interest. Look at the Extra column

explain 
select count(*) 
from myNames 
where lastname like 'smi%' and firstname like 'joh%' and col4=1;
+----+-------------+---------+------+---------------+------+---------+------+--------+-------------+
| id | select_type | table   | type | possible_keys | key  | key_len | ref  | rows   | Extra       |
+----+-------------+---------+------+---------------+------+---------+------+--------+-------------+
|  1 | SIMPLE      | myNames | ALL  | lastname      | NULL | NULL    | NULL | 457932 | Using where |
+----+-------------+---------+------+---------------+------+---------+------+--------+-------------+

explain 
select count(*) 
from myNames 
where firstname like 'joh%';
+----+-------------+---------+-------+---------------+----------+---------+------+--------+--------------------------+
| id | select_type | table   | type  | possible_keys | key      | key_len | ref  | rows   | Extra                    |
+----+-------------+---------+-------+---------------+----------+---------+------+--------+--------------------------+
|  1 | SIMPLE      | myNames | index | NULL          | lastname | 604     | NULL | 453601 | Using where; Using index |
+----+-------------+---------+-------+---------------+----------+---------+------+--------+--------------------------+


analyze table myNames;
+----------------------+---------+----------+----------+
| Table                | Op      | Msg_type | Msg_text |
+----------------------+---------+----------+----------+
| so_gibberish.mynames | analyze | status   | OK       |
+----------------------+---------+----------+----------+

select count(*) 
from myNames where left(lastname,3)='smi';
-- 393216 -- the REAL #
select count(*) 
from myNames where left(lastname,3)='smi' and left(firstname,3)='joh';
-- 262144 -- the REAL #

explain 
select lastname,firstname 
from myNames  
where lastname like 'smi%' and firstname like 'joh%';
+----+-------------+---------+-------+---------------+----------+---------+------+--------+--------------------------+
| id | select_type | table   | type  | possible_keys | key      | key_len | ref  | rows   | Extra                    |
+----+-------------+---------+-------+---------------+----------+---------+------+--------+--------------------------+
|  1 | SIMPLE      | myNames | range | lastname      | lastname | 604     | NULL | 226800 | Using where; Using index |
+----+-------------+---------+-------+---------------+----------+---------+------+--------+--------------------------+
于 2015-11-29T06:28:25.693 に答える
0

上記のリック・ジェームスの答えが正しいことを確認しました。ただし、Drew と Rick James は、私の SELECT によってはカバリング インデックスを使用できると指摘しています。

ワイルドカードを使用するときにすべてのキー部分が使用されるかどうかについては、MySQL のドキュメントに次のように記載されています

BTREE インデックスの場合、間隔は、AND と組み合わせた条件に使用できる場合があります。各条件は、=、<=>、IS NULL、>、<、>=、<=、!=、 <>、BETWEEN、または LIKE 'pattern' ('pattern' がワイルドカードで始まらない場合)。条件に一致するすべての行を含む単一のキー タプルを決定できる限り、間隔を使用できます (<> または != が使用されている場合は 2 つの間隔)。

オプティマイザーは、比較演算子が =、<=>、または IS NULL である限り、追加のキー部分を使用して間隔を決定しようとします。演算子が >、<、>=、<=、!=、<>、BETWEEN、または LIKE の場合、オプティマイザーはそれを使用しますが、それ以上のキー部分は考慮しません。次の式では、オプティマイザーは最初の比較から = を使用します。また、2 番目の比較から >= を使用しますが、それ以上の重要な部分を考慮せず、間隔の構築に 3 番目の比較を使用しません

key_part1 = 'foo' AND key_part2 >= 10 AND key_part3 > 10

単一の間隔は次のとおりです。

('foo',10,-inf) < (key_part1,key_part2,key_part3) < ('foo',+inf,+inf)

作成された間隔には、初期条件よりも多くの行が含まれる可能性があります。たとえば、前の間隔には、元の条件を満たさない値 ('foo', 11, 0) が含まれています。

コンポジットのキー部分で LIKE を使用する場合、右側のキー部分は使用されません。これにより、last_name と first_name に 2 つの個別のセカンダリ インデックスを使用する必要が生じました。どちらのカーディナリティが優れているかをMySQLに判断させて使用します。しかし、最終的には、(last_name 範囲の検索に加えて) カバリング キーとして機能するlast_name,first_name,person_idだけのつもりだったので、カバリング インデックスを使用しました。SELECT person_id私のテストでは、これが最速であることが証明されました。

于 2015-11-30T18:17:06.073 に答える