Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/mysql/55.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
MySQL连接查询性能问题_Mysql_Sql_Optimization_Indexing - Fatal编程技术网

MySQL连接查询性能问题

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

我正在运行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这两个表都编制了索引。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)
查询执行时间从8.1317秒更改为0.0387秒。

尽管如此,具有providertype='reg'的提供程序值对所有用户都有效,我希望避免将这些值插入所有用户的usertoprovider表中,因为这些数据是冗余的


有人能解释一下为什么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'的提供程序条目对所有用户都有效