80

Is there any SQL subquery syntax that lets you define, literally, a temporary table?

For example, something like

SELECT
  MAX(count) AS max,
  COUNT(*) AS count
FROM
  (
    (1 AS id, 7 AS count),
    (2, 6),
    (3, 13),
    (4, 12),
    (5, 9)
  ) AS mytable
  INNER JOIN someothertable ON someothertable.id=mytable.id

This would save having to do two or three queries: creating temporary table, putting data in it, then using it in a join.

I am using MySQL but would be interested in other databases that could do something like that.

thomasrutter
  • 114,488
  • 30
  • 148
  • 167

8 Answers8

71

I suppose you could do a subquery with several SELECTs combined with UNIONs.

SELECT a, b, c, d
FROM (
    SELECT 1 AS a, 2 AS b, 3 AS c, 4 AS d
    UNION ALL 
    SELECT 5 , 6, 7, 8
) AS temp;
Blixt
  • 49,547
  • 13
  • 120
  • 153
35

You can do it in PostgreSQL:

=> select * from (values (1,7), (2,6), (3,13), (4,12), (5,9) ) x(id, count);
 id | count 
----+-------
  1 |     7
  2 |     6
  3 |    13
  4 |    12
  5 |     9

http://www.postgresql.org/docs/8.2/static/sql-values.html

Paul A Jungwirth
  • 23,504
  • 14
  • 74
  • 93
dvv
  • 559
  • 5
  • 6
29

In Microsoft T-SQL 2008 the format is:

SELECT a, b FROM (VALUES (1, 2), (3, 4), (5, 6), (7, 8), (9, 10) ) AS MyTable(a, b)

I.e. as Jonathan mentioned above, but without the 'table' keyword.

See:

Pete
  • 291
  • 3
  • 2
  • This works, but I want to create a second table this way and join them together, but I can't seem to get the syntax right... – Michael Mar 30 '23 at 20:25
22

In standard SQL (SQL 2003 - see http://savage.net.au/SQL/) you can use:

INSERT INTO SomeTable(Id, Count) VALUES (1, 7), (2, 6), (3, 13), ...

With a bit more chasing, you can also use:

SELECT * FROM TABLE(VALUES (1,7), (2, 6), (3, 13), ...) AS SomeTable(Id, Count)

Whether these work in MySQL is a separate issue - but you can always ask to get it added, or add it yourself (that's the beauty of Open Source).

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • Thanks for the answer! Unfortunately MySQL (5.0) doesn't like FROM TABLE(VALUES... but at least I know about it now – thomasrutter Jun 12 '09 at 07:09
  • This answer is probably more informative than the accepted answer, it just didn't solve my personal problem as well because I was using MySQL. I wish I could accept multiple answers. – thomasrutter Dec 06 '11 at 04:33
  • 1
    conversely, the ugly of Open Source is, asking will get ignored or rejected unless a lot of people and/or a developer with free time is interested, and the learning curve for DIY is often time prohibitive. (not to say the alternative is in any way better...) – Michael Apr 01 '15 at 21:05
  • 1
    Remove the keyword `TABLE` and this will work with MS SQL Server 2008 and probably above. – Zarepheth Oct 15 '15 at 15:33
7

Since MariaDB v10.3.3 and MySQL v8.0.19 you can now do exactly that!

See docs: MariaDB, MySQL

MariaDB:

WITH literaltable (id,count) AS (VALUES (1,7),(2,6),(3,13),(4,12),(5,9))
SELECT MAX(count) AS max,COUNT(*) AS count FROM literaltable

I used a WITH here because MariaDB doesn't supply nice column names for VALUES .... You can use it in a union without column names:

SELECT 1 AS id,7 AS count UNION ALL VALUES (2,6),(3,13),(4,12),(5,9) ORDER BY count DESC

And although the docs don't appear to mention it, you can even use it as a top-level query:

VALUES (1,7),(2,6),(3,13),(4,12),(5,9) ORDER BY 2 DESC

The actual column names are in fact the just first row of values, so you can even do this (though it's inelegant, and you can run into duplicate column name errors):

SELECT MAX(`7`) AS max,COUNT(*) AS count FROM (VALUES (1,7),(2,6),(3,13),(4,12),(5,9)) literaltable

MySQL:

I don't have an instance of MySQL v8.0.19 to test against right now, but according to the docs either of these should work:

SELECT MAX(column_1) AS max,COUNT(*) AS count FROM (VALUES ROW(1,7), ROW(2,6), ROW(3,13), ROW(4,12), ROW(5,9)) literaltable

SELECT MAX(data) AS max,COUNT(*) AS count FROM (VALUES ROW(1,7), ROW(2,6), ROW(3,13), ROW(4,12), ROW(5,9)) literaltable(id,data)

Unlike MariaDB, MySQL supplies automatic column names column_0, column_1, column_2, etc., and also supports renaming all of a subquery's columns when referencing it.

I'm not sure, but this dev worklog page seems to suggest that MySQL has also implemented the shorter sytax (omitting "ROW", like MariaDB), or that they will in the near future.

Doin
  • 7,545
  • 4
  • 35
  • 37
  • 1
    In the [release page for 8.0.19](https://dev.mysql.com/blog-archive/the-mysql-8-0-19-maintenance-release-is-generally-available/) they state: """ due to a conflict with the VALUES() function, a MySQL non-standard feature, we decided to use the SQL standard verbose form of the table value constructors: VALUES ROW(1, 2) """ So unfortunately I don't think they plan on supporting VALUES without ROW – matrix10657 Oct 30 '22 at 00:07
6

I found this link Temporary Tables With MySQL

CREATE TEMPORARY TABLE TempTable ( ID int, Name char(100) ) TYPE=HEAP; 

INSERT INTO TempTable VALUES( 1, "Foo bar" ); 

SELECT * FROM TempTable; 

DROP TABLE TempTable;
ole6ka
  • 210
  • 1
  • 3
  • Just wondering if this was the only answer for MySQL. In the same situation as some other commenters, and wondering if there was a different construct to use besides temporary tables, like a sort fo select query listing the data inline, but I guess that is still also just data in memory the same as this example. – Pysis May 12 '18 at 19:47
1

In a word, yes. Even better IMO if your SQL product supports common table expressions (CTEs) i.e. easier on the eye than using a subquery plus the same CTE can be used multiple times e.g. this to 'create' a sequence table of unique integers between 0 and 999 in SQL Server 2005 and above:

WITH Digits (nbr) AS 
(
 SELECT 0 AS nbr UNION ALL SELECT 1 UNION ALL SELECT 2 
 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 
 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 
 UNION ALL SELECT 9 
), 
Sequence (seq) AS
(
 SELECT Units.nbr + Tens.nbr + Hundreds.nbr 
   FROM Digits AS Units
        CROSS JOIN Digits AS Tens
        CROSS JOIN Digits AS Hundreds
)
SELECT S1.seq 
  FROM Sequence AS S1;

except you'd actually do something useful with the Sequence table e.g. parse the characters from a VARCHAR column in a base table.

HOWEVER, if you are using this table, which consists only of literal values, multiple time or in multiple queries then why not make it a base table in the first place? Every database I use has a Sequence table of integers (usually 100K rows) because it is so useful generally.

onedaywhen
  • 55,269
  • 12
  • 100
  • 138
0

CREATE TEMPORARY TABLE ( ID int, Name char(100) ) SELECT ....

Read more at : http://dev.mysql.com/doc/refman/5.0/en/create-table.html

( near the bottom )

This has the advantage that if there is any problem populating the table ( data type mismatch ) the table is automatically dropped.

An early answer used a FROM SELECT clause. If possible use that because it saves the headache of cleaning up the table.

Disadvantage ( which may not matter ) with the FROM SELECT is how large is the data set created. A temporary table allows for indexing which may be critical. For the subsequent query. Seems counter-intuitive but even with a medium size data set ( ~1000 rows), it can be faster to have a index created for the query to operate on.

Pat
  • 5,761
  • 5
  • 34
  • 50