【问题标题】:MySQL: View with Subquery in the FROM Clause LimitationMySQL:在 FROM 子句限制中使用子查询查看
【发布时间】:2010-09-17 09:22:04
【问题描述】:

在 MySQL 5.0 中,为什么在 FROM 子句中尝试使用子查询创建视图时会出现以下错误?

ERROR 1349 (HY000): View 的 SELECT 在 FROM 子句中包含子查询

如果这是 MySQL 引擎的限制,那他们为什么还没有实现这个功能呢?

另外,有什么好的解决方法可以解决这个限制?

是否有任何变通方法适用于 FROM 子句中的任何子查询,或者是否存在一些不使用 FROM 子句中的子查询就无法表达的查询?


一个示例查询(埋在评论中):

SELECT temp.UserName 
FROM (SELECT u1.name as UserName, COUNT(m1.UserFromId) as SentCount 
      FROM Message m1, User u1 
      WHERE u1.uid = m1.UserFromId 
      Group BY u1.name HAVING SentCount > 3 ) as temp

【问题讨论】:

  • 感谢您的这篇文章。我通常避免在 mysql 中查看视图。处理边缘情况,尝试合并 fk 表 - 将它们硬塞到视图中。现在这个。多么痛苦——所有这些对子查询的限制?准备转向不同的引擎。
  • 这是一个 known design flaw in MySQL,它花了大约 10 才受到关注。它已在 MySQL 5.7.7 中修复,我可以确认从 5.7.24 开始就是这种情况。仅供参考。

标签: mysql sql view mysql-error-1349


【解决方案1】:

我遇到了同样的问题。我想创建一个视图来显示最近一年的信息,从一个包含 2009 年到 2011 年记录的表中。这是原始查询:

SELECT a.* 
FROM a 
JOIN ( 
  SELECT a.alias, MAX(a.year) as max_year 
  FROM a 
  GROUP BY a.alias
) b 
ON a.alias=b.alias and a.year=b.max_year

解决方案大纲:

  1. 为每个子查询创建一个视图
  2. 用这些视图替换子查询

这是解决方案查询:

CREATE VIEW v_max_year AS 
  SELECT alias, MAX(year) as max_year 
  FROM a 
  GROUP BY a.alias;

CREATE VIEW v_latest_info AS 
  SELECT a.* 
  FROM a 
  JOIN v_max_year b 
  ON a.alias=b.alias and a.year=b.max_year;

它在 mysql 5.0.45 上运行良好,没有太大的速度损失(与执行相比 没有任何视图的原始子查询选择)。

【讨论】:

  • 备份失败,因为 mysql 会根据名称恢复视图,因此它会先尝试恢复“v_latest_info”,但会失败,因为“v_max_year”尚不存在...
  • 创建这些多个视图会导致性能问题吗?
【解决方案2】:

你的查询不能写成:

SELECT u1.name as UserName from Message m1, User u1 
  WHERE u1.uid = m1.UserFromID GROUP BY u1.name HAVING count(m1.UserFromId)>3

这也应该有助于解决 MySQL 中子查询的已知速度问题

【讨论】:

  • 谢谢,我没有意识到您可以在 SELECT 中没有聚合函数的情况下执行 GROUP BY。那么他们不允许在 MySQL 的 FROM 子句中使用子查询的原因之一是因为速度问题?
  • 在这种特定情况下,不一定是因为速度。就像现在一样,优化器根本不能很好地处理子查询。如果可能的话,远离他们。这个问题在 6.0 中得到修复,并且已经取得了很大的进展,但那是在 6.0 中,而您正在使用 5.0。
  • 请不要使用隐式语法。它是一种 SQL 反模式,并在 20 年前被更好的语法所取代。
  • @CodeCommander,HLGEM 表示您应该使用 ANSI 连接语法:... FROM Message m1 JOIN User u1 ON u1.uid = m1.UserFromID ...
  • “你评论中的查询不能写成...”这是指什么评论?
【解决方案3】:

这似乎是一个已知问题。

http://dev.mysql.com/doc/refman/5.1/en/unnamed-views.html

http://bugs.mysql.com/bug.php?id=16757

许多 IN 查询可以重写为(左外)连接和某种 IS (NOT) NULL。例如

SELECT * FROM FOO WHERE ID IN (SELECT ID FROM FOO2)

可以改写为

SELECT FOO.* FROM FOO JOIN FOO2 ON FOO.ID=FOO2.ID

SELECT * FROM FOO WHERE ID NOT IN (SELECT ID FROM FOO2)

可以

SELECT FOO.* FROM FOO 
LEFT OUTER JOIN FOO2 
ON FOO.ID=FOO2.ID WHERE FOO.ID IS NULL

【讨论】:

  • 但是你将如何重写 FROM 子句中的查询呢?例如,我该如何重写这个查询?:SELECT temp.UserName FROM ( SELECT u1.name as UserName, COUNT(m1.UserFromId) as SentCount FROM Message m1, User u1 WHERE u1.uid = m1.UserFromId Group BY u1.名称 HAVING SentCount > 3 ) as temp
  • 我不认为你可以,但据我所知,你可以创建第二个视图并从中进行选择,而不是使用子选择。如果您不介意存储过程,也可以使用临时表(假设 MySQL 版本足够新)。
【解决方案4】:

您可以通过为要使用的任何子查询创建一个单独的 VIEW,然后在您正在创建的 VIEW 中加入该子查询来解决此问题。 这是一个例子: http://blog.gruffdavies.com/2015/01/25/a-neat-mysql-hack-to-create-a-view-with-subquery-in-the-from-clause/

这非常方便,因为您很可能希望重用它,并帮助您保持 SQL DRY。

【讨论】:

  • 您好 Ninjabber - 请分享您遇到的任何特定视图陷阱。视图在 MySQL 中非常强大,但是是的,您确实需要了解它们的来龙去脉以防止性能问题,但根据我的经验,它们很容易避免。
  • 恐怕不在 mysql 5.0 中。没有 5.0 的实例,但自己创建一个,将 3 个子查询放在视图中,并将它们加入索引列。你会对解释计划感到惊讶。使用非常复杂的子查询,这些子查询单独工作得很好,即使在 5.6 中我也遇到了问题。 5.7不同。那里的处理更像是 oracle-ish
  • 忘记添加:将您的机器升级到 5.7 并且没有任何限制。甚至 AWS 也宣布在 RDS 上支持 5.7 您可能不会升级的唯一原因是如果您使用了一些还不支持 5.7 的应用服务器(比如 Hybris)。但这只是时间问题,因为 5.7 只是处于不同的级别,并且是迄今为止 MySQL 有史以来最好的引擎。最重要的是我们最终可以使用多个内核并处理并行事务,这反过来又允许您使用缓存并最终使用它的好处
  • 谢谢,知道这很有用 - 除了我没有指定合并和无意中获得巨大物化视图的视图之外,我在 5.5 中没有遇到任何重大问题。尽管我使用两个单独的查询/临时表解决了子查询(而不是视图)的连接,但我也看到了一些奇怪的行为。
【解决方案5】:

为每个子查询创建一个视图是要走的路。让它像魅力一样工作。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-09-29
    • 2014-02-12
    • 2015-12-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多