优化SQL,用于从两个连接表中获取数据(user-from-id的用户名和来自两个表的user-to-id msgs)

时间:2015-03-08 15:54:24

标签: mysql join query-optimization

我的表“msgs”包含用户(他们的ID)之间的消息:

+--------+-------------+------------+---------+---------+
| msg_id |user_from_id | user_to_id | message | room_id |
+--------+-------------+------------+---------+---------+
| 1      |           1 |          4 |Hello!   |       2 |
| 2      |           1 |          5 |Hi there |       1 |
| 3      |           2 |          1 |CU soon  |       2 |
| 4      |           3 |          7 |nice...  |       1 |
+--------+-------------+------------+---------+---------+

我还有两个用户名表 表:user1

+--------+----------+
|user_id |user_name |
+--------+----------+
| 5      | Ann      |
| 6      | Sam      |
| 7      | Michael  |
+--------+----------+

表:user2

+--------+----------+
|user_id |user_name |
+--------+----------+
| 1      | John     |
| 2      | Alice    |
| 3      | Tom      |
| 4      | Jane     |
+--------+----------+

我需要在每一行中获取两个用户ID的用户名。每个user-id都可以在带有用户名的第一个或第二个表中。

我写了这个SQL查询:

SELECT DISTINCT
  m.msg_id,
  m.user_from_id,
  CASE WHEN c1.user_name IS NULL THEN c3.user_name ELSE c1.user_name END AS from_name,
  m.user_to_id,
  CASE WHEN c2.user_name IS NULL THEN c4.user_name ELSE c2.user_name END AS to_name,
  m.message
FROM msgs m
LEFT JOIN users1 c1 ON c1.user_id=m.user_from_id
LEFT JOIN users1 c2 ON c2.user_id=m.user_to_id
LEFT JOIN users2 c3 ON c3.user_id=m.user_from_id
LEFT JOIN users2 c4 ON c4.user_id=m.user_to_id
WHERE m.room_id=1
LIMIT 0, 8

有效。 执行查询以获取没有用户名(没有任何连接)的原始数据大约需要0.1秒。但仅加入一个用户名表(仅限user1或user2)就足以在约6.2秒内获取此数据。 (加入一个表)。我在这个表中有很多行:msgs中35K行,user1中0.5K,user2中25K。 用连接两个表(包含所有这些数据)执行查询是不可能的。

如何优化此查询?我只需要在第一个“msgs”表中使用user_ids的用户名。

1 个答案:

答案 0 :(得分:1)

有和没有连接的查询之间可能存在许多差异。我将假设id具有适当的索引 - 主键自动执行。如果没有,那就检查一下。

显而易见的解决方案是将原始查询用作子查询:

SELECT m.msg_id, m.user_from_id,
       (CASE WHEN c1.user_name IS NULL THEN c3.user_name ELSE c1.user_name
        END) AS from_name,
       m.user_to_id,
       (CASE WHEN c2.user_name IS NULL THEN c4.user_name ELSE c2.user_name
        END) AS to_name,
       m.message
FROM (SELECT m.*
      FROM msgs m
      WHERE m.room_id = 1
      LIMIT 0, 8
     ) m LEFT JOIN
     users1 c1
     ON c1.user_id = m.user_from_id LEFT JOIN
     users1 c2
     ON c2.user_id = m.user_to_id LEFT JOIN
     users2 c3
     ON c3.user_id = m.user_from_id LEFT JOIN
     users2 c4
     ON c4.user_id = m.user_to_id;

对于大多数数据结构,distinct也是不必要的。

这也使(合理的假设)user_id在用户表中是唯一的。

此外,强烈建议不要LIMIT使用ORDER BY。您获得的特定行是不确定的,可能会从一次执行更改为下一次执行。