问题描述
具有以下架构:
sqlite>
sqlite>
sqlite> .schema
CREATE TABLE movie (
id INTEGER PRIMARY KEY,title TEXT,year INTEGER,nth TEXT,for_video BOOLEAN
);
CREATE TABLE actor (
id INTEGER PRIMARY KEY,name TEXT,gender TEXT
);
CREATE TABLE role (
movie_id INTEGER,actor_id INTEGER,name TEXT
);
CREATE TABLE sqlite_stat1(tbl,idx,stat);
sqlite>
在两个表上运行JOIN
,如下所示:
sqlite> select * from movie JOIN role ON (movie.id = role.movie_id) WHERE movie.title='Batman' LIMIT 1;
"id" "title" "year" "nth" "for_video" "movie_id" "actor_id" "name"
"47844" "Batman" "1989" "" "0" "47844" "84264" "Napier Hood"
sqlite>
sqlite> create index id1 on role(movie_id);
sqlite>
sqlite>
sqlite> create index id2 on movie(title);
sqlite>
sqlite>
然后查询计划说:
案例1
sqlite> EXPLAIN QUERY PLAN select * from movie JOIN role ON (movie.id = role.movie_id) WHERE movie.title='Batman' LIMIT 1;
"selectid" "order" "from" "detail"
"0" "0" "0" "SEARCH TABLE movie USING INDEX id2 (title=?)"
"0" "1" "1" "SEARCH TABLE role USING INDEX id1 (movie_id=?)"
案例2
sqlite> EXPLAIN QUERY PLAN select movie.title,role.name from movie JOIN role ON (movie.id = role.movie_id)
...> WHERE role.name = 'King Arthur' LIMIT 2;
"selectid" "order" "from" "detail"
"0" "0" "0" "SCAN TABLE movie USING COVERING INDEX id2"
"0" "1" "1" "SEARCH TABLE role USING INDEX id1 (movie_id=?)"
sqlite>
sqlite>
在两种情况下:
解决方法
数据库引擎如何确定需要先搜索电影表然后搜索角色表?
您的WHERE
子句通过其title
来请求电影,并且有一个索引,因此,一定要先获取仅具有该标题的电影,然后获取其ID,这是有道理的,然后获得具有这些ID(也有索引查找)的角色,然后将少量结果结合在一起。
反过来就没什么意义了:获取所有80,000部电影并将它们与1000个不同的角色联系起来,以列出80,000部电影角色,然后将它们与标题X分开。
这是关于特定数据库如何计划此特定查询的极其简单的视图;可以计划和执行查询的方式有很多种。要解释优化器/计划者采取的每个步骤和每个决策,都将超出SO答案的范围
对于第二种情况,SQLite似乎得出了这样的结论:它必须以未索引的内容进行搜索,并且必须返回两位数据。一种被索引,另一种未被索引。它已决定采取一种策略,将所有电影标题从索引而不是表中提取出来(索引可以提供标题,而SQLite则更喜欢使用它来检索数据而不是表),从而将电影加入角色中根据角色中的movie_id索引编制,然后过滤所有工作,仅留下亚瑟王角色名称和相关的电影标题
为什么数据库引擎在第二种情况下扫描电影表?而不是搜索
这不是在搜索表,而是在扫描索引,并且正在执行扫描,因为查询不要求索引的任何内容,因此必须检索每个值并进行比较以查找所需内容
,不是特定于mySql的-因此,如果MySQL绝对低于par,则其工作方式类似于“语句中从左到右”。
通常,任何databsae服务器都具有STATISTICS并estimages针对给定过滤器在哪个索引上将返回多少行,然后尝试各种方法(全部基于这些统计信息)以查看哪种方法最有效。然后执行这个。当您按Movie.Title进行过滤时,很有可能会先执行此操作,然后在另一个表中找到匹配的行。