A values list into result set without UNION ALL - mysql

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

Related

MySQL: what's the maximum number of arguments allowed within the IN() function for one query? [duplicate]

Say you have the following query:
SELECT * FROM table1 WHERE table1.id IN (1, 2, 3, 4, 5, ..., 999999)
What is a reasonable maximum for the number of items in the IN clause? I'm using Sphinx to generate full-text search results and inserting the IDs into a MySQL query. Is this an acceptable way to do it?
You can also have the IN clause take the results of a query, such as:
SELECT * FROM table1
WHERE table1.id IN
(
SELECT id from table2
)
That way, you don't need to generate a text string with all the possible values.
In mysql, you should be able to put as many values in the IN clause as you want, only constrained by the value of "max_allowed_packet".
http://dev.mysql.com/doc/refman/5.0/en/comparison-operators.html#function_in
http://dev.mysql.com/doc/refman/5.0/en/server-system-variables.html#sysvar_max_allowed_packet
MariaDB (10.3.22 in my case) has a limit of 999 parameters to IN() before it creates a materialized temporary table, resulting in possibly much longer execution times. Depending on your indices. I haven't found a way to control this behaviour. MySQL 5.6.27 does not have this limit, at least not at ~1000 parameters. MySQL 5.7 might very well have the same "feature".
I ended up doing a series of where id = a or id = b ... but it also works fine by using series of where id in(a, b) or id in(c, d) ....
You have to add laravel row query and then add NOT IN condition into this:
$object->whereRaw('where id NOT IN (' . $array_list . ') ');
This will work for my code.
From my experience the maximum values is 1000 values in clause IN ('1',....,'1000'),
I have 1300 value in my excel sheet,I put them all into IN ,MySQL return only 1000 .

SQL Basics: Simple WITH [duplicate]

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

MySQL IN clause: max number of arguments

Say you have the following query:
SELECT * FROM table1 WHERE table1.id IN (1, 2, 3, 4, 5, ..., 999999)
What is a reasonable maximum for the number of items in the IN clause? I'm using Sphinx to generate full-text search results and inserting the IDs into a MySQL query. Is this an acceptable way to do it?
You can also have the IN clause take the results of a query, such as:
SELECT * FROM table1
WHERE table1.id IN
(
SELECT id from table2
)
That way, you don't need to generate a text string with all the possible values.
In mysql, you should be able to put as many values in the IN clause as you want, only constrained by the value of "max_allowed_packet".
http://dev.mysql.com/doc/refman/5.0/en/comparison-operators.html#function_in
http://dev.mysql.com/doc/refman/5.0/en/server-system-variables.html#sysvar_max_allowed_packet
MariaDB (10.3.22 in my case) has a limit of 999 parameters to IN() before it creates a materialized temporary table, resulting in possibly much longer execution times. Depending on your indices. I haven't found a way to control this behaviour. MySQL 5.6.27 does not have this limit, at least not at ~1000 parameters. MySQL 5.7 might very well have the same "feature".
I ended up doing a series of where id = a or id = b ... but it also works fine by using series of where id in(a, b) or id in(c, d) ....
You have to add laravel row query and then add NOT IN condition into this:
$object->whereRaw('where id NOT IN (' . $array_list . ') ');
This will work for my code.
From my experience the maximum values is 1000 values in clause IN ('1',....,'1000'),
I have 1300 value in my excel sheet,I put them all into IN ,MySQL return only 1000 .

Is there a way to run quick dummy SQL queries to test out aggregate functions, like so?

I realize this may be a really newbie question and I'm totally missin something , but I want to test out using MIN with strings (in MSSQL):
MIN('d3d742ce-f12e-4402-9a0e-8a05066f6bed',
'03f8d7a7-9feb-4375-b7ff-04c187d46009',
'1c180a55-ce67-4ab4-afe5-9d9907ed1c21' )
But it tells me:
Msg 174, Level 15, State 1, Line 5 The MIN function requires 1
argument(s).
And that makes sense to me now, but I want to do something like that if possible.
Is there a way to do something like this, without using a specific table?
What about just using a table variable
DECLARE #TABLE Table (str varchar(100))
INSERT #TABLE (str) VALUES ('d3d742ce-f12e-4402-9a0e-8a05066f6bed')
INSERT #TABLE (str) VALUES ('03f8d7a7-9feb-4375-b7ff-04c187d46009')
INSERT #TABLE (str) VALUES ('1c180a55-ce67-4ab4-afe5-9d9907ed1c21')
SELECT MIN(str) FROM #TABLE
That's it: (Works starting from SQL Server 2008)
SELECT min(id)
FROM (
VALUES
('d3d742ce-f12e-4402-9a0e-8a05066f6bed'),
('03f8d7a7-9feb-4375-b7ff-04c187d46009'),
('1c180a55-ce67-4ab4-afe5-9d9907ed1c21')) AS guids(id)
You can always create your own derived table on the fly with unions:
select min(MyCol)
from (
select 'd3d742ce-f12e-4402-9a0e-8a05066f6bed' as MyCol
union all select '03f8d7a7-9feb-4375-b7ff-04c187d46009'
union all select '1c180a55-ce67-4ab4-afe5-9d9907ed1c21'
) as MyDerivedTable
I'm using union all in this case because that's faster when you know your test data is all unique (it doesn't implicitly run as distinct like union).
This same technique can be used in a Common Table Expression (CTE):
;with CTE as (
select 'd3d742ce-f12e-4402-9a0e-8a05066f6bed' as MyCol
union all select '03f8d7a7-9feb-4375-b7ff-04c187d46009'
union all select '1c180a55-ce67-4ab4-afe5-9d9907ed1c21'
)
select min(MyCol)
from CTE
I've begun the statement with a semi-colon because SQL Server will complain if you haven't terminated the previous statement with one.
This only works to a point, however, as you can only use 256 tables in a query before having to resort to workarounds.

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