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%'Example-1 会比 Example-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

这是您的问题。复数。通过改写它们(用“换句话说”),它们只是不同的问题。这样做并不一定会使响应者更容易。相反。

Q1:【题目问题】复合索引最左列的通配符是否意味着索引中的剩余列不用于索引查找(MySQL)?

A1:不,不是那个意思。


Q2:last_name 条件中使用的通配符是否意味着在进一步帮助 MySQL 查找索引时不会使用 first_name 条件?

A2:不,不是这个意思。加上那个问题的尾巴是模棱两可的。它已经知道使用什么索引可能是对这种模糊性的一个分支答案。


Q3:换句话说,通过在 last_name 条件上放置通配符,MySQL 只会进行部分索引查找(并忽略在 last_name 右侧的列中给出的条件)?

A3:不。最右边的列是从索引中提供的,类似于覆盖索引策略,这得益于数据页查找的缓慢性。


Q4:……Example-1 会比 Example-2 快吗?

A4:是的。它是关于这些列的覆盖索引。请参阅覆盖索引。

作为关于第四季度的旁白。它是 PK 还是非 PK 无关紧要。可能有十几个原因导致 PK 对您的应用程序不利。


原始答案如下:

正如您提到的,只有一个复合键和一个查询(last_name,first_name)

WHERE first_name LIKE 'joh%'

...它根本不会使用索引。它将进行表扫描。由于没有

  • 单列键first_name
  • 最左边的复合键first_name

所以我们来了表扫描。

请参阅手册页多列索引以了解更多信息。并专注于left-most它的概念。事实上,去那个页面,然后搜索这个词left

请参阅mysql中解释工具的手册页。还有文章Using Explain to Write Better Mysql Queries


编辑

自从我一两个小时前来到这里以来,对这个问题进行了一些修改。我将为您留下以下内容。通过解释运行您的实际查询,并通过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. 巫毒?是的,因为可能会运行一个小时的查询,所以您要求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 回答
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:……Example-1 会比 Example-2 快吗?

A4:没有。在极端情况下,INDEX(last_name)会比INDEX(last_name, first_name). 任一示例都将仅使用索引的第一部分 (last_name)。但是,磁盘上的复合索引更大。对于一个巨大的表,这可能会导致它被缓存的百分比较小,因此磁盘命中率更高,因此速度更慢。

于 2015-11-30T02:05:04.227 回答
0

我已经确认 Rick James 上面的回答是正确的。但是,Drew 和 Rick James 指出,根据我的 SELECT,我可以使用覆盖索引。

关于在使用通配符时是否使用所有关键部分,MySQL 文档在这里说:

对于 BTREE 索引,间隔可能可用于与 AND 组合的条件,其中每个条件使用 =、<=>、IS NULL、>、<、>=、<=、!=、 <>、BETWEEN 或 LIKE 'pattern'(其中 'pattern' 不以通配符开头)。只要可以确定包含与条件匹配的所有行的单个键元组,就可以使用间隔(如果使用 <> 或 !=,则可以使用两个间隔)。

只要比较运算符是 =、<=> 或 IS NULL,优化器就会尝试使用其他关键部分来确定间隔。如果运算符是 >、<、>=、<=、!=、<>、BETWEEN 或 LIKE,则优化器使用它但不再考虑关键部分。对于以下表达式,优化器使用第一次比较中的 =。它还使用第二个比较中的 >= 但不考虑其他关键部分,并且不使用第三个比较进行区间构造

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 使用两个单独的二级索引。我会让 MySQL 判断哪个具有更好的基数并使用它。但最后,我选择了一个覆盖索引,last_name,first_name,person_id因为我只打算做一个SELECT person_id,而这个作为一个覆盖键(除了搜索 last_name 范围)。在我的测试中,这被证明是最快的。

于 2015-11-30T18:17:06.073 回答