Reputation: 475
Is there any way to build a CTE for a list of hard coded values? For example, I have a list of known IDs (i.e. 101,102,105,200...), how would I be able to create a CTE with one column called ID but all the ID values are hard coded in the query? BTW, I need to run this query in Oracle. Thanks!
Upvotes: 33
Views: 56305
Reputation: 4840
Slight improvement to Justin Cave's popular answer (works in SQL Server and PostgreSQL, may not work in Oracle):
WITH cte (num) AS
(
SELECT 101 UNION SELECT 102 UNION SELECT 105 UNION SELECT 200
)
select num from cte
Upvotes: 1
Reputation: 786
You can use collection types for this, either submitting them as a bound parameter or creating them within your query.
As Justin Cave suggested, you can create your own collection type, but SYS
contains a few for basic types that are defined by default, such as SYS.ODCIDATELIST
(for DATE
), SYS.ODCINUMBERLIST
(for NUMBER
/NUMERIC
), SYS.ODCIVARCHAR2LIST
(for VARCHAR2
up to 4000 chars), among those that don't seem specifically made for internal use.
Since you're using integer IDs, the built-in numeric collection sould work just fine:
-- column_value and table() work similarily to UNNEST() in Postgres
SELECT column_value as selected_id FROM TABLE(
SYS.ODCINUMBERLIST(101, 102, 105, 200)
)
Depending on what your host language supports, you can also send the list as a parameter instead. In Python, for instance, using cx_Oracle directly:
import cx_Oracle
query = 'select column_value as selected_id FROM TABLE(:id_list)'
conn = cx_Oracle.connect('user', 'hunter2', '//192.0.2.5:1521/mydb')
OdciNumberList = conn.gettype("SYS.ODCINUMBERLIST")
id_list = OdciNumberList.newobject()
id_list.extend([101, 102, 105, 200])
cur = conn.cursor()
res = cur.execute(query, id_list=id_list )
res.fetchall() # [(101,), (102,), (105,), (200,)]
Then you just need to wrap it in a CTE.
Upvotes: 3
Reputation: 698
EDIT: previously advised solution works only for MSSQL. Therefore I am adding an Oracle solution. I am keeping the original answer below.
I thought of one more solution (though the one provided by Justin Cave still seems a bit better) - using temporary tables.
Here is how it may look like
CREATE GLOBAL TEMPORARY TABLE temp_ids
(id INT)
ON COMMIT PRESERVE ROWS;
INSERT INTO ids (id) VALUES (101);
INSERT INTO ids (id) VALUES (102);
INSERT INTO ids (id) VALUES (103);
This should be a valid solution for Oracle database.
Original answer below
I have come across similar issue and here is my solution (this does not work on Oracle DB as mentioned in comments, only MSSQL though)
WITH cte AS (
SELECT * FROM (
VALUES
(1, 2, 3, ...),
(2, 3, 4, ...)
) AS a (col1, col2, col3, ...)
)
INSERT INTO ...
Hope this helps :)
Upvotes: 49
Reputation: 231681
You could do something like
WITH cte AS (
SELECT 101 id FROM dual UNION ALL
SELECT 102 FROM dual UNION ALL
SELECT 105 FROM dual UNION ALL
SELECT 200 FROM dual UNION ALL
...
)
Depending on what you are really trying to accomplish, though, you may want to declare a collection and use that (with or without a function that parses a comma-separated string)
CREATE TYPE num_tbl
AS TABLE OF NUMBER;
WITH cte AS (
SELECT column_value
FROM TABLE( num_tbl( 101, 102, 105, 200 ))
)
Upvotes: 23