使用不带 UNION 的 SELECT DISTINCT 效率更高吗?有没有办法优化以下查询?

Is using SELECT DISTINCT without UNION more efficient? Is there a way to optimize the following query?

我有 3 个 table。它们每个都包含(除其他外)列 UserIDPeriod.

为了获得与特定用户关联的所有 Periods,我使用这样的联合:

# Original Query
SELECT Period FROM table_1 WHERE UserID = :user
UNION SELECT Period FROM table_2 WHERE UserID = :user
UNION SELECT Period FROM table_3 WHERE UserID = :user
ORDER BY Period ASC;

我得到的结果是我所期望的。由于我没有使用 UNION ALL,所有重复的结果都会被自动过滤掉。

此项目使用的调试器认为查询可能很慢。这是它的查询计划-

id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY table_1 ref UserID UserID 4 const 29 NULL
2 UNION table_2 ref UserID UserID 4 const 5 NULL
3 UNION table_3 ref UserID UserID 4 const 4 NULL
NULL UNION RESULT <union1,2,3> ALL NULL NULL NULL NULL NULL Using temporary

我想也许使用 SELECT DISTINCT 而不是 SELECT 会有所帮助,因为每个 SELECT 结果集在 UNION.

之前会变小
# Updated Query
SELECT DISTINCT Period FROM table_1 WHERE UserID = :user
UNION SELECT DISTINCT Period FROM table_2 WHERE UserID = :user
UNION SELECT DISTINCT Period FROM table_3 WHERE UserID = :user
ORDER BY Period ASC;

然而,这似乎只会使查询计划使用更多的临时 tables。

id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY table_1 ref UserID UserID 4 const 29 Using where; Using temporary
2 UNION table_2 ref UserID UserID 4 const 5 Using where; Using temporary
3 UNION table_3 ref UserID UserID 4 const 4 Using where; Using temporary
NULL UNION RESULT <union1,2,3> ALL NULL NULL NULL NULL NULL Using temporary

我也试过 运行 这个相同的查询在 500 行中重复第 2-3 行(UNIONs)以查看是否存在明显差异。从时间上看,结果非常相似。

分析查询,使用 DISTINCT 似乎使查询返回结果的时间更短。但是,查询现在必须清理所有临时 tables,这最终使两个查询的最终时间非常相似。


我想问所有 MySQL 专家,有没有办法在不改变 table 结构的情况下使这个查询更快或更优化?

也许有一些索引? (注意,Period 是 VARCHAR,table 都是 MyISAM

table 的顺序在查询中重要吗?我故意把最大的table放在第一位。

# Original Query
SELECT Period FROM table_1 WHERE UserID = :user
UNION SELECT Period FROM table_2 WHERE UserID = :user
UNION SELECT Period FROM table_3 WHERE UserID = :user
ORDER BY Period ASC;

处理为:

  1. 执行子查询1(输出A)
  2. 执行子查询2(输出B)
  3. 合并输出 A 和 B(输出 С)
  4. 对组合输出 C 进行排序并删除重复项(输出 D)
  5. 执行子查询3(输出E)
  6. 合并输出 C 和 E(输出 F)
  7. 对组合输出 E 进行排序并删除重复项(输出 G)
  8. Return输出G
# Updated Query
SELECT DISTINCT Period FROM table_1 WHERE UserID = :user
UNION SELECT DISTINCT Period FROM table_2 WHERE UserID = :user
UNION SELECT DISTINCT Period FROM table_3 WHERE UserID = :user
ORDER BY Period ASC;

处理为:

  1. 执行子查询1(输出A)
  2. 对输出 A 排序并删除重复项(输出 B)
  3. 执行子查询2(输出C)
  4. 对输出 C 排序并删除重复项(输出 D)
  5. 合并输出 B 和 D(输出 E)
  6. 对输出 E 排序并删除重复项(输出 F)
  7. 执行子查询3(输出G)
  8. 对输出 G 排序并删除重复项(输出 H)
  9. 合并输出 F 和 H(输出 I)
  10. 对输出 I 排序并删除重复项(输出 J)
  11. Return输出J

# Recommended Query
SELECT DISTINCT Period
FROM ( SELECT Period FROM table_1 WHERE UserID = :user
       UNION ALL
       SELECT Period FROM table_2 WHERE UserID = :user
       UNION ALL
       SELECT Period FROM table_3 WHERE UserID = :user ) AS total
ORDER BY Period ASC;
  1. 执行子查询1(输出A)
  2. 执行子查询2(输出B)
  3. 合并输出 A 和 B(输出 С)
  4. 执行子查询3(输出D)
  5. 合并输出 C 和 D(输出 E)
  6. 对组合输出 E 进行排序并删除重复项(输出 F)
  7. Return输出F

如果你的结果只有几十行,很难注意到它是否“慢”。

此处讨论的大多数权衡都会得出“视情况而定”的结论。

假设每个 table 中的用户有 1000 行,但结果中只有 10 行。一种公式会比另一种更快。

假设 table 之间几乎没有重复。现在另一个公式可能会更快。

既然你想要重复数据删除,那么成为临时table某处。

我建议您以对您来说“最简单”或“最合乎逻辑”的任何方式编写查询。

此外,如果您想要性能,请从 MyISAM 切换到 InnoDB。在几乎所有基准测试中,InnoDB 至少一样快。