使用 UNION 在 VIEW 中级联 WHERE 子句
Posted
技术标签:
【中文标题】使用 UNION 在 VIEW 中级联 WHERE 子句【英文标题】:Cascading WHERE clause inside a VIEW with a UNION 【发布时间】:2010-11-08 22:04:19 【问题描述】:这还没有解决,但我发现了原因:mysql View containing UNION does not optimize well...In other words SLOW!
原帖:
我正在为游戏使用数据库。有两个相同的表equipment
和safety_dep_box
。要检查玩家是否拥有一件装备,我想检查两张表。
我不想做两个查询,而是想利用 MySQL 中的 UNION 功能。我最近了解到我可以创建一个视图。这是我的看法:
CREATE VIEW vAllEquip AS SELECT * FROM equipment UNION SELECT * FROM safety_dep_box;
视图创建得很好。但是当我运行时
SELECT * FROM vAllEquip WHERE owner=<id>
查询需要很长时间,而独立的选择查询很快。我想我知道为什么,但我不知道如何解决它。
谢谢!
附:附加信息:
这两个表在结构上是相同的,但是因为它们是多 1 亿行表而被拆分。 该结构包括 int id 上的主键,int owner 上的多个索引。 我不明白的是以下之间的速度差异:
SELECT COUNT(*) FROM (SELECT * FROM equipment WHERE owner=1 UNION ALL SELECT * FROM safety_dep_box WHERE owner=1) AS uES;
0.42 秒
SELECT COUNT(*) FROM (SELECT * FROM equipment WHERE owner=1 UNION SELECT * FROM safety_dep_box WHERE owner=1) AS uES;
0.37 秒
SELECT COUNT(*) FROM vAllEquip WHERE owner=1;
60 秒后中止
版本:5.1.51
mysql> explain SELECT * FROM equipment UNION SELECT * FROM safety_dep_box;
+----+--------------+----------------+------+---------------+------+---------+------+---------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+--------------+----------------+------+---------------+------+---------+------+---------+-------+
| 1 | PRIMARY | equipment | ALL | NULL | NULL | NULL | NULL | 1499148 | |
| 2 | UNION | safety_dep_box | ALL | NULL | NULL | NULL | NULL | 867321 | |
| NULL | UNION RESULT | <union1,2> | ALL | NULL | NULL | NULL | NULL | NULL | |
+----+--------------+----------------+------+---------------+------+---------+------+---------+-------+
带有 WHERE 子句
mysql> explain SELECT * FROM equipment WHERE owner=1 UNION ALL SELECT * FROM safety_dep_box WHERE owner=1
-> ;
+----+--------------+----------------+------+-----------------------+-------+---------+-------+------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+--------------+----------------+------+-----------------------+-------+---------+-------+------+-------+
| 1 | PRIMARY | equipment | ref | owner,owner_2,owner_3 | owner | 4 | const | 1 | |
| 2 | UNION | safety_dep_box | ref | owner,owner_3 | owner | 4 | const | 1 | |
| NULL | UNION RESULT | <union1,2> | ALL | NULL | NULL | NULL | NULL | NULL | |
+----+--------------+----------------+------+-----------------------+-------+---------+-------+------+-------+
【问题讨论】:
您使用的是哪个版本的 MySQL?你能解释一下 SELECT * FROM 设备 UNION SELECT * FROM safety_dep_box; ? 我已经在我原来的帖子中解决了你的 2 个问题。 您需要解释with WHERE 子句。否则执行计划非常明显(从一个表中获取所有行,然后从另一个表中获取)。使用 WHERE 子句,EXPLAIN 将(可能)告诉您它从第一个表中获取少量行,然后在第二个表上进行某种表扫描(可能是因为缺少 UNION ALL,如下所述)。跨度> 我在 WHERE 中添加了 EXPLAIN。这是问题的核心——当我将 WHERE 子句添加到视图中的选择时——它不会传播到内部 SELECT 语句。据我所知,除了不使用视图之外,没有其他解决方案。 【参考方案1】:首先,您可能应该使用 UNION ALL 而不是普通的 UNION。使用普通的 UNION,引擎将尝试对您的结果集进行重复数据删除。这可能是您的问题的根源。
其次,您需要两个表中的所有者索引,而不仅仅是一个。而且,理想情况下,它们将是整数列。
第三,Randolph 是正确的,您不应该在 SELECT 语句中使用“*”。列出您想要包含的所有列。这在 UNION 中尤其重要,因为列必须完全匹配,如果两个表中的列顺序存在分歧,您可能会强制进行一些类型转换,这会花费您一些时间。
最后,“有两个相同的表”这句话几乎总是暗示您的数据库没有经过优化设计。这些可能应该是一个表。为了表明物品的所有权,您的 safety_dep_box 表应该只包含物品的 ownerID 和 itemID(用于关联设备和玩家),并且可能还有一个额外的自动编号整数键列。
【讨论】:
我已在原始帖子中添加了其他信息,以解决您提出的观点。我还创建了一个仅选择 id、owner、name 的视图,并且查询该视图也在 60 秒后中止 “SELECT COUNT(*) FROM (SELECT * FROM devices WHERE owner=1 UNION SELECT * FROM safety_dep_box WHERE owner=26990) AS uES;”的时间是多少?除非您输入错误,否则您不会针对桌面设备测试 SELECT 吗?所有者 ID = 1。 是的,错字:(。我替换了所有者 ID 以消除混乱,但我创建了一些 此答案中的union all
评论是解决您的速度问题的重要部分。正如他所说,union
包括一个隐式的重复数据删除阶段,这需要排序和比较。有 100+ 百万行,这将非常非常慢。
但我尝试了 UNION ALL 并没有帮助>_
【参考方案2】:
首先,永远不要在视图中使用 SELECT *。这是懒惰的代码。其次,在不知道基表是什么样子的情况下,我们更不可能为您提供帮助。
它需要永远的原因是因为它必须构建完整的结果然后过滤它。您需要在 owner
字段上建立索引,无论它们是什么。
【讨论】:
SELECT * 是因为我希望我的视图具有与单独选择相同的功能。我已经在所有者列上有索引,尽管它没有帮助。基表的列太多,无法在此处列出,但我不明白这有什么关系。 很抱歉,在视图中使用 SELECT * 没有任何借口。也许您需要查看索引视图? 有没有办法在 MySQL 中索引我不知道的视图?以上是关于使用 UNION 在 VIEW 中级联 WHERE 子句的主要内容,如果未能解决你的问题,请参考以下文章