使用 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!


原帖:

我正在为游戏使用数据库。有两个相同的表equipmentsafety_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 子句的主要内容,如果未能解决你的问题,请参考以下文章

sql中级联删除,级联更新是怎么理解的?

如何在mongodb中级联删除文档?

在 Weka 中级联分类器的错误方法

在 MFC MDI 应用程序中级联特定窗口

首先在实体框架数据库中级联删除

SQLite“在删除级联时”不在Qt中级联