Oracle- create a temporary resultset for use in a query
How do I create a temporary result set for use in an SQL without creating a table and inserting the data?
Example: I have a list of, say 10 codes for example. I want to put this into a query, and then query the database to see which codes in this temporary list do not exist in a table.
If it was already in a table, I could do something like:
SELECT ITEM_CODE FROM TEMP_ITEMS MINUS SELECT ITEM_CODE FROM M_ITEMS
Is their a way without using PL/SQL, and pure SQL to create a temporary rowset before querying? Please don't answer with something like:
SELECT 1 FROM DUAL UNION ALL SELECT 2 FROM DUAL
I am sort of thinking of something where I can provide my codes in an IN statement, and it turns that into rows for use in a later query.
Edit: so everyone knows my objective here, basically I sometimes get a list of product codes that I need to find which ones in the list are not setup in our system. I want a quick way to throw this into an SQL statement so I can see which ones are not in the system (rather than importing data etc). I usually put these into excel, then do a formula such as :
So that I can create my comma separated list.
If you are using oracle 11g you can do this
with t as ( select (column_value).getnumberval() Codes from xmltable('1,2,3,4,5') ) SELECT * FROM t WHERE NOT EXISTS (SELECT 1 FROM M_ITEMS M WHERE codes = M.ITEM_CODE);
with t as ( select (column_value).getstringval() Codes from xmltable('"A","B","C"') ) SELECT * FROM t WHERE NOT EXISTS (SELECT 1 FROM M_ITEMS M WHERE codes = M.ITEM_CODE);
I would go with:
with t as ( select 1 as val from dual union all select 2 as val from dual ) select . . .
And then use "t" or whatever you call it, in the subsequent query block.
I'm not sure what the objection is to using the select method . . . just pop the values you want in a column in Excel and produce the code for each value by copying down the formula. Then paste the results back into your query interface.
If you want to use a temporary table, you can use the values clause. Alternatively, you can use string functions if you only want IN functionality. Put the values in a comma separated list and check to see if it matches a particular value:
where ','||<list>||',' like '%,'||col||',%'
There are two approaches I would lean towards:
1. Global Temporary Table
Although you say you don't want to create a table, it depends on why you don't want a table. If you choose to create a Global Temporary table, the rows are only visible to the session that inserted them, so it's like having a private in-memory table but gives you all the benefits of a real table - i.e. being able to query and join to it.
2. Pipelined function
You can create a function that returns the results in a form that can be queried using the TABLE() operator. More info here: http://www.oracle-base.com/articles/misc/pipelined-table-functions.php
This one is interesting because it's not a union and fit in a single select. You have to enter the string with delimiters ('a/b/c/def') two times though:
SELECT regexp_substr('a/b/c/def', '[^/]+', 1, ROWNUM) var, regexp_substr('2/432/sd/fsd', '[^/]+', 1, ROWNUM) var2 FROM dual CONNECT BY LEVEL <= length(regexp_replace('a/b/c/def', '[^/]', '')) + 1; var var2 === ==== a 2 b 432 c sd def fsd
Note: Credits go to : https://stackoverflow.com/a/1381495/463056
So using the with clause it would give someting like :
with tempo as ( SELECT regexp_substr('a/b/c/def', '[^/]+', 1, ROWNUM) var, regexp_substr('2/432/sd/fsd', '[^/]+', 1, ROWNUM) var2 FROM dual CONNECT BY LEVEL <= length(regexp_replace('a/b/c/def', '[^/]', '')) + 1 ) select ...
or you can use it in a from clause :
select ... from ( SELECT regexp_substr('a/b/c/def', '[^/]+', 1, ROWNUM) var, regexp_substr('2/432/sd/fsd', '[^/]+', 1, ROWNUM) var2 FROM dual CONNECT BY LEVEL <= length(regexp_replace('a/b/c/def', '[^/]', '')) + 1 ) tempo
It's a bit hokey-looking. But you can parse a string into separate rows using regular expressions assuming you are using 10g or later. For example
SQL> ed Wrote file afiedt.buf 1 SELECT REGEXP_SUBSTR('a,b,c,def,g', '[^ |,]+', 1, LEVEL) parsed_str 2 FROM dual 3* CONNECT BY LEVEL <= REGEXP_COUNT('a,b,c,def,g', '[^ |,]+') SQL> / PARSED_STR -------------------------------------------- a b c def g
Personally, I would find a pipelined table function or a PL/SQL block that generates a collection easier to understand, but if you have to do it in SQL you can.
Based on your edit, if you are getting a list of product codes that is already in some sort of file, it would seem to make more sense to use an external table to expose the file as a table or to use SQL*Loader to load the data into a table (temporary or permanent) that you can query. Barring either of those options, if you really want to manipulate the list in Excel first, it would make more sense to generate an IN list in Excel and just copy and past that into your query. Generating a comma-separated list of codes in Excel only to parse that list into it's constituent elements in SQL seems like way too many steps.