帮我优化这个MySql查询

时间:2011-08-31 13:58:31

标签: mysql sql optimization indexing

我有一个MySql查询需要很长时间才能运行(大约7秒)。问题似乎与查询的这一部分中的OR有关:“(tblprivateitem.userid =?userid OR tblprivateitem.userid = 1)”。如果我跳过“OR tblprivateitem.userid = 1”部分,则只需0.01秒。由于我需要这部分,我需要找到一种优化此查询的方法。有什么想法吗?

QUERY:

SELECT 
    tbladdeditem.addeditemid,
    tblprivateitem.iitemid,
    tblprivateitem.itemid
FROM tbladdeditem 
INNER JOIN tblprivateitem 
    ON tblprivateitem.itemid=tbladdeditem.itemid 
        AND (tblprivateitem.userid=?userid OR tblprivateitem.userid=1)
WHERE tbladdeditem.userid=?userid

说明:

id    select_type    table            type    possible_keys    key    key_len    ref                    rows    extra
1     SIMPLE         tbladdeditem     ref     userid           userid 4          const                  293     Using where
1     SIMPLE         tblprivateitem   ref     userid,itemid    itemid 4          tbladdeditem.itemid    2       Using where

TABLES:

tbladdeditem包含1 100 000行:

CREATE TABLE `tbladdeditem` (
    `addeditemid` int(11) NOT NULL auto_increment,
    `itemid` int(11) default NULL,
    `userid` mediumint(9) default NULL,
    PRIMARY KEY  (`addeditemid`),
    KEY `userid` (`userid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

tblprivateitem包含2 700 000行:

CREATE TABLE `tblprivateitem` (
    `privateitemid` int(11) NOT NULL auto_increment,
    `userid` mediumint(9) default '1',
    `itemid` int(10) NOT NULL,
    `iitemid` mediumint(9) default NULL,
    PRIMARY KEY  (`privateitemid`),
    KEY `userid` (`userid`),
    KEY `itemid` (`itemid`) //Changed this index to only use itemid instead
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

3 个答案:

答案 0 :(得分:1)

我会尝试这样做,在您的原始JOIN上,您有OR与参数关联,将其移至WHERE子句。

SELECT 
    tbladdeditem.addeditemid,
    tblprivateitem.iitemid,
    tblprivateitem.itemid
FROM tbladdeditem 
INNER JOIN tblprivateitem 
    ON tblprivateitem.itemid=tbladdeditem.itemid 
WHERE tbladdeditem.userid=?userid
    AND (tblprivateitem.userid=?userid OR tblprivateitem.userid=1)

答案 1 :(得分:1)

由于你在where子句中有谓词条件tbladdeditem.userid=?userid,我认为你不需要它在连接条件中。尝试从连接条件中删除它(如果你使用Or来处理如果参数为null,则使用Coalesce而不是OR),如果不将其保留为Or

-- If Or is to provide default for when (?userid is null...
      SELECT  a.addeditemid, p.iitemid,  p.itemid 
      FROM tbladdeditem a 
        JOIN tblprivateitem p
          ON p.itemid=a.itemid 
      WHERE a.userid=?userid 
         AND p.userid=Coalesce(?userid, 1)
-- if not then
      SELECT  a.addeditemid, p.iitemid,  p.itemid 
      FROM tbladdeditem a 
        JOIN tblprivateitem p
          ON p.itemid=a.itemid 
      WHERE a.userid=?userid 
         AND (p.userid=?userid Or p.userid = 1)

其次,如果这两个表中的userId列没有索引,请考虑添加一个索引。

最后,如果这些都失败了,请尝试转换为两个单独的查询并将它们合并在一起:

      Select  a.addeditemid, p.iitemid,  p.itemid 
      From tbladdeditem a 
        Join tblprivateitem p
          On p.itemid=a.itemid 
            And p.userId = a.Userid
      Where p.userid=?userid 
      Union
      Select a.addeditemid, p.iitemid,  p.itemid 
      From tbladdeditem a 
        Join tblprivateitem p
          On p.itemid=a.itemid 
            And p.userId = a.Userid
      Where p.userid = 1

答案 2 :(得分:1)

<强>更新

我使我的查询和架构完全匹配您的原始问题,多列密钥和所有。唯一可能的区别是我在每个表中填入了两百万个条目。我的查询(您的查询)在0.15秒内运行。

delimiter $$
set @userid = 6
$$
SELECT 
    tbladdeditem.addeditemid,    tblprivateitem.iitemid,    tblprivateitem.itemid
FROM tbladdeditem 
INNER JOIN tblprivateitem 
    ON tblprivateitem.itemid=tbladdeditem.itemid 
        AND (tblprivateitem.userid=@userid or tblprivateitem.userid = 1)
WHERE tbladdeditem.userid=@userid

我有相同的解释,你和我的数据,我的查询返回超过一千场比赛没有任何问题。完全不知所措,因为你真的不应该遇到这些问题 - 你是否有可能运行极限版本的MySQL?你在运行64位吗?记忆力很强?

我曾假设您的查询效果不佳,而当我的查询时,我认为我已经解决了您的问题。所以现在我吃乌鸦。我会发布一些我失去的途径。但是我告诉你,你发布它的方式你的查询原来运行得很好。我只能把你的MySQL震撼到硬盘或其他东西。对不起,我无法提供更多帮助。

以前的回应(这也是一次更新)

我崩溃了,并在我自己的数据库中重新创建了你的问题。在useriditemid上尝试独立索引后,我无法在几秒钟内获得查询,因此我按照查询的指示设置了非常具体的多列密钥。关于tbladdeditem的注意事项多列查询以itemid开头,tblprivateitem列反转:

以下是我使用的架构:

CREATE TABLE `tbladdeditem` (
  `addeditemid` int(11) NOT NULL AUTO_INCREMENT,
  `itemid` int(11) NOT NULL,
  `userid` mediumint(9) NOT NULL,
  PRIMARY KEY (`addeditemid`),
  KEY `userid` (`userid`),
  KEY `i_and_u` (`itemid`,`userid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `tblprivateitem` (
  `privateitemid` int(11) NOT NULL AUTO_INCREMENT,
  `userid` mediumint(9) NOT NULL DEFAULT '1',
  `itemid` int(10) NOT NULL,
  `iitemid` mediumint(9) NOT NULL,
  PRIMARY KEY (`privateitemid`),
  KEY `userid` (`userid`),
  KEY `itemid` (`itemid`),
  KEY `u_and_i` (`userid`,`itemid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

我在每张桌子上填充了200万条随机数据。我做了一些假设:

  • userid从1到2000不等
  • itemid在1到10000之间

这为每个用户提供了每个表中大约一千个条目。

以下是查询的两个版本(我正在为我的编辑器使用工作台):

版本1 - 对联接进行所有过滤。

结果:0.016秒返回1297行

delimiter $$
set @userid = 3
$$
SELECT 
    a.addeditemid,
    p.iitemid,
    p.itemid
FROM tblprivateitem as p
INNER JOIN tbladdeditem as a
    ON (p.userid in (1, @userid))
        AND p.itemid = a.itemid 
        AND a.userid = @userid
$$

以下是解释:

EXPLAIN: 
id select_type table type  key     ref  rows extra
1  SIMPLE      p     range u_and_i      2150 Using where; Using index
1  SIMPLE      a     ref   i_and_u      1    Using where; Using index

版本2 - 预先过滤

结果:返回1297行的时间为0.015秒

delimiter $$
set @userid = 3
$$
SELECT 
    a.addeditemid,
    p.iitemid,
    p.itemid
from 
  (select userid, itemid, iitemid from tblprivateitem 
      where userid in (1, @userid)) as p
  join tbladdeditem as a on p.userid = a.userid and a.itemid = p.itemid;
where a.userid = @userid
$$

以下是解释:

id select_type table      type  key     ref               rows extra
1  PRIMARY     <derived2> ALL   null    null              2152
1  PRIMARY     a          ref   i_and_u p.itemid,const    1    Using where; Using index
2  DERIVED     p1         range u_and_i                   2150 Using where