【问题标题】:Get data from mysql, taking almost 436 seconds从mysql获取数据,耗时近436秒
【发布时间】:2019-03-28 14:25:59
【问题描述】:

我是 android 开发人员,在 mysql 方面没有太多经验。我在 mysql 数据库中有 3 个表。

第一个表:-UserTable

+-------------+-------------+----------+----------------------+
| user_id     | name      |   image    |   joining_date       |
+-------------+-------------+----------+----------------------+
|         100 | King      | defualt.jpg | 2018-05-23 20:09:27 |
|         101 | Kochhar   | defualt.jpg | 2018-05-23 20:09:27 |
| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |

第二张表:- friendRequests

+-------------+-------------+----------+
| unique_id   | from_id     |   to_id  |
+-------------+-------------+----------+
|         1  | 100          | 101      |
|         2  | 200          | 110      |
| - - - - - - - - - - - - - - - - - - -|
| - - - - - - - - - - - - - - - - - - -|

第三张桌子:- 朋友

+-------------+-------------+----------+
| unique_id   | from_id     |   to_id  |
+-------------+-------------+----------+
|         1  | 104          | 101      |
|         2  | 206          | 110      |
| - - - - - - - - - - - - - - - - - - -|
| - - - - - - - - - - - - - - - - - - -|

我想获取不是我朋友的用户列表,而且我们还没有待处理的朋友请求。所以我尝试了以下查询:-

SELECT usertable.name,
       usertable.user_id,
       usertable.image
FROM   `UserTable` AS usertable
WHERE  usertable.user_id <> '100'
       AND (SELECT Count(*)
            FROM   friendrequests
            WHERE  ( to_id = '100'
                     AND from_id = usertable.user_id )
                    OR ( to_id = usertable.user_id
                         AND from_id = '100' )) = 0
       AND (SELECT Count(*)
            FROM   friends
            WHERE  to_id = usertable.user_id
                   AND from_id = '100'
                    OR to_id = '100'
                       AND from_id = usertable.user_id) = 0
ORDER  BY usertable.joining_date DESC
LIMIT  10  

它正在运行,但需要 436 秒。编写此查询的正确方法是什么?

【问题讨论】:

  • user 是一个关键字。它不是表名的好候选。你真的应该把你的桌子命名为别的东西。
  • 谢谢@MadhurBhaiya。我已经更新了我的问题。
  • 避免在你的选择中使用 select 并尝试使用一些 join 可能吗?
  • 你能给我们一个 SQL fiddle 用你的数据来测试一些请求吗?
  • 闻起来就像你没有任何索引。请提供SHOW CREATE TABLE。

标签: mysql performance query-optimization


【解决方案1】:

这里有一些其他的方法:

1) 使用NOT IN

您在 usertable.user_id 上添加了一个不应出现在 id 列表中的条件:这些 id 是表 friendrequests 和 friends 的所有 id 具有 to_id 或 from_id = 100。

SELECT 
  usertable.name,
  usertable.user_id,
  usertable.image
FROM `UserTable` AS usertable
WHERE usertable.user_id <> '100'
  AND usertable.user_id NOT IN 
    (SELECT from_id as user_id FROM friendrequests WHERE to_id = '100'
     UNION
     SELECT to_id as user_id FROM friendrequests WHERE from_id = '100'
     UNION
     SELECT from_id as user_id FROM friends WHERE to_id = '100'
     UNION
     SELECT to_id as user_id FROM friends WHERE from_id = '100')               
ORDER  BY usertable.joining_date DESC
LIMIT  10  

EDIT 根据 Rick James 的评论,同样没有联合:

SELECT 
  usertable.name,
  usertable.user_id,
  usertable.image
FROM `UserTable` AS usertable
WHERE usertable.user_id <> '100'
  AND usertable.user_id NOT IN (SELECT from_id as user_id FROM friendrequests WHERE to_id = '100')
  AND usertable.user_id NOT IN (SELECT to_id as user_id FROM friendrequests WHERE from_id = '100')
  AND usertable.user_id NOT IN (SELECT from_id as user_id FROM friends WHERE to_id = '100')
  AND usertable.user_id NOT IN (SELECT to_id as user_id FROM friends WHERE from_id = '100')               
ORDER  BY usertable.joining_date DESC
LIMIT  10  

2) 使用LEFT JOIN

我看到 this answer 并尝试使用相同的逻辑:您使用另一个表创建一些 LEFT JOIN 并添加一个条件,其中这些连接返回 NULL 值

SELECT 
  usertable.name,
  usertable.user_id,
  usertable.image
FROM `UserTable` AS usertable
LEFT JOIN friendrequests as FRa on FRa.from_id = usertable_user_id
LEFT JOIN friendrequests as FRb on FRb.to_id   = usertable_user_id
LEFT JOIN friends        as Fa  on Fa.from_id  = usertable_user_id
LEFT JOIN friends        as Fb  on Fb.to_id    = usertable_user_id
WHERE usertable.user_id <> '100' 
  AND FRa.from_id IS NULL
  AND FRb.to_id IS NULL
  AND Fa.from_id IS NULL
  AND Fb.to_id IS NULL

3) 使用NOT EXISTS

SELECT 
  usertable.name,
  usertable.user_id,
  usertable.image
FROM `UserTable` AS usertable
WHERE usertable.user_id <> '100'
  AND NOT EXISTS (SELECT from_id as user_id FROM friendrequests WHERE to_id = '100')
  AND NOT EXISTS (SELECT to_id as user_id FROM friendrequests WHERE from_id = '100')
  AND NOT EXISTS (SELECT from_id as user_id FROM friends WHERE to_id = '100')
  AND NOT EXISTS (SELECT to_id as user_id FROM friends WHERE from_id = '100')               
ORDER  BY usertable.joining_date DESC
LIMIT  10  

抱歉,没有示例数据或 SQL Fiddle 来测试这些查询,我无法告诉您它是否返回您想要的内容,或者它是否真的更快...


编辑:这个页面是从 2009 年开始的,所以结果已经改变了!我不会删除它,因为您仍然拥有我之前使用的查询结构:

我建议您查看此页面以了解这些查询之间的区别,并为您提供一些想法来实现您想要的:https://explainextended.com/2009/09/18/not-in-vs-not-exists-vs-left-join-is-null-mysql/

本页提出了第三种方法(使用NOT EXISTS),但根据测试它效率不高:

然而,这个查询的效率比前两个略低:它需要 0.92 秒。

这并没有造成太大的性能下降,但是查询需要多花 27% 的时间。

【讨论】:

  • 自 2009 年以来,优化器已经投入了大量精力,尤其是在此处讨论的领域。不要相信那个链接。
  • 尽量避免UNION;它有一些开销并避免了一些优化路径。尝试使用 fpir AND .. NOT IN ( SELECT ... ) 子句的 NOT IN 解决方案。用NOT EXISTS 做同样的事情:四个AND NOT EXISTS ( SELECT 1 FROM ... ) 子句。
  • 谢谢。也许 gourav 正在运行旧版本?你有时间——你是在替他说话吗?还是您进行了独立测试?
  • 我只是想给他一些其他方法来实现他想要的,我不知道他的数据库(索引或其他东西)看起来如何很难进行测试
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-11-08
  • 2019-10-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多