0

I need to perform a query like this:

SELECT *, 
    (SELECT Table1.Column 
     FROM Table1 
     INNER JOIN Table2 ON Table1.Table2Id = Table2.Id 
    ) as tmp 
FROM Table2 WHERE tmp = 1

I know I can take a workaround but I would like to know if this syntax is possible as it is (I think) in Mysql.

4
  • Why would a sql server guru know if that syntax works in MySQL? They are two completely different database systems. Commented May 27, 2009 at 12:50
  • He wants to know if a sqlserver query has an equivalent in mysql. The best person to ask would be someone who has used both. I doubt that is all that unusual - I've used both. Unfortunately, I don't have an mysql instance under my control so would feel uncomfortable with submitting an answer without checking it on a test table. Anyway, I don't think it's your place to ask that question especially when it is crystal clear from his question that he needs a mysql version of a sqlserver query. Commented May 27, 2009 at 12:56
  • Sure sounds like he wants a SQL Server version of a MySQL querry... Commented May 27, 2009 at 13:01
  • that syntax is not possible. Pretty sure you cannot use that "tmp" alias in the where clause on any dbms? Commented May 27, 2009 at 14:29

5 Answers 5

8

The query you posted won't work on sql server, because the sub query in your select clause could possibly return more than one row. I don't know how MySQL will treat it, but from what I'm reading MySQL will also yield an error if the sub query returns any duplicates. I do know that SQL Server won't even compile it.

The difference is that MySQL will at least attempt to run the query and if you're very lucky (Table2Id is unique in Table1) it will succeed. More probably is will return an error. SQL Server won't try to run it at all.

Here is a query that should run on either system, and won't cause an error if Table2Id is not unique in Table1. It will return "duplicate" rows in that case, where the only difference is the source of the Table1.Column value:

SELECT  Table2.*, Table1.Column AS tmp
FROM Table1 
INNER JOIN Table2 ON Table1.Table2Id = Table2.Id
WHERE Table1.Column = 1

Perhaps if you shared what you were trying to accomplish we could help you write a query that does it.

Sign up to request clarification or add additional context in comments.

8 Comments

I often use left joins instead of inner joins. The left join will guarentee one, and only one occurance of a row of data in a table I care about. That being said, I think Table2 is the table you care about, and should be listed first: 'FROM Table2 LEFT JOIN Table1 ...'
This site is broken if incorrect answers like this are getting voted up.
"left join will guarentee one, and only one occurance of a row of data in a table I care about." -- That is WRONG. A left join will guarantee one OR MORE occurance of the row.
+1 It looks to me like this answer would give the same result as the OP's query (if that query were legal).
@Bill Karwin: actually, the query in this answer will return rows in cases where the OP query would have failed with an exception. The query in this answer returns a different result set, unless we take a big leap and pre-assume that Table2Id column in Table1 is UNIQUE.
|
2
SELECT  *
FROM    (
        SELECT  t.*,
                (
                SELECT  Table1.Column
                FROM    Table1
                INNER JOIN
                        Table2
                ON      Table1.Table2Id = Table2.Id
                ) as tmp
        FROM    Table2 t
        ) q
WHERE   tmp = 1

This is valid syntax, but it will fail (both in MySQL and in SQL Server) if the subquery returns more than 1 row

What exactly are you trying to do?

Please provide some sample data and desired resultset.

1 Comment

At least this answer does not assume he really wanted to select a column value.
2

I agree with Joel's solution but I want to discuss why your query would be a bad idea to use (even though the syntax is essentially valid). This is a correlated subquery. The first issue with these is that they don't work if the subquery could possibly return more than one value for a record. The second and more critical problem (in my mind) is that they must work row by row rather than on the set of data. This means they will virtually always affect performance. So correlated subqueries should almost never be used in a production system. In this simple case, the join Joel showed is the correct solution.

If the subquery is more complicated, you may want to turn it into a derived table instead (this also fixes the more than one value associated to a record problem). While a derived table looks a lot like a correlated subquery to the uninitated, it does not perform the same way because it acts on the set of data rather than row-by row and thus will often be significantly faster. You are essentially making the query a table in the join.

Below is an example of your query re-written as a derived table. (Of course in production code you would not use select * either especially in a join, spell out the fields you need)

SELECT *     
FROM Table2 t2
JOIN
(SELECT Table1.[Column], Table1.Table2Id  as tmp      
FROM Table1      
INNER JOIN Table2 ON Table1.Table2Id = Table2.Id     ) as t
ON t.Table2Id = Table2.Id
WHERE tmp = 1

2 Comments

+1 Good points, but FWIW, MySQL doesn't use the square brackets for delimited identifiers. That's a Microsoft/Sybase convention. In MySQL, use back-quotes by default, or set SQL_MODE to ANSI_QUOTES and use standard double-quotes.
I figured he didn't really have a column named column, but I did not know mysql didn't allow the brackets. I just put the brackets in to test my syntax. Thanks for the info, never know when I might someday need to know mysql.
0

You've already got a variety of answers, some of them more useful than others. But to answer your question directly:

No, SQL Server will not allow you to reference the column alias (defined in the select list) in the predicate (the WHERE clause). I think that is sufficient to answer the question you asked.

Additional details:

(this discussion goes beyond the original question you asked.)

As you noted, there are several workarounds available.

Most problematic with the query you posted (as others have already pointed out) is that we aren't guaranteed that the subquery in the SELECT list returns only one row. If it does return more than one row, SQL Server will throw a "too many rows" exception:

 
    Subquery returned more than 1 value.
     This is not permitted when the subquery
     follows =, !=, , >= or when the
     subquery is used as an expression.

For the following discussion, I'm going to assume that issue is already sufficiently addressed.

Sometimes, the easiest way to make the alias available in the predicate is to use an inline view.

SELECT v.*
  FROM ( SELECT * 
              , (SELECT Table1.Column 
                   FROM Table1 
                   JOIN Table2 ON Table1.Table2Id = Table2.Id
                  WHERE Table1.Column = 1
                ) as tmp 
           FROM Table2
       ) v   
 WHERE v.tmp = 1

Note that SQL Server won't push the predicate for the outer query (WHERE v.tmp = 1) into the subquery in the inline view. So you need to push that in yourself, by including the WHERE Table1.Column = 1 predicate in the subquery, particularly if you're depending on that to make the subquery return only one value.

That's just one approach to working around the problem, there are others. I suspect that query plan for this SQL Server query is not going to be optimal, for performance, you probably want to go with a JOIN or an EXISTS predicate.

NOTE: I'm not an expert on using MySQL. I'm not all that familiar with MySQL support for subqueries. I do know (from painful experience) that subqueries weren't supported in MySQL 3.23, which made migrating an application from Oracle 8 to MySQL 3.23 particularly painful.

Oh and btw... of no interest to anyone in particular, the Teradata DBMS engine DOES have an extension that allows for the NAMED keyword in place of the AS keyword, and a NAMED expression CAN be referenced elsewhere in the QUERY, including the WHERE clause, the GROUP BY clause and the ORDER BY clause. Shuh-weeeet

Comments

0

That kind of syntax is basically valid (you need to move the where tmp=... to on outer "select * from (....)", though), although it's ambiguous since you have two sets named "Table2"- you should probably define aliases on at least one of your usages of that table to clear up the ambiguity.

Unless you intended that to return a column from table1 corresponding to columns in table2 ... in which case you might have wanted to simply join the tables?

2 Comments

It's not permitted (in SQL Server 2005) to reference an alias for an expression in the SELECT list in the query predicate.
good point, and in fact that's true in all DBs I've used as far as I can remember.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.