优化长mysql查询

时间:2013-02-26 09:20:14

标签: mysql sql

我查询了一个我很久以前建立的聊天网站,由于流量很大,我糟糕的查询设计已经赶上了我。这里有一个来自我的长查询日志的例子:

SELECT  DISTINCT user.id 
FROM    user 
        STRAIGHT_JOIN user_pics 
            ON user.id=user_pics.uid      
        STRAIGHT_JOIN user_account 
            ON user_account.user_id=user.id 
WHERE   registered = 1  AND 
        user.id<>0 AND 
        user.id<>23847 AND 
        user.id<>12392... (IT HAS LITERALLY 1000 OF THESE)
        AND user_pics.main=1 AND 
        user_pics.approved=1 AND 
        user_pics.deleted<>1 AND 
        gender LIKE '%female%' AND 
        country LIKE '%United Kingdom%' AND 
        city LIKE '%birmingham%' AND 
        sexorientation     LIKE '%Straight%' 
ORDER   BY updatedate DESC 
LIMIT   20;

查询大约需要15秒才能执行,我也已经索引了所有引用列。将查找到临时表替换1000“AND user.id&lt;&gt; 0”标记会改进查询。我想我会先去询问并做出改变。如果您可以通过代码推荐任何有用的更改,我将非常感激。

编辑:“user.id&lt;&gt; 23847”标记是在php中通过简单的select创建的,然后是foreach数组循环,将它们添加到更大的sql查询中。

编辑2:感谢您的帮助,通过使用“不在”,他们的查询从13秒减少到0.3秒。

6 个答案:

答案 0 :(得分:5)

尝试使用EXPLAIN
http://dev.mysql.com/doc/refman/5.0/en/explain.html
你会发现什么可以更好。

这可以替换为user.id NOT IN(23847 ,0 , 23847 ,...) 使用NOT IN()

答案 1 :(得分:1)

是的,如果您要使用,

user.id NOT IN (SELECT id FROM idExemptTable)

这比单独检查每个id

要快得多

我已经包含了另一个答案的链接,该答案进一步详细介绍了IN声明: -

SQL: SELECT IN faster and best practice?

我也不确定你为什么要使用通配符匹配和LIKE来检查国家名称。

答案 2 :(得分:1)

不使用慢速not-equals-to,而是使用技巧:选择那些不被选中的技巧,用LEFT JOIN将其连接回原始表,并通过过滤获得其余的:< / p>

SELECT DISTINCT user.id 
FROM user 
STRAIGHT_JOIN user_pics ON user.id=user_pics.uid
STRAIGHT_JOIN user_account ON user_account.user_id=user.id 
LEFT OUTER JOIN 
 (SELECT u.id from user u where u.id in (0,23847, 12397 ... ... ...)) as notToBeIncluded ON user.id=notToBeIncluded.id -- the users that are to be excluded
WHERE registered=1  
AND notToBeIncluded.id IS NULL --this is the important part.
AND user_pics.main=1 
AND user_pics.approved=1 
AND user_pics.deleted<>1 
AND gender LIKE '%female%' 
AND country LIKE '%United Kingdom%' 
AND city LIKE '%birmingham%' 
AND sexorientation     LIKE '%Straight%' 
ORDER BY updatedate DESC LIMIT 20;

编辑我是多么愚蠢......你甚至提到过,你所拥有的不需要的ID是另一个查询的结果!在这种情况下,不会从PHP中获得结果,直接在查询中使用它!这将使它更快。

所以:

  • 删除其他查询以及foreach处理其结果
  • 重写主要查询

(这里需要一个字符串用于标记以正确格式化)

SELECT DISTINCT user.id 
FROM user 
STRAIGHT_JOIN user_pics ON user.id=user_pics.uid
STRAIGHT_JOIN user_account ON user_account.user_id=user.id 
LEFT OUTER JOIN 
 (SELECT u.id from user <[ your other query here ]> ) as notToBeIncluded ON user.id=notToBeIncluded.id -- the users that are to be excluded
WHERE registered=1  
AND notToBeIncluded.id IS NULL --this is the important part.
--( conditions removed for brewity)
ORDER BY updatedate DESC LIMIT 20;

其他建议:

  • 对性别和方向等列使用varchar无济于事。使用数值,这有助于一些。
  • 另外,请检查您的索引。有人建议使用执行计划:这是尝试找出特定查询的性能瓶颈的最终方式。

答案 3 :(得分:0)

向users表添加一个额外字段并为其编制索引。在

中为每个用户设置值为1
user.id<>0 AND 
user.id<>23847 AND 
user.id<>12392...

,每个其他用户都为0。

然后在上面的查询中按此字段进行过滤。

答案 4 :(得分:0)

ppeterkas解决方案的微小变化,假设使用临时表与其中不需要的用户。同样将LIKE更改为直线等于(甚至更好地使用标志,甚至是位串,具体取决于字段是否存储值的组合)。

SELECT  DISTINCT user.id 
FROM    user 
        STRAIGHT_JOIN user_pics ON user.id=user_pics.uid      
        STRAIGHT_JOIN user_account ON user_account.user_id=user.id 
        LEFT OUTER JOIN tmp_users_to_ignore ON user.id = tmp_users_to_ignore.id
WHERE   registered = 1   
AND     tmp_users_to_ignore.id IS NULL 
AND     user_pics.main=1  
AND     user_pics.approved=1  
AND     user_pics.deleted<>1  
AND     gender = 'female'  
AND     country = 'United Kingdom'  
AND     city = 'birmingham'  
AND     sexorientation     = 'Straight' 
ORDER   BY updatedate DESC 
LIMIT   20;

答案 5 :(得分:0)

我明白了:

  

编辑:“user.id&lt;&gt; 23847”标记是在php中通过简单的select创建的,然后是foreach数组循环,将它们添加到更大的sql查询中。

那么为什么不创建子查询?

我们假设您的第一个查询是SELECT * FROM user WHERE yourcondition。 您需要所有数据返回吗?如果不是,请在第二个查询中执行此操作:

SELECT DISTINCT user.id 
FROM user 
STRAIGHT_JOIN user_pics ON user.id=user_pics.uid
STRAIGHT_JOIN user_account ON user_account.user_id=user.id 
WHERE registered=1  
AND user.id NOT IN(SELECT id FROM user WHERE yourcondition)
AND user_pics.main=1 
AND user_pics.approved=1 
AND user_pics.deleted<>1 
AND gender ='female' 
AND country LIKE '%United Kingdom%' 

AND CITY LIKE'%birmingham%' AND sexorientation LIKE'%Straight%' ORDER BY更新了DESC LIMIT 20;