3

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

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<>0" 标记替换为查找临时表会改进查询。我想我会在去改变之前问一下。如果您可以推荐任何有用的代码更改,我将不胜感激。

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

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

4

6 回答 6

5

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

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

于 2013-02-26T09:26:02.427 回答
1

是的,如果你要使用,

user.id NOT IN (SELECT id FROM idExemptTable)

这比单独检查每个 id 快得多

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

SQL:SELECT IN 更快和最佳实践?

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

于 2013-02-26T09:23:05.063 回答
1

不要使用慢速不等于,而是使用一个技巧:选择那些不被选中的,用 将其连接回原始表LEFT JOIN,并通过过滤仅获取其余部分:

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 没有帮助。使用数值,这会有所帮助。
  • 另外,查看您的索引。有人建议使用执行计划:这是试图找出特定查询的性能瓶颈的最终方法。
于 2013-02-26T09:27:10.523 回答
0

在 users 表中添加一个额外的字段并将其编入索引。将每个用户的值设置为 1

user.id<>0 AND 
user.id<>23847 AND 
user.id<>12392...

并且对于每个其他用户为 0。

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

于 2013-02-26T09:24:26.490 回答
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;
于 2013-02-26T09:48:50.980 回答
0

我懂了 :

编辑:“user.id<>23847”标记是通过简单的选择在 php 中创建的,然后是 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 updatedate DESC LIMIT 20;

于 2013-02-26T09:49:31.510 回答