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

Optimization SQL for getting data from two joined tables (usernames for user-from-id and user-to-id msgs from two tables)

我有 table "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 |
+--------+-------------+------------+---------+---------+

我还有两个 table 用户名。
Table: 用户 1

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

Table: 用户 2

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

我需要为每一行中的两个用户 ID 获取用户名。每个用户 ID 可以在第一或第二 table 与用户名。

我写了这个 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 秒。但是仅加入一个用户名 table(仅限 user1 或 user2)就足以在大约 6.2 秒内获取此数据。 (加入一个 table)。我在这个 tables 中有很多行:msgs 中有 35K 行,user1 中有 0.5K 行,user2 中有 25K 行。 执行连接两个 table 的查询(包含所有这些数据)是不可能的。

如何优化这个查询?我只需要 user_ids 在第一个 "msgs" table.

中的用户名

有连接和没有连接的查询之间可能存在许多差异。我将假设 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 在用户表中是唯一的。

另外,非常不鼓励在没有 ORDER BY 的情况下使用 LIMIT。您获得的特定行是不确定的,并且可能会从一次执行更改为下一次执行。