SQL Basics: Simple WITH [duplicate] - mysql

I'm trying to use MySQL to create a view with the "WITH" clause
WITH authorRating(aname, rating) AS
SELECT aname, AVG(quantity)
FROM book
GROUP BY aname
But it doesn't seem like MySQL supports this.
I thought this was pretty standard and I'm sure Oracle supports this. Is there anyway to force MySQL to use the "WITH" clause? I've tried it with the MyISAM and innoDB engine. Both of these don't work.

Update: MySQL 8.0 is finally getting the feature of common table expressions, including recursive CTEs.
Here's a blog announcing it: http://mysqlserverteam.com/mysql-8-0-labs-recursive-common-table-expressions-in-mysql-ctes/
Below is my earlier answer, which I originally wrote in 2008.
MySQL 5.x does not support queries using the WITH syntax defined in SQL-99, also called Common Table Expressions.
This has been a feature request for MySQL since January 2006: http://bugs.mysql.com/bug.php?id=16244
Other RDBMS products that support common table expressions:
Oracle 9i release 2 and later:
http://www.oracle-base.com/articles/misc/with-clause.php
Microsoft SQL Server 2005 and later:
http://msdn.microsoft.com/en-us/library/ms190766(v=sql.90).aspx
IBM DB2 UDB 8 and later:
http://publib.boulder.ibm.com/infocenter/db2luw/v8/index.jsp?topic=/com.ibm.db2.udb.doc/admin/r0000879.htm
PostgreSQL 8.4 and later:
https://www.postgresql.org/docs/current/static/queries-with.html
Sybase 11 and later:
http://dcx.sybase.com/1100/en/dbusage_en11/commontblexpr-s-5414852.html
SQLite 3.8.3 and later:
http://sqlite.org/lang_with.html
HSQLDB:
http://hsqldb.org/doc/guide/dataaccess-chapt.html#dac_with_clause
Firebird 2.1 and later (the first Open Source DBMS to support recursive queries):
http://www.firebirdsql.org/file/documentation/release_notes/html/rlsnotes210.html#rnfb210-cte
H2 Database (but only recursive):
http://www.h2database.com/html/advanced.html#recursive_queries
Informix 14.10 and later:
https://www.ibm.com/support/knowledgecenter/SSGU8G_14.1.0/com.ibm.sqls.doc/ids_sqs_with.htm

You might be interested in something like this:
select * from (
select * from table
) as Subquery

You've got the syntax right:
WITH AuthorRating(AuthorName, AuthorRating) AS
SELECT aname AS AuthorName,
AVG(quantity) AS AuthorRating
FROM Book
GROUP By Book.aname
However, as others have mentioned, MySQL does not support this command. WITH was added in SQL:1999; the newest version of the SQL standard is SQL:2008. You can find some more information about databases that support SQL:1999's various features on Wikipedia.
MySQL has traditionally lagged a bit in support for the SQL standard, whereas commercial databases like Oracle, SQL Server (recently), and DB2 have followed them a bit more closely. PostgreSQL is typically pretty standards compliant as well.
You may want to look at MySQL's roadmap; I'm not completely sure when this feature might be supported, but it's great for creating readable roll-up queries.

Oracle does support WITH.
It would look like this.
WITH emps as (SELECT * FROM Employees)
SELECT * FROM emps WHERE ID < 20
UNION ALL
SELECT * FROM emps where Sex = 'F'
#ysth WITH is hard to google because it's a common word typically excluded from searches.
You'd want to look at the SELECT docs to see how subquery factoring works.
I know this doesn't answer the OP but I'm cleaning up any confusion ysth may have started.

Building on the answer from #Mosty Mostacho, here's how you might do something equivalent in MySQL,for a specific case of determining what entries don't exist in a table, and are not in any other database.
select col1 from (
select 'value1' as col1 union
select 'value2' as col1 union
select 'value3' as col1
) as subquery
left join mytable as mytable.mycol = col1
where mytable.mycol is null
order by col1
You may want to use a text editor with macro capabilities to convert a list of values to the quoted select union clause.

MariaDB is now supporting WITH. MySQL for now is not.
https://mariadb.com/kb/en/mariadb/with/

Have you ever tried Temporary Table?
This solved my convern:
create temporary table abc (
column1 varchar(255)
column2 decimal
);
insert into abc
select ...
or otherwise
insert into abc
values ('text', 5.5), ('text2', 0815.8);
Then you can use this table in every select in this session:
select * from abc inner join users on ...;

WITH authorRating as (select aname, rating from book)
SELECT aname, AVG(quantity)
FROM authorRating
GROUP BY aname

Related

A values list into result set without UNION ALL

I found how to make a result set from a list of values without using temporary tables:
select 12 as id
union all
select 155 as id
union all
select 139 as id
union all
select 11 as id
this result set can be used in various joins later.
Now I am seeking for a possibility to shorten the SQL string passed to MySQL. Ideally there could be something like:
select (12, 155, 139, 11) as id;
but there is no such syntax in MySQL. Any thoughts?
What you're looking for is called the "table value constructor" in the SQL standard, and it would work like this:
SELECT id FROM (VALUES (12), (155), (139), (11)) AS t(id)
Unfortunately, it is not available in MySQL. To my knowledge, at least these databases support it:
CUBRID
DB2
Derby
HSQLDB
PostgreSQL
SQL Server
Sybase ASE
And at least these databases don't and need to emulate it using UNION ALL:
MS Access
Firebird
Informix
Ingres
MariaDB
MySQL
Oracle
SQLite
Sybase SQL Anywhere
Workaround using IN predicate
If this is merely about syntax (not performance), you could of course also query a table that is known to contain at least the values that you're looking for, and then simply use an IN predicate to list the values:
SELECT id FROM some_table WHERE id IN (12, 155, 139, 11)
Of course, this will probably be quite slower than the UNION ALL solution.
But syntactically, this is an obvious workaround given the fact according to the SQL standard, the IN predicate's "in value list" is really just syntax sugar for a "table value constructor" (although, I don't believe any SQL engine really implements that alternative syntax):
8.4 <in predicate>
2) Let IVL be an <in value list>.
( IVL )
is equivalent to the <table value constructor>:
( VALUES IVL )
I'm not exactly sure what you mean, but you can generate a list-as-string from a list of values with the group_concat function:
select group_concat(x.id ', ') as idList from
(
select 12 as id
union all
select 155 as id
union all
select 139 as id
union all
select 11 as id
) x
UPDATE: if you're actually looking to do the opposite i.e. pass over a comma-delimited list (e.g. select '12, 155, 139, 11' as id for use in the database, then you'll need a SPLIT function: this does not yet exist in mysql, but you can find several examples online: here for instance:
http://lists.mysql.com/mysql/199134

Can not Access Aliased Columns in Select Statement (MySQL)

I have a problem with Aliased Columns in MySQL!
My Query:
SELECT Price AS Pr, (Pr*10/100) FROM MyTable;
MySQL WorkBench Error: UnKnown Column 'Pr' in Field List !!!
I tested my query in W3Schools with no error !
I tested my query in W3Schools with no error!
This doesn't prove that your query is valid.
You can only use aliases in GROUP BY, ORDER BY or HAVING clauses. Your usage variant is not allowed, because the value of alias is not known when MySQL is selecting the 2-nd column.
I've got a suspicion that W3Schools uses MS Access to run user queries, and MS Access does allow such atrocity as referencing column aliases in a SELECT clause that are defined in the same SELECT clause.
The standard doesn't allow this and MySQL does follow standard in this particular case.
As for solution to your problem, I can see two options.
The more generic solution, which would run in probably any SQL product, would be to use a derived table:
SELECT
Pr,
(Pr * 10 / 100) AS SomethingElse
FROM
(
SELECT
SomeComplexExpression AS Pr
FROM MyTable
) AS sub
;
The other option would be to use a variable, which is MySQL-specific:
SELECT
#Pr := SomeComplexExpression AS Pr,
(#Pr * 10 / 100) AS SomethingElse
FROM MyTable
;
Finally, if you need to test/demonstrate if something can/cannot work in MySQL, I'd recommend using SQL Fiddle.

Is there a generic workaround to express a derived column list in Oracle (and MySQL)?

Many SQL databases support what the SQL standard calls a <derived column list>. Such databases include at least CUBRID, Derby, Firebird, HSQLDB, Postgres, SQL Server, and Sybase SQL Anywhere. A (simplified) extract from the SQL:2008 specification
7.6 <table reference>
Format
<table reference> ::=
<table or query name> [ [ AS ] <correlation name>
[ <left paren> <derived column list> <right paren> ] ]
| <derived table> [ AS ] <correlation name>
[ <left paren> <derived column list> <right paren> ]
This means, I can express things like these (e.g. in Postgres, which is pretty standards-compliant)
-- Rename a <table or query name> to u(b)
with t(a) as (select 1)
select * from t as u(b)
-- Rename a <derived table> to u(b)
select * from (select 1) as u(b)
Now, according to the Oracle documentation, I can't rename columns using a <derived column list> specification. I could of course rename tables and columns separately, like this:
-- Rename a <table or query name> to u(b)
with t(a) as (select 1 from dual)
select u.a b from t u;
-- Rename a <derived table> to u(b)
select u.a b from (select 1 a from dual) u;
But this requires more knowledge about the derived table (actual column names) than the previous syntax. Also, the renamed columns would only be available after the projection (e.g. in the ORDER BY clause), not in any other clauses, including the projection itself.
Is there a more generic way to rename tables AND columns the way the SQL standard suggests, in Oracle (and also MySQL)? In particular, this could be useful for things like array unnesting, pivot/unpivot table renaming, inlining complex subqueries, renaming results from table functions, etc.
N.B: Please do not focus on the above examples too much. They're really just here to illustrate the problem. Real-world queries are much more complex, so I'm looking for a very general way to implement renaming to u(b)
NOTE: I'm still looking for a solution that works on a database like MySQL. A related question:
How to select an unaliased numeric literal from a sub-select
For a MySQL solution, you could use a UNION to set the names of all the columns in a zero-row query term, and then subsequently query something more complex:
SELECT null AS a, null AS b, null AS c FROM dual WHERE false
UNION ALL
SELECT <expr>, <expr>, <expr>
FROM <realtable>...
Only the first query term of a UNION defines the column names of the whole query. Column names (or lack thereof) in subsequent query terms don't affect the ultimate column names.
You do need to know the number of columns, but it should be pretty easy to keep the two query terms separate. As far as I know, it works in both Oracle and MySQL (however, I have only tested it in MySQL, not in Oracle).
Since you MUST know the number of columns, but not necessarily the column names, you can use the WITH clause to rename these columns as you wish. For example (WITH works in Oracle and SQL Server, don't have MySQL instance handy):
WITH t(x,y,z) as (select * from TABLE(fn_returning_xcols(3)))
select * from t;
Here we don't know the column names in the inner select, but we can rename them in outer WITH clause.
Another example using a PIVOT in Oracle:
WITH t(a,b,c,d,e) as
(
select * from
(
select level as levl from dual connect by level <= 5
)
PIVOT(max(levl) as l for levl in (1,2,3,4,5))
)
select * from t;
Again, we don't care what the inner select column names are (the inner pivot creates somewhat odd column names), we just need to know how many columns and we can rename.
As suggested by user tbone here, common table expressions are a good solution to my problem, at least for Oracle. For completeness, here is my example query written using CTEs in Oracle
-- Rename a <derived table> to u(b) with Oracle
with u(b) as (select 1 from dual)
select u.b from u
-- Rename a <derived table> to u(b) with H2, which only knows recursive CTEs
-- Thanks to a comment by user a_horse_with_no_name
with recursive u(b) as (
select 1
union all
select null where false
)
select u.b from u

MySql using correct syntax for the over clause

What is the correct syntax to get the over clause to work in mysql?
I would like to see the total sms's sent by each user without grouping it with the group by clause.
SELECT
username,
count(sentSmsId) OVER (userId)
FROM
sentSmsTable,
userTable
WHERE
userId = sentUserId;
MySQL 8 has got the window functions! Therefore, you can write your query in it like this:
SELECT username,
count(sentSmsId) OVER (partition by userId)
FROM sentSmsTable
JOIN userTable ON userId = sentUserId;
There is no OVER clause in MySQL that I know of, but here is a link that might assist you to accomplish the same results:
http://explainextended.com/2009/03/10/analytic-functions-first_value-last_value-lead-lag/
Hope this helps.
MySQL does not currently support window functions, so over() will only yield syntax errors (or garbage, if it's accepted regardless).
MySQL Doesn't have window functions until the most recent release: MySQL 8 (release in April, 2018). MS SQL Server also accepts OVER clause.
The syntax is:
function(col1) OVER (PARTITION BY col2 ORDER BY col3)
Check out https://mysqlserverteam.com/mysql-8-0-2-introducing-window-functions/ for more examples.

MySQL "WITH" clause

I'm trying to use MySQL to create a view with the "WITH" clause
WITH authorRating(aname, rating) AS
SELECT aname, AVG(quantity)
FROM book
GROUP BY aname
But it doesn't seem like MySQL supports this.
I thought this was pretty standard and I'm sure Oracle supports this. Is there anyway to force MySQL to use the "WITH" clause? I've tried it with the MyISAM and innoDB engine. Both of these don't work.
Update: MySQL 8.0 is finally getting the feature of common table expressions, including recursive CTEs.
Here's a blog announcing it: http://mysqlserverteam.com/mysql-8-0-labs-recursive-common-table-expressions-in-mysql-ctes/
Below is my earlier answer, which I originally wrote in 2008.
MySQL 5.x does not support queries using the WITH syntax defined in SQL-99, also called Common Table Expressions.
This has been a feature request for MySQL since January 2006: http://bugs.mysql.com/bug.php?id=16244
Other RDBMS products that support common table expressions:
Oracle 9i release 2 and later:
http://www.oracle-base.com/articles/misc/with-clause.php
Microsoft SQL Server 2005 and later:
http://msdn.microsoft.com/en-us/library/ms190766(v=sql.90).aspx
IBM DB2 UDB 8 and later:
http://publib.boulder.ibm.com/infocenter/db2luw/v8/index.jsp?topic=/com.ibm.db2.udb.doc/admin/r0000879.htm
PostgreSQL 8.4 and later:
https://www.postgresql.org/docs/current/static/queries-with.html
Sybase 11 and later:
http://dcx.sybase.com/1100/en/dbusage_en11/commontblexpr-s-5414852.html
SQLite 3.8.3 and later:
http://sqlite.org/lang_with.html
HSQLDB:
http://hsqldb.org/doc/guide/dataaccess-chapt.html#dac_with_clause
Firebird 2.1 and later (the first Open Source DBMS to support recursive queries):
http://www.firebirdsql.org/file/documentation/release_notes/html/rlsnotes210.html#rnfb210-cte
H2 Database (but only recursive):
http://www.h2database.com/html/advanced.html#recursive_queries
Informix 14.10 and later:
https://www.ibm.com/support/knowledgecenter/SSGU8G_14.1.0/com.ibm.sqls.doc/ids_sqs_with.htm
You might be interested in something like this:
select * from (
select * from table
) as Subquery
You've got the syntax right:
WITH AuthorRating(AuthorName, AuthorRating) AS
SELECT aname AS AuthorName,
AVG(quantity) AS AuthorRating
FROM Book
GROUP By Book.aname
However, as others have mentioned, MySQL does not support this command. WITH was added in SQL:1999; the newest version of the SQL standard is SQL:2008. You can find some more information about databases that support SQL:1999's various features on Wikipedia.
MySQL has traditionally lagged a bit in support for the SQL standard, whereas commercial databases like Oracle, SQL Server (recently), and DB2 have followed them a bit more closely. PostgreSQL is typically pretty standards compliant as well.
You may want to look at MySQL's roadmap; I'm not completely sure when this feature might be supported, but it's great for creating readable roll-up queries.
Oracle does support WITH.
It would look like this.
WITH emps as (SELECT * FROM Employees)
SELECT * FROM emps WHERE ID < 20
UNION ALL
SELECT * FROM emps where Sex = 'F'
#ysth WITH is hard to google because it's a common word typically excluded from searches.
You'd want to look at the SELECT docs to see how subquery factoring works.
I know this doesn't answer the OP but I'm cleaning up any confusion ysth may have started.
Building on the answer from #Mosty Mostacho, here's how you might do something equivalent in MySQL,for a specific case of determining what entries don't exist in a table, and are not in any other database.
select col1 from (
select 'value1' as col1 union
select 'value2' as col1 union
select 'value3' as col1
) as subquery
left join mytable as mytable.mycol = col1
where mytable.mycol is null
order by col1
You may want to use a text editor with macro capabilities to convert a list of values to the quoted select union clause.
MariaDB is now supporting WITH. MySQL for now is not.
https://mariadb.com/kb/en/mariadb/with/
Have you ever tried Temporary Table?
This solved my convern:
create temporary table abc (
column1 varchar(255)
column2 decimal
);
insert into abc
select ...
or otherwise
insert into abc
values ('text', 5.5), ('text2', 0815.8);
Then you can use this table in every select in this session:
select * from abc inner join users on ...;
WITH authorRating as (select aname, rating from book)
SELECT aname, AVG(quantity)
FROM authorRating
GROUP BY aname