60

In MySQL 5.0 why does the following error occur when trying to create a view with a subquery in the FROM clause?

ERROR 1349 (HY000): View's SELECT contains a subquery in the FROM clause

If this is a limitation of the MySQL engine, then why haven't they implemented this feature yet?

Also, what are some good workarounds for this limitation?

Are there any workarounds that work for any subquery in the FROM clause or are there some queries that can not be expressed without using a subquery in the FROM clause?


An example query (was buried in a comment):

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
Deduplicator
  • 44,692
  • 7
  • 66
  • 118
Daniel
  • 1,231
  • 3
  • 15
  • 20
  • Thank you for this post. I Typically avoid views in mysql. Working on an edge case, trying to union fk tables - shoehorning them into a view. Now this. What a pain - all these limitations on subqueries? Ready to move on to a different engine. – eggmatters Feb 26 '16 at 17:37
  • 1
    This was a [known design flaw in MySQL](https://bugs.mysql.com/bug.php?id=16757) and it took ~10 _years_ before it received some attention. It was fixed in MySQL 5.7.7 and I can confirm this is the case from 5.7.24. Just FYI. – code_dredd Jan 16 '19 at 22:59

5 Answers5

85

I had the same problem. I wanted to create a view to show information of the most recent year, from a table with records from 2009 to 2011. Here's the original query:

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

Outline of solution:

  1. create a view for each subquery
  2. replace subqueries with those views

Here's the solution query:

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;

It works fine on mysql 5.0.45, without much of a speed penalty (compared to executing the original sub-query select without any views).

Matt Fenwick
  • 48,199
  • 22
  • 128
  • 192
  • 5
    fail on backup, as mysql restore views based on their names so it will try to restore "v_latest_info" first and will fail because "v_max_year" does not exists yet... – Apolo May 29 '15 at 13:36
  • 1
    Will creation of these multiple views result in performance issues? – Annie Feb 15 '17 at 11:01
19

Couldn't your query just be written as:

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

That should also help with the known speed issues with subqueries in MySQL

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
Grant Limberg
  • 20,913
  • 11
  • 63
  • 84
  • Thanks, I didn't realize you could do the GROUP BY without having the aggregate function in the SELECT. So one of the reasons they don't allow subqueries in the FROM clause in MySQL is because of speed issues? – Daniel Oct 15 '08 at 20:01
  • It's not necessarily because of speed in this specific case. As it is right now, the optimizer does not work well on sub queries at all. Stay away from them if at all possible. This is being fixed in 6.0 and a lot of progress has been made on it, but that's in 6.0 and you're using 5.0. – Grant Limberg Oct 15 '08 at 20:43
  • 1
    Please do not use implicit syntax. It is a SQL antipattern and was repolaced by a better syntax 20years ago. – HLGEM Feb 17 '12 at 18:01
  • HLGEM, could you please show us an example of the better syntax? I assume you are saying we should use subqueries, but since MySQL doesn't allow them in a view I'm curious what the best practice is in this case. – Code Commander Feb 28 '12 at 18:26
  • 1
    @CodeCommander, HLGEM means you should use the ANSI join syntax: `... FROM Message m1 JOIN User u1 ON u1.uid = m1.UserFromID ...` – qu1j0t3 May 24 '13 at 20:01
  • 3
    *"Couldn't the query in your comment just be written as ..."* What comment was this in reference to? – Joshua Taylor Nov 26 '14 at 14:57
  • @JoshuaTaylor I just edited the original post with the query he is referencing to. – agent provocateur Jun 03 '15 at 20:59
  • @agentprovocateur which post did you edit? I don't see any edits to the answer, and the last edit to the question was "Aug 7 '13 at 14:21". – Joshua Taylor Jun 03 '15 at 21:01
  • @JoshuaTaylor the edit is pending approval... haha. i edited the original post/question. im not sure how peer review process works but http://stackoverflow.com/review/suggested-edits/8313127 – agent provocateur Jun 03 '15 at 21:05
  • i guess i cant add their original query to their question because they have to do it themselves(?). Some guy rejected me saying its "irrelevant information" LOL. "Couldn't the query in your comment just be written as:" seems pretty clear that the SELECTED ANSWER is referring to a query and without a query being posted on the original question... this is pretty much useless. its like they rather this question stay vague. I am going to guess that the OP will never update this on his own (originally posted 2008) as well. OH WELL. I TRIED. =) – agent provocateur Jun 04 '15 at 00:08
6

You can work around this by creating a separate VIEW for any subquery you want to use and then join to that in the VIEW you're creating. Here's an example: http://blog.gruffdavies.com/2015/01/25/a-neat-mysql-hack-to-create-a-view-with-subquery-in-the-from-clause/

This is quite handy as you'll very likely want to reuse it anyway and helps you keep your SQL DRY.

Gruff
  • 524
  • 5
  • 13
  • Hi Ninjabber - please share any specific view gotchas you have experienced. Views are very powerful in MySQL, but yes, you do need to know their ins and outs to guard against performance issues, but in my experience they're easily avoidable. – Gruff Mar 09 '16 at 08:07
  • 1
    not in mysql 5.0 I'm afraid. Don't have an instance with 5.0 but create yourself one, put 3 sub-queries in views and join them on indexed columns. You will be surprised by the explain plan. With really sophisticated sub-queries which individually worked fine I have had problems even in 5.6. 5.7 is different. There the handling is more oracle-ish – ninjabber Mar 10 '16 at 09:54
  • 1
    forgot to add : Upgrade your machine to 5.7 and there are no restrictions. Even AWS announced the support of 5.7 on RDS The only reason you might not wont to upgrade is if you use some app server (say Hybris) which does not support 5.7 yet. But that's a matter of time since 5.7 is just on a different level and by far the best engine MySQL ever had. Most important is we can finally use more then one core and process parallel transactions which in turn allows you to play with cache and finally use it's benefits – ninjabber Mar 10 '16 at 10:08
  • Thanks, that's useful to know - I've not had any major issues with 5.5 other than with views where I hadn't specified merge and inadvertently got huge materialised views. I have seen some odd behaviours though even with joins to subqueries (not views) which I worked around with two separate queries / temp tables. – Gruff Mar 11 '16 at 14:05
5

It appears to be a known issue.

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

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

Many IN queries can be re-written as (left outer) joins and an IS (NOT) NULL of some sort. for example

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

can be re-written as

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

or

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

can be

SELECT FOO.* FROM FOO 
LEFT OUTER JOIN FOO2 
ON FOO.ID=FOO2.ID WHERE FOO.ID IS NULL
ConroyP
  • 40,958
  • 16
  • 80
  • 86
Nikki9696
  • 6,260
  • 1
  • 28
  • 23
  • But how would you rewrite a query in the FROM clause? For instance, how could I rewrite this query?: 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 – Daniel Oct 15 '08 at 19:48
  • I don't think you can, but you can create a second view and select from that instead of using the sub-select, as far as I know. If you don't mind a stored proc, you can also use temp tables (assuming recent enough version of MySQL). – Nikki9696 Oct 15 '08 at 21:18
4

create a view for each subquery is the way to go. Got it working like a charm.

Dexin Wang
  • 477
  • 4
  • 6