使用不带 UNION 的 SELECT DISTINCT 效率更高吗?有没有办法优化以下查询?
Is using SELECT DISTINCT without UNION more efficient? Is there a way to optimize the following query?
我有 3 个 table。它们每个都包含(除其他外)列 UserID
和 Period
.
为了获得与特定用户关联的所有 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 行(UNION
s)以查看是否存在明显差异。从时间上看,结果非常相似。
分析查询,使用 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(输出A)
- 执行子查询2(输出B)
- 合并输出 A 和 B(输出 С)
- 对组合输出 C 进行排序并删除重复项(输出 D)
- 执行子查询3(输出E)
- 合并输出 C 和 E(输出 F)
- 对组合输出 E 进行排序并删除重复项(输出 G)
- 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(输出A)
- 对输出 A 排序并删除重复项(输出 B)
- 执行子查询2(输出C)
- 对输出 C 排序并删除重复项(输出 D)
- 合并输出 B 和 D(输出 E)
- 对输出 E 排序并删除重复项(输出 F)
- 执行子查询3(输出G)
- 对输出 G 排序并删除重复项(输出 H)
- 合并输出 F 和 H(输出 I)
- 对输出 I 排序并删除重复项(输出 J)
- 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(输出A)
- 执行子查询2(输出B)
- 合并输出 A 和 B(输出 С)
- 执行子查询3(输出D)
- 合并输出 C 和 D(输出 E)
- 对组合输出 E 进行排序并删除重复项(输出 F)
- Return输出F
如果你的结果只有几十行,很难注意到它是否“慢”。
此处讨论的大多数权衡都会得出“视情况而定”的结论。
假设每个 table 中的用户有 1000 行,但结果中只有 10 行。一种公式会比另一种更快。
假设 table 之间几乎没有重复。现在另一个公式可能会更快。
既然你想要重复数据删除,那么将成为临时table某处。
我建议您以对您来说“最简单”或“最合乎逻辑”的任何方式编写查询。
此外,如果您想要性能,请从 MyISAM 切换到 InnoDB。在几乎所有基准测试中,InnoDB 至少一样快。
我有 3 个 table。它们每个都包含(除其他外)列 UserID
和 Period
.
为了获得与特定用户关联的所有 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 行(UNION
s)以查看是否存在明显差异。从时间上看,结果非常相似。
分析查询,使用 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(输出A)
- 执行子查询2(输出B)
- 合并输出 A 和 B(输出 С)
- 对组合输出 C 进行排序并删除重复项(输出 D)
- 执行子查询3(输出E)
- 合并输出 C 和 E(输出 F)
- 对组合输出 E 进行排序并删除重复项(输出 G)
- 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(输出A)
- 对输出 A 排序并删除重复项(输出 B)
- 执行子查询2(输出C)
- 对输出 C 排序并删除重复项(输出 D)
- 合并输出 B 和 D(输出 E)
- 对输出 E 排序并删除重复项(输出 F)
- 执行子查询3(输出G)
- 对输出 G 排序并删除重复项(输出 H)
- 合并输出 F 和 H(输出 I)
- 对输出 I 排序并删除重复项(输出 J)
- 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(输出A)
- 执行子查询2(输出B)
- 合并输出 A 和 B(输出 С)
- 执行子查询3(输出D)
- 合并输出 C 和 D(输出 E)
- 对组合输出 E 进行排序并删除重复项(输出 F)
- Return输出F
如果你的结果只有几十行,很难注意到它是否“慢”。
此处讨论的大多数权衡都会得出“视情况而定”的结论。
假设每个 table 中的用户有 1000 行,但结果中只有 10 行。一种公式会比另一种更快。
假设 table 之间几乎没有重复。现在另一个公式可能会更快。
既然你想要重复数据删除,那么将成为临时table某处。
我建议您以对您来说“最简单”或“最合乎逻辑”的任何方式编写查询。
此外,如果您想要性能,请从 MyISAM 切换到 InnoDB。在几乎所有基准测试中,InnoDB 至少一样快。