繁体   English   中英

帮我优化这个MySql查询

[英]Help me optimize this MySql query

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

查询:

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

表:

tbladdeditem包含1100万行:

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包含2700 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;

我会尝试这样做,在您原始的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)

由于您在where子句中有谓词条件tbladdeditem.userid=?userid ,因此我不认为您在tbladdeditem.userid=?userid条件中需要它。请尝试从tbladdeditem.userid=?userid条件tbladdeditem.userid=?userid其删除,(如果您使用Or来处理where参数为null,如果不将其保留为Or,则使用Coalesce代替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

UPDATE

我让我的查询和模式完全匹配您的原始问题,多列键以及所有其他内容。 唯一可能的区别是,我在每个表中填充了200万个条目。 我的查询(您的查询)在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

我的解释与您所做的相同,根据我的数据,我的查询返回了1000多个匹配项,而没有任何问题。 完全不知所措,因为您实际上不应该遇到这些问题-运行的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万个随机数据条目。 我做了一些假设:

  • 用户名从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-预先过滤

结果:0.015秒返回1297行

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM