我正在运行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
      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表都被索引。提供程序在providerid和providertype上有索引,而usertoprovider在userid和providerid上有索引
密钥的基数是:
provider.id=47,provider.type=1,usertoprovider.userid=1245,usertoprovider.providerid=6
所以很明显索引没有被使用。
更进一步,为了测试它,我继续说:
复制了usertoprovider表
将providertype为'reg'的所有提供程序值插入到克隆的表中
将条件简化为(usertoprovider.userid=1和usertoprovider.provider ID=provider.ID)
查询执行时间已从8.1317秒更改。到0.0387秒。
不过,providertype为'reg'的提供程序值对所有用户都有效,我希望避免将这些值插入到所有用户的usertoprovider表中,因为这些数据是多余的。
有人能解释一下为什么MySQL仍然运行一个完整的扫描并且不使用密钥吗?怎样才能避免呢?

最佳答案

似乎provider.providertype != 'reg'是多余的(始终为true),除非provider.providertype可以为空,并且您希望查询在空时失败。
尽管MySQL可能允许!=,但<>不应该成为标准SQL吗?
关于表扫描的成本
完全表扫描并不一定比遍历索引更昂贵,因为遍历索引仍然需要多个页面访问。在许多数据库引擎中,如果表足够小,可以放在几页中,并且行数足够小,那么进行表扫描会更便宜。数据库引擎根据表的数据和索引统计信息做出此类决策。
这个案子
然而,在您的情况下,这也可能是因为OR子句中的另一条腿:!=。如果providertype是“reg”,那么这个查询将连接到usertoprovider的所有行(很可能不是您想要的),因为它是一个多表交叉连接。
数据库引擎确定您可能无论如何都需要usertoprovider中的所有表行是正确的(除非所有provider类型都不是“reg”,但引擎也可能知道!).
查询隐藏了这个事实,因为您正在(MASSIVE!)结果集稍后返回包ID,这样就看不到返回了多少usertoprovider行。但它会跑得很慢。去掉GROUPBY子句,找出您实际强制数据库引擎工作的行数!!!
如果您填写usertoprovider表,您会看到一个巨大的速度改进,这是因为随后每一行都参与一个连接,并且在“reg”的情况下没有发生完全交叉连接。以前,如果usertoprovider中有1000行,那么type=“reg”的每一行都会将结果集扩展1000倍。现在,该行只与usertoprovider中的一行联接,结果集不会展开。
如果您确实想传递providertype为'reg'的任何内容,但不是在多对多映射表中,那么最简单的方法可能是使用子查询:
从from子句中删除usertoprovider
执行以下操作:
provider.providertype = 'reg'
另一种方法是在usertoprovider上使用外部联接——表中不包含“reg”的任何行都将返回一行NULL,而不是展开结果集。

10-08 00:49