MySQL连接查询性能问题
我正在运行be查询MySQL连接查询性能问题,mysql,sql,optimization,indexing,Mysql,Sql,Optimization,Indexing,我正在运行be查询 SELECT packages.id, packages.title, subcat.id, packages.weight FROM packages ,provider, packagestosubcat, packagestocity, subcat, usertosubcat, usertocity, usertoprovider WHERE packages.endDate >'2011-03-11 06:00:00' AND
SELECT packages.id, packages.title, subcat.id, packages.weight
FROM packages ,provider, packagestosubcat,
packagestocity, subcat, usertosubcat,
usertocity, usertoprovider
WHERE packages.endDate >'2011-03-11 06:00:00' AND
usertosubcat.userid = 1 AND
usertocity.userid = 1 AND
packages.providerid = provider.id AND
packages.id = packagestosubcat.packageid AND
packages.id = packagestocity.packageid AND
packagestosubcat.subcatid = subcat.id AND
usertosubcat.subcatid = packagestosubcat.subcatid AND
usertocity.cityid = packagestocity.cityid AND
(
provider.providertype = 'reg' OR
(
usertoprovider.userid = 1 AND
provider.providertype != 'reg' AND
usertoprovider.providerid = provider.ID
)
)
GROUP BY packages.title
ORDER BY subcat.id, packages.weight DESC
当我运行explain时,除了对usertoprovider表的扫描之外,其他一切看起来都正常,该表似乎没有使用表的键:
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE usertocity ref user,city user 4 const 4 Using temporary; Using filesort
1 SIMPLE packagestocity ref city,packageid city 4 usertocity.cityid 419
1 SIMPLE packages eq_ref PRIMARY,enddate PRIMARY 4 packagestocity.packageid 1 Using where
1 SIMPLE provider eq_ref PRIMARY,providertype PRIMARY 4 packages.providerid 1 Using where
1 SIMPLE packagestosubcat ref subcatid,packageid packageid 4 packages.id 1 Using where
1 SIMPLE subcat eq_ref PRIMARY PRIMARY 4 packagestosubcat.subcatid 1
1 SIMPLE usertosubcat ref userid,subcatid subcatid 4 const 12 Using where
1 SIMPLE usertoprovider ALL userid,providerid NULL NULL NULL 3735 Using where
正如您在上面的查询中所看到的,条件本身是:
provider.providertype = 'reg' OR
(
usertoprovider.userid = 1 AND
provider.providertype != 'reg' AND
usertoprovider.providerid = provider.ID
)
provider和usertoprovider这两个表都编制了索引。provider在providerid和providertype上具有索引,而usertoprovider在userid和providerid上具有索引
键的基数为:
provider.id=47,provider.type=1,usertoprovider.userid=1245,usertoprovider.providerid=6
所以很明显,没有使用索引
此外,为了测试它,我继续:
- 复制了usertoprovider表
- 将具有providertype='reg'的所有提供程序值插入到克隆表中
- 将条件简化为(usertoprovider.userid=1和usertoprovider.providerid=provider.ID)
有人能解释一下为什么MySQL仍然运行完全扫描并且不使用密钥吗?如何避免它?似乎
provider.providertype!='reg'
是多余的(始终为true),除非provider.providertype可为NULL,并且您希望查询在NULL时失败
而且不应该=
be
改为标准SQL,尽管MySQL可能允许=代码>
关于表格扫描的成本
完整表扫描并不一定比遍历索引更昂贵,因为遍历索引仍然需要多个页面访问。在许多数据库引擎中,如果您的表足够小,可以容纳几个页面,并且行数足够小,那么进行表扫描会更便宜。数据库引擎根据表的数据和索引统计信息做出此类决策
这个案子
但是,在您的情况下,这也可能是因为OR子句中的另一个分支:provider.providertype='reg'
。如果providertype为“reg”,则此查询将加入usertoprovider的所有行(很可能不是您想要的),因为它是一个多表交叉联接
数据库引擎正确地确定您可能仍然需要usertoprovider中的所有表行(除非providertype中没有一个是“reg”,但引擎也可能知道!)
查询隐藏了这一事实,因为您稍后将对(massible!)结果集进行分组,并且只返回包ID,因此您将看不到返回了多少usertoprovider行。但它将运行得非常缓慢。去掉GROUPBY子句,找出您实际强制数据库引擎处理的行数
如果您填写usertoprovider表,您会看到速度大幅提高的原因是因为这样每一行都参与了一个连接,而在“reg”的情况下没有发生完全的交叉连接。之前,如果usertoprovider中有1000行,则type=“reg”的每一行将结果集扩展1000次。现在,该行仅与usertoprovider中的一行联接,结果集不会展开
如果确实希望使用providertype='reg'传递任何内容,但不希望传递多对多映射表中的内容,那么最简单的方法可能是使用子查询:
从from子句中删除usertoprovider
请执行以下操作:
provider.providertype='reg'或存在(从usertoprovider中选择*其中userid=1和providerid=provider.ID)
另一种方法是在usertoprovider上使用外部联接——表中没有的任何带有“reg”的行都将返回一行NULL,而不是扩展结果集。Hmm,我知道MySQL在分组方面做了一些有趣的事情。在任何其他RDBMS中,您的查询都不会被执行。这是什么意思
SELECT packages.id
[...]
GROUP BY packages.title
ORDER BY subcat.id, packages.weight DESC
您想按标题
分组。然后在标准SQL语法中,这意味着您只能选择title
并聚合其他列的函数。MySQL神奇地尝试执行(并且可能猜测)您想要执行的内容。那么,您希望被选为packages.id
?每个标题
的第一个匹配包ID?还是最后一个?关于分组,orderby
子句意味着什么?如何按不属于结果集的列排序(因为只有packages.title真正属于结果集)
据我所知,有两种解决方案:
您的查询是正确的,然后删除orderby
子句,因为我认为这不会影响您的结果,但可能会严重减慢您的查询速度
您有SQL问题,而不是性能问题
您能给出CREATETABLE语句并将查询包装在多行上吗?你可能想检查一下索引提示:抱歉搞砸了,我已经整理好了信息。嗯。。。对于这些类型的连接,我建议您使用ANSI-JOIN语法。只是让事情更具可读性,而且您可以轻松地将实际谓词与连接条件分开……较新的MySQL版本同时允许“”和“!=”好的。:-)我不知道,但通常坚持标准SQL术语是个好主意。provider表中的条目有两个不同的providertype值,而不是一个值。当我复制usertoprovider表并将providertype='reg'来自provider表的所有条目添加到usertoprovider表中时。将查询更改为(usertoprovider.userid=1和usertoprovider.providerid=provider.ID)。查询时间从8.1317秒减少到0.0387秒。但是,由于具有providertype='reg'的提供程序条目对所有用户都有效