mysql - 使用mysql查询大表

标签 mysql

我管理一个房地产网站。我有一个包含禁止用户的表(小表)和一个名为 advert_views 的表,该表跟踪每个用户查看的每个列表(目前有 130 万行,并且还在不断增长)。 advert_views 表还记录了查看的每个广告的 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

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

谢谢! 克里斯

最佳答案

首先:为什么要对表进行外部连接?或者更好:为什么您尝试外部连接表?左连接意味着即使没有匹配项也可以从表中获取数据。但是,您的结果可能包含所有值为空的行。 (但这不会发生,因为 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)。我不知道你的 table 。每次重用 user_id 是否会获得很多记录?如果没有,请删除该子句。

对于索引我建议:

  • banned_users(rec_status、电子邮件地址)
  • 用户(电子邮件地址、用户 ID)
  • 广告浏览量(user_id、client_ip)
  • 广告浏览(client_ip、user_id)

关于mysql - 使用mysql查询大表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39677217/

相关文章:

php - PDO语法错误

mysql - 在 MySQL 中存储 HTML : blob or text?

MySQL 两个非空结果之间的 OR 运算符

mysql - 如何找到开始和结束几乎与时间匹配的行列表?

sql - MySQL 和 UTF-8

mysql - MySQL更新中的顺序排名

mysql - 减去 MYSQL 时间戳固定的小时数,其中 ID 是某事

java - 如何使用 Spring、Hibernate、MySQL ISAM 进行事务处理?

php - 基于 MySQL 列数据类型的动态 PHP 代码创建

mysql - 为什么我们不在源文件名.sql 之后使用分号?