Oracle SQL - Selecting column without knowing column name - sql

Is it possible to select the value of the second column using something like an index or column position if i dont know the name of the column?
Select col(2) FROM (
Select 'a', 'b',' c', 'd' from dual
)

Is it possible? Sure. You could write a PL/SQL block that used dbms_sql to open a cursor using the actual query against dual, describe the results, bind a variable to whatever you find the second column to be, fetch from the cursor, and then loop. That would be a terribly involved and generally rather painful process but it could be done.
The SQL language does not define a way to do this in a static SQL statement and Oracle does not provide an extension that would allow this. I'd be rather concerned about the underlying problem that you're trying to solve, though, if you somehow know that you want the second column but don't know what that column represents. That's not something that makes a lot of sense in a relational database.

SELECT ORDINAL_POSITION, COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'dual'
not sure if this is what you need.

If you don't know the name, just give it a nice name ;).
Select b FROM (
Select 'a', 'b' as b, 'c', 'd' from dual
)

It is possible in case when you know number of columns in the subquery:
SELECT COL2 FROM (
SELECT NULL COL1, NULL COL2, NULL COL3, NULL COL4
UNION ALL
select 'a', 'b', 'c', 'd' from dual
)
WHERE COL2 IS NOT NULL;
There are limitations:
query is hard to read and understand
you should know number of columns
value should not be NULL
not recommended for production use

Related

List of values as table

I'm looking for a smarter way to have a list of values as a table in Oracle.
What I do nowadays is
select 'value1' as val from dual
union
select 'value2' from dual
What I'm hoping for is some function/way/magic, that I'll do for example
select 'value1', 'value2' from dual -- + some additional magic
I'm looking for non-PL/SQL way which I think is overkill, but I'm not saying definite no to PL/SQL if that's the only option, but I can look here Create an Oracle function that returns a table for inspiration for PL/SQL. But extra table to have a list seems still easier to maintain than PL/SQL.
The motivation for not using select distict from transactional table is that I want to have a defined list of values, and with that approach, I can miss those I expect there but have no records in the table.
The expected number of elements in the list is several tens of records (like 30).
Here is one option:
select column_value
from table(sys.dbms_debug_vc2coll('value1', 'value2', 'value3', 'value4'));
Starting Oracle 12.2, you don't need the table function:
select column_value
from sys.dbms_debug_vc2coll('value1', 'value2', 'value3', 'value4');
Or yet another, similar:
SQL> select column_value
2 from table(sys.odcivarchar2list('Little', 'Foot', 'Scott', 'Tiger'))
3 order by column_value;
COLUMN_VALUE
----------------------------------------------------------------------------
Foot
Little
Scott
Tiger
SQL>
Starting with Oracle 12c you could use JSON_TABLE for that:
select *
from json_table('["value1", "value2"]', '$[*]'
columns val varchar(20) path '$');
If you aren't on 12c and can't use json_table (or even if you are/can but don't want to) you could use an XML sequence via xmltable instead:
select *
from xmltable('"value1", "value2", "value3"');
Result Sequence
--------------------------------------------------------------------------------
value1
value2
value3
I'd probably use an ODCI collection by default, but might be interesting to compare how all of these options compare with large data volumes - with 30 values you might not be able to see much of a difference.
Also, You can use the connect by query:
SQL> select regexp_substr('VALUE1,VALUE2','[^,]+', 1, level) from dual
2 connect by level <= regexp_count('VALUE1,VALUE2', '[^,]+');
REGEXP_SUBSTR('VALUE1,VALUE2','[^,]+',1,LEVEL)
----------------------------------------------------
VALUE1
VALUE2
SQL>

Managing SQL scripts with arguments

I keep many work-related SQL convenience scripts.
For a while I've been using a convention of having
several AND clauses in the where statement that I can activate
by providing a value or values to search on. For example,
where color like '%&color' and size like '%&size'
When I run such SQL in my preferred client (Golden6) it pops up
a dialog box where I can provide values for color, size or both.
Very convenient, but the performance of LIKE '%string' is often
terrible, often resulting in a full table scan, or so I have read.
Is there some alternative technique for writing and managing these
scripts that maintains the convenience of being able to fill in only the
arguments I want to use, yet avoids the performance issues around LIKE '%string'? I don't want to have to edit the script each time I use it,
because I keep them in git and I don't want to manage having a bunch of locally modified files to sort out all the time.
If you want to support optional input parameters then you could try
with data as
(select '123' col1, 'ABC' col2 from dual union select '124', 'AB' from dual)
select * from data a where a.col1 = nvl('&col1', a.col1) and a.col2 = nvl('&col2', a.col2)
Additonal data rows with null values
with data as
(select '123' col1, 'ABC' col2
from dual
union
select '124', 'AB'
from dual
union
select '123', null from dual
union
select '124', null from dual)
select *
from data
where ('&col1' is null or '&col1' is not null and '&col1' = col1)
and ('&col2' is null or '&col2' is not null and '&col2' = col2)

sql statement inside decode clause

The decode works like this:
SELECT DECODE('col1', 'x', 'result1','y','result2') resultFinal
FROM table1;
It possible to accomplish this in sql:
SELECT *
FROM (SELECT DECODE('col1', 'x' (someSql),'y',(someOthersql)) result
FROM table1)
So instead of result1 and result2 being fixed values, they would be sql statements. If not possible, how can I achieve the same result without a stored proc.
EDIT: someSql and someOthersql are both complex queries with many joins returining many but same number of cols with same col names.
If someSql and someOthersql return exactly one row with one column, then this should work.
The following works for me:
select decode(col, (select 'foo' from dual), (select 'bar' from dual))
from some table
I think you may need to create a PL/SQL procedure to handle the complex logic.

select a column where the column name is retrieved from a query

I'm looking for a elegant way to select a column from table A where as the column name is retrieved from a query on table B.
A query on table B results in 'col01'
The Table A has several columns named 'col01','col02','col03',...
Final query should be for result
result from B | effective SQL query
'col01' | SELECT col01 FROM A
'col02' | SELECT col02 FROM A
I'm looking for pure SQL solution with no scripting. It should run with Oracle and/or MySQL.
I'm looking for pure SQL solution with no scripting.
This is not possible. You have to create the statements dynamically.
In a very simple case you can just about do it with a CASE statement, but this is unlikely to be of much use in the real world:
with a as (
select 'col_01_val_01' as col_01, 'col_02_val_01' as col_02,
'col_03_val_01' as col_03 from dual
union select 'col_01_val_02' as col_01, 'col_02_val_02' as col_02,
'col_03_val_02' as col_03 from dual
union select 'col_01_val_03' as col_01, 'col_02_val_03' as col_02,
'col_03_val_03' as col_03 from dual),
b as (
select 1 parm, 'col_01' value from dual
union select 2 parm, 'col_02' value from dual
union select 3 parm, 'col_03' value from dual)
select b.value as result_from_b,
case b.value
when 'col_01' then col_01
when 'col_02' then col_02
when 'col_03' then col_03 end as result_from_a
from a, b
where b.parm = 1;
RESULT_FROM_B RESULT_FROM_A
------------- -------------
col_01 col_01_val_01
col_01 col_01_val_02
col_01 col_01_val_03
The columns have to be the same type (or at least expressible as such in the case), and b can only return one row... And it isn't very pretty of course. For anything more complicated you need dynamic SQL as #a_horse_with_no_name has already suggested.
This could be done within a stored routine in MySQL. I don't know about Oracle as I haven't used it. You can find out more about MySQL stored procedures here and here (the 2nd link is a pretty good PDF which walks you through some simple examples).

return a default row in sql

Is it possible in oracle sql to return a default row if no rows found.
I have a process where in rows fetched will be put in a flat ascii file.
now i have a requirement that if there are no rows fetched by the sql query then there should be a default row in the ascii file.
is it possible in sql to output a default row if no rows fetched by the query
note: i dont want to use pl/sql.
For complex queries where the overhead on finding out if there is a row in the result set is onerous or the query is just very large and unwieldy, then a subquery factoring clause might be beneficial:
With my_query as
(
select a, b, c from foobar where foo='FOO'
)
Select a,b,c
From my_query
Union All
Select ...
From dual
Where Not Exists
(Select 1 from my_query)
/
You could use UNION ALL for this:
select a, b, c from foobar
where foo='FOO'
union all
select 'def', 'ault', 'value' from dual
where not exists ( select 'x' from foobar where foo='FOO' )
I suspect that it would be cleaner to have the process that the writes the ASCII file write the default data if no rows are returned rather than getting Oracle to do it. If the query that you're using is expensive, you'd significantly increase that cost if you fudge it to return a default row as ammoQ and David Oniell have done.
There is another (sad, twisted) option. No CTEs or subquery repetition needed.
select
coalesce(a, 'def'),
coalesce(b, 'ault'),
coalesce(c, 'value')
from foobar
right join (select 1 from dual) on 1=1
where foobar.some_condition;