[英]Help me optimize this MySql query
I have a MySql query that take a very long time to run (about 7 seconds). 我有一个MySql查询,需要很长时间才能运行(大约7秒)。 The problem seems to be with the OR in this part of the query: "(tblprivateitem.userid=?userid OR tblprivateitem.userid=1)".
问题似乎出在查询的此部分中的OR:“((tblprivateitem.userid =?userid OR tblprivateitem.userid = 1)”。 If I skip the "OR tblprivateitem.userid=1" part it takes only 0.01 seconds.
如果跳过“ OR tblprivateitem.userid = 1”部分,则只需0.01秒。 As I need that part I need to find a way to optimize this query.
由于需要这一部分,因此我需要找到一种优化此查询的方法。 Any ideas?
有任何想法吗?
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
EXPLAIN: 说明:
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 contains 1 100 000 rows: 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 contains 2 700 000 rows: 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;
I would try this instead, on your original JOIN
you have an OR
associated with a parameter, move that to your WHERE
clause. 我会尝试这样做,在您原始的
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)
Since you have the predicate condition tbladdeditem.userid=?userid
in the where clause I don't think you need it in the join condition.. Try removing it from the join condition and (If you are using the Or to handle the case where the parameter is null, then use Coalesce
instead of OR
) if not leave it as an Or 由于您在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)
Second, if there is not an index on the userId column in these two tables, consider adding one. 其次,如果在这两个表的userId列上没有索引,请考虑添加一个。
Finally, if these all fail, try converting to two separate queries and unioning them together: 最后,如果所有这些均失败,请尝试转换为两个单独的查询并将它们合并在一起:
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 UPDATE
I made my queries and schema match your original question exactly, multi-column key and all. 我让我的查询和模式完全匹配您的原始问题,多列键以及所有其他内容。 The only possible difference is that I populated each table with two million entries.
唯一可能的区别是,我在每个表中填充了200万个条目。 My query (your query) runs in 0.15 seconds.
我的查询(您的查询)在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
I have the same explain that you do, and with my data, my query return over a thousand matches without any issue at all. 我的解释与您所做的相同,根据我的数据,我的查询返回了1000多个匹配项,而没有任何问题。 Being completely at a loss, as you really shouldn't be having these issues -- is it possible you are running a very limiting version of MySQL?
完全不知所措,因为您实际上不应该遇到这些问题-运行的MySQL版本非常有限吗? Are you running 64-bit?
您正在运行64位吗? Plenty of memory?
有足够的记忆力吗?
I had made the assumption that your query wasn't performing well, and when mine was, assumed I had fixed you problem. 我已经假设您的查询效果不佳,而当我的查询成功时,我已经解决了您的问题。 So now I eat crow.
所以现在我吃乌鸦了。 I'll post some of the avenues I went down.
我会发布一些我走的路。 But I'm telling you, your query the way you posted it originally works just fine.
但我告诉您,您查询的发布方式本来就很好。 I can only imagine your MySQL thrashing to the hard drive or something.
我只能想象您的MySQL撞击硬盘或其他东西。 Sorry I couldn't be more help.
对不起,我无法提供更多帮助。
PREVIOUS RESPONSE (Which is also an update) 上一个响应(这也是更新)
I broke down and recreated your problem in my own database. 我崩溃了,并在自己的数据库中重新创建了您的问题。 After trying independent indexes on
userid
and on itemid
I was unable to get the query below a few seconds, so I set up very specific multi-column keys as directed by the query. 在尝试对
userid
和itemid
独立索引后,我无法在几秒钟内得到查询,因此我按照查询的指示设置了非常具体的多列键。 Notice on tbladdeditem
the multi-column query begins with itemid
while on the tblprivateitem
the columns are reversed: 请注意,在
tbladdeditem
,多列查询以itemid
开头,而在tblprivateitem
,列则相反:
Here is the schema I used: 这是我使用的架构:
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;
I filled each table with 2 million entries of random data. 我在每个表中填充了200万个随机数据条目。 I made some assumptions:
我做了一些假设:
This gives each user about a thousand entries in each table. 这使每个用户在每个表中都有大约一千个条目。
Here are two versions of the query (I'm using workbench for my editor): 这是查询的两个版本(我在编辑器中使用工作台):
Version 1 - do all the filtering on the join. 版本1-对联接执行所有过滤。
Result: 0.016 seconds to return 1297 rows 结果: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
$$
Here's the explain: 这是解释:
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
Version 2 - filter up front 版本2-预先过滤
Result: 0.015 seconds to return 1297 rows 结果: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
$$
Here's the explain: 这是解释:
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.