Clinton
Clinton

Reputation: 23135

Inline table in Oracle SQL

I'm trying to integrate with some software (that I can't modify) that queries a database that I can modify.

I can give this software SQL queries, like so "select username, firstname, lastname from users where username in ?"

The software than fills in the ? with something like ('alice', 'bob'), and gets user information for them.

Thing is, there's another piece of software, which I again can't modify, which occasionally generates users like 'user2343290' and feeds them through to the first piece of software. Of course, it throws errors because it can't find that user.

So the query I want to run is something like this:

select username, firstname, lastname from users where username in ?
UNION ALL
select t.column1, 'Unknown', 'Unknown' from create_table(?) t

where create_table generates a table with the rows mentioned in ?, with the first column named column1.

Or alternatively:

select username, firstname, lastname from users where username in ?
UNION ALL
select t.column1, 'Unknown', 'Unknown' from _universe_ t where t.column1 in ?

where _universe_ is some fake table that contains possible every value in column1 (i.e. infinitely large).

I've tried select ? from dual, but unfortunately this only worked when ? was something like ('x'), not ('x', 'y').

Keep in mind I can't change the format of how the ? comes out, so I can't do select 'alice' from dual union all select 'bob' from dual.

Anyone know how I could do what I've mentioned, or something else to have a similar effect?

Upvotes: 1

Views: 11895

Answers (2)

Dave Costa
Dave Costa

Reputation: 48121

You can turn the delimited string of names into a table type like so:

CREATE TYPE name_tab AS TABLE OF VARCHAR2(30);
/

SELECT * FROM table(name_tab('alice','bob'));

So you would just need to create the type then your example would become:

select username, firstname, lastname from users where username in ?
UNION ALL
select t.column1, 'Unknown', 'Unknown' from table(name_tab ?) t

(I'm assuming that the ? is replaced by simple text substitution -- because the IN wouldn't work if it was done as a bind variable -- and that the substituted text includes the parentheses.)

However, I am not sure the result of this will be helpful, since when a list of good usernames is given, you'll now have two result rows for each username, one with the actual information and another with the 'Unknown' values.

A better way to phrase the query might be:

select t.column_value username,
       NVL(users.firstname,'Unknown'),
       NVL(users.lastname,'Unknown')
  from table(name_tab ?) t left join users on users.username = t.column_value

That should give you one row per username, with the actual data if it exists, or the 'Unknown' values if it does not.

Upvotes: 1

Frank Schmitt
Frank Schmitt

Reputation: 30775

You could use a pipelined function:

create type empname_t is table of varchar2(100);

create or replace function to_list(p_Names in string) return empname_t pipelined is
begin
  pipe row(p_Names);
  return;
end;

select * from table(to_list('bob'))

If you need to split the names (e.g. 'bob,alice'), you could use a function accepting a string and returning a empname_t, e.g. Tom Kyte's in_list, see
http://asktom.oracle.com/pls/apex/f?p=100:11:0::::P11_QUESTION_ID:210612357425
and modify the to_list function to iterate over the collection and pipe each item from the collection.

Upvotes: 0

Related Questions