使用mysql查询大表

时间:2016-09-24 14:05:37

标签: mysql

我管理一个物业网站。我有一个禁止用户的表(小表)和一个名为advert_views的表,它跟踪每个用户查看的每个列表(当前1.3米行和增长)。 advert_views表alsio记录了所查看的每个广告的IP地址。

我想获取被禁用户使用的IP地址,并检查这些被禁用户是否已开设新帐户。我运行了以下查询:

SELECT adviews.user_id AS 'banned user_id', 
       adviews.client_ip AS 'IPs used by banned users', 
       adviews2.user_id AS 'banned users that opened a new account'
FROM banned_users
LEFT JOIN users on users.email_address = banned_users.email_address  #since I don't store the user_id in banned_users
LEFT JOIN advert_views adviews ON adviews.user_id = users.id AND adviews.user_id IS NOT NULL # users may view listings when not logged in but they have restricted access to the information on the listing
LEFT JOIN (SELECT client_ip,
                  user_id 
                  FROM advert_views 
                  WHERE user_id IS NOT NULL   
                ) adviews2 
                ON adviews2.client_ip = adviews.client_ip
WHERE banned_users.rec_status = 1 and adviews.user_id <> adviews2.user_id
GROUP BY adviews2.user_id

我在advert_views表和users表上应用了索引,如下所示:

enter image description here

我的查询需要半个小时才能执行。有没有办法提高我的查询速度?

谢谢! 克里斯

1 个答案:

答案 0 :(得分:0)

首先:你为什么要加入表?或者更好:为什么尝试外连接表?左连接意味着即使没有匹配也能从表中获取数据。但是,您的结果可能包含所有值为null的行。 (但这不会发生,因为where子句中的adviews.user_id <> adviews2.user_id会解散所有外部连接的行。)不要让DBMS做更多的工作而不是必要的。如果你想要内连接,那么不要外连接。 (虽然执行时间的差异不会很大。)

下一步:您从banned_users中选择,但您只能用它来检查是否存在。你不应该这样做。请改用EXISTSIN子句。 (这主要是为了提高可读性,以免产生重复的结果。这可能不会加快速度。)

SELECT av1.user_id AS 'banned user_id', 
       av2.client_ip AS 'IPs used by banned users', 
       av2.user_id AS 'banned users that opened a new account'
FROM adviews av1
JOIN adviews av2 ON av2.client_ip = av1.client_ip AND av2.user_id <> av1.user_id
WHERE av1.user_id IN 
(
  SELECT user_id 
  FROM users 
  WHERE email_address IN (select email_address from banned_users where rec_status = 1)
)
GROUP BY av2.user_id;

您可以使用连接替换内部IN子句。这主要是个人偏好的问题,但是在过去,MySQL有时在IN条款上表现不佳,所以很多人都习惯加入。

WHERE av1.user_id IN 
(
  SELECT u.user_id 
  FROM users u
  JOIN banned_users bu ON bu.email_address = u.email_address
  WHERE bu.rec_status = 1
)

最后考虑删除GROUP BY子句。每次重用user_id时,它会将结果减少到一行,显示其中一个相关的禁用user_id(如果有多个,则任意选择)。我不知道你的桌子。每次重用user_id会获得多条记录吗?如果没有,请删除该条款。

至于我建议的指数:

  • banned_users(rec_status,email_address)
  • 用户(email_address,user_id)
  • adviews(user_id,client_ip)
  • adviews(client_ip,user_id)