Select Query Column name based on Table name - oracle

I have like thousands tables, each of them will have their own unique key(s) columns. I want to select the keys based on the table name in a single query. Its always the second column in the table if it helps.
I want something like
select c_name from t_name
where c_name (is)
(
select column_name as c_name
from Dba_tab_Columns
where table_name = t_name
and column_name like '%name' --->>>(((or column_id =2)))
)
I know the t_name but I need a single query to select column name based on table_name.
So let say if I say select c_name from Animals, it should give me list of all animals and if I say select c_name from Cars, it should give me a list of avilable cars.

You cannot do this in pure SQL, you'll need a table function. Here's a way:
create or replace type tvc as table of varchar2(128);
/
create or replace function return_col (tname user_tables.table_name%type) return tvc pipelined
as
c_statement varchar2(400);
get_data sys_refcursor;
out_d varchar2(128);
begin
for gettnames_and_cols in (select c.column_name
from user_cons_columns c, user_constraints uc
where constraint_type in ('P','U') and uc.table_name=c.table_name and c.table_name=upper(tname)) loop
c_statement:='select '||gettnames_and_cols.column_name||' as output_col from '||tname;
open get_data for c_statement;
while true loop
fetch get_data into out_d;
exit when get_data%notfound;
pipe row(out_d);
end loop;
close get_data;
end loop;
return;
end;
/
Thing is that this just gives the data, with no idea what's the column_name or from which table the data comes. You can modify the PL/SQL code to add this info. Also, this assumes that the data will be returned as VARCHAR2(128), you may need to adapt this to your needs. You use this table function as follows:
select *
from table (return_col('your_table_name'));

Related

Get list of columns and alias name by a query string in Oracle

I'm using Pl/SQL with Oracle Database 11g.
I want to get a list of columns with alias by a query string.
There is a way to get all the column names of a query, using dbms_sql.describe_columns2 but only I get alias.
For example:
DECLARE
l_cursor NUMBER := dbms_sql.open_cursor;
l_ignore NUMBER;
l_desc dbms_sql.desc_tab2;
l_cnt NUMBER;
BEGIN
dbms_sql.parse( l_cursor, 'select a.col1 column1, a.col2 column2 from table_test a', dbms_sql.native );
dbms_sql.describe_columns2( l_cursor, l_cnt, l_desc );
FOR i IN 1 .. l_cnt LOOP
dbms_output.put_line(l_desc(i).col_name);
END LOOP;
dbms_sql.close_cursor( l_cursor );
END;
/
Returns:
column1
column2
Is there any way to get the values a.col1, or a.col2 with alias in the query?
The column names used in a SQL statement can be retrieved using Rob van Wijk's custom view DBA_DEPENDENCY_COLUMNS.
Install the view as SYS and grant select on dba_dependency_columns to <your user>;.
The view only works on objects. Create a VIEW on the SELECT statement and then the dependencies will be available. For example:
create table table_test(col1 number, col2 number);
create or replace view test_view as
select a.col1 column1, a.col2 column2 from table_test a;
select referenced_column
from sys.dba_dependency_columns
where owner = user
and name = 'TEST_VIEW'
order by 1;
Results:
REFERENCED_COLUMN
-----------------
COL1
COL2
The above results get the "list of columns". But part of your answer also implies you may want to get the "column expressions". That would be a completely different task, but is possible. Parsing SQL can be ridiculously difficult so it might help to explain exactly what you want and why you want it.

oracle query select a column (where column name is unknown)

My column name is retrieved from another query, then using that result as the column name I want to query to get the value. I have found a duplicate question here but I could not find the answer. So my code looks like this:
DECLARE
COL_NO NUMBER(3,0);
COL_NAME VARCHAR2(30);
COL_VALUE VARCHAR2(100);
BEGIN
COL_NO:=0;
COL_NAME:=NULL;
COL_VALUE:=NULL;
SELECT COUNT(*) INTO COL_NO FROM user_tab_columns WHERE table_name='SALARYSLIP';
FOR A IN 4..COL_NO LOOP
SELECT column_name INTO COL_NAME FROM user_tab_columns WHERE column_id=A AND table_name='SALARYSLIP';
--SELECT COL_NAME INTO COL_VALUE FROM SALARYSLIP WHERE ID=SALARYSLIP.ID;
DBMS_OUTPUT.PUT_LINE(COL_VALUE);
END LOOP;
END;
The commented query is supposed to fetch the column value but is not working obviously. What should I write there? TIA.
I'm not exactly sure what you are trying to accomplish. Counting the umber of columns in the table and iterating from 4 to that number doesn't seem particularly logical. In the desired SQL statement, it's also not clear where you are getting the value for the id that you want to use.
If you want to determine the column name at runtime, then you would need to use dynamic SQL. Something like
FOR A IN 4..COL_NO LOOP
SELECT column_name INTO COL_NAME FROM user_tab_columns WHERE column_id=A AND table_name='SALARYSLIP';
--SELECT COL_NAME INTO COL_VALUE FROM SALARYSLIP WHERE ID=SALARYSLIP.ID;
EXECUTE IMMEDIATE 'SELECT ' || col_name ||
' FROM salaryslip ' ||
' WHERE id = :1'
INTO col_value
USING <<whatever ID value you want>>;
DBMS_OUTPUT.PUT_LINE(COL_VALUE);
END LOOP;
For simplicity, I'm not using a local variable for the SQL statement that is assembled before being dynamically executed. Normally, you would want to have a local variable with the SQL statement so that you can do things like log the statement that was assembled in case it doesn't do what you expected.
I think the logic needs to be addressed first - i.e. whether the required results is in terms of the rows from the SALARYSLIP table, OR the columns of the SALARYSLIP table.
SELECT COUNT(*) INTO COL_NO FROM user_tab_columns WHERE table_name='SALARYSLIP'; ( this would get the number of columns from the SALARYSLIP table )
After this, what is the desired result to iterate through the for loop, based on the number of columns on the SALARYSLIP table? i.e. since, generally, the output is based on the rows being retrieved.
Where is the "ID" value coming from? It's not being initialized / declared in the code.
From what I gather, the column_name of the table is required to be retrieved at runtime. Then, the data for this column should be displayed for the SALARYSLIP table.
Try this :
DECLARE
COL_NO NUMBER(3,0);
COL_NAME VARCHAR2(30);
COL_VALUE VARCHAR2(100);
ROWNO NUMBER(3,0);
BEGIN
COL_NO:=0;
COL_NAME:=NULL;
COL_VALUE:=NULL;
SELECT COUNT(*) INTO COL_NO FROM user_tab_columns WHERE table_name='SALARYSLIP'; --number of columns in SALARYSLIP
SELECT Count(*) INTO rownno FROM SALARYSLIP; --number of rows in SALARYSLIP
--go through each column
FOR A IN 1..COL_NO LOOP
SELECT column_name INTO COL_NAME FROM user_tab_columns WHERE column_id=A AND table_name='SALARYSLIP'; --column_name
--go through each row in the SALARYSLIP table
--for each row, get the specific column of the row, and print this column's value
FOR B IN 1..rowno LOOP
SELECT COL_NAME INTO COL_VALUE FROM SALARYSLIP WHERE =SALARYSLIP.COL_NAME = COL_NAME;
DBMS_OUTPUT.PUT_LINE(COL_VALUE);
END LOOP;
END LOOP;
END;

Dynamically selecting partitions

I have a table with a few hundred partitions and I am generally interested on the latest 35.
Accordingly I am trying to create views which would access these dynamically. i.e. always use the latest in case ones are created.
The query:
select PARTITION_NAME,
PARTITION_POSITION,
NUM_ROWS,
AVG_ROW_LEN
from all_tab_partitions
where
table_name = 'MY_TABLE'
AND PARTITION_NAME <> 'P_LAST'
AND PARTITION_POSITION < (SELECT MAX(PARTITION_POSITION)
FROM all_tab_partitions) - 35
order by 2 DESC
;
Seems to return me the partition names I'm interested, however, I don't manage to use it's results to select the partitions. e.g.:
CREATE OR REPLACE VIEW MY_VIIEW AS
WITH t AS ( [Above query] )
SELECT * FROM
MY_TABLE PARTITION (SELECT /*+ FIRST_ROWS(1) */ PARTITION_NAME
from t);
(not the actual view, just an example)
So how do I do that? How do I create a view which will acess always the latest partition (execpt of "MAX")?
I am using Oracle 10g
thanks
You can do it using PL/SQL only
create or replace package my_table_ is
type t_records is table of my_table%rowtype;
function getpart(c_parts sys_refcursor) return t_records pipelined;
end;
create or replace package body my_table_ is
function getpart(c_parts sys_refcursor) return t_records pipelined is
v_partition all_tab_partitions.partition_name%type;
v_row my_table%rowtype;
c_mytab sys_refcursor;
begin
loop
fetch c_parts into v_partition;
exit when c_parts%notfound;
open c_mytab for 'select * from my_table partition ('||v_partition||')';
loop
fetch c_mytab into v_row;
exit when c_mytab%notfound;
pipe row (v_row);
end loop;
end loop;
end;
end;
Now you can
select * from table(my_table_.getpart(cursor(<QUERY_RETURNING_PARTITION_NAMES>)));
May be you can construct view's query using batch of union all statements with partition name in each statement, e.g.
create view p as
select * from my_table partition (part1)
union all
select * from my_table partition (part1)
...
union all
select * from my_table partition (part35)
Ok... I don't think your can use the Partition-Names, but you can use the Starting-Values of the Partitions to select the Data matching these Partitions...
So you View would look like this:
SELECT * FROM my_table WHERE date_col > get_part_limit( 'my_table', 35 ):
Where date_col is the column you use for partitioning - and get_part_limit is a stored function you write like this:
...
BEGIN
SELECT high_value FROM all_tab_partitions
INTO local_var
WHERE table_name = parameter_name
AND PARTITION_POSITION = MAX... - 35
EXECUTE IMMEDIATE 'SELECT '||local_var||' FROM DUAL' INTO local_return_value;
RETURN local_return_value;
END;
partitions are designed to be transparent for the data, so when you write a query, you simply don't know how your data is stored.
I see only one possibility to hit a particular partition: your WHERE clause should match values to the partitioned columns of latest (or latest 5) partition.
Next question is to build this WHERE clause on the fly. You already know that there is plenty of information in oracle dictionary. So you will read that and create a constructor to convert metadata conditions back into SQL.
irl we do exactly the same thing and use falco's solution like.
Here is my code:
create or replace function longToDate( myOwner varchar2,
mytable_name in varchar2,
mypartition_name in varchar2
) return date
as
cDate date;
cvar varchar2(1024);
rq varchar2(1024);
infiniteValue EXCEPTION;
PRAGMA EXCEPTION_INIT(infiniteValue, -00904);
begin
select high_value into cvar FROM dba_tab_partitions t where t.table_owner=myOwner and table_name=mytable_name and partition_name=mypartition_name;
rq:='select '||cvar||' from dual';
execute immediate rq into cDate;
return cdate;
EXCEPTION
WHEN infiniteValue
then return'01 jan 3000';
when others
then return null;
end longToDate;
Ant the view is something like this
create or replace view last_35 as
with maxdate as
(select longToDate(p.table_owner,p.table_name,p.partition_name) mydate,
rank()over(order by p.partition_position desc) mypos,
p.* from all_tab_partitions p
where p.table_name='MY_TABLE'
)
select /*+full(a)*/* from MY_TABLE a, maxdate
where MY_TABLE.partition_name>maxdate.mydate
and maxdate.mypos=35

Dynamically selecting table name using sub-query in from clause

I have a mapping table with source and table name as attributes.
And i have the below query :
select *
from
(select table_name from mapping_table where source='CL Table')
I want to retrieve the data of the table_name but this query will return only the table name not the data.
How to do it?
DECLARE
v_mystring VARCHAR(50);
v_my_ref_cursor sys_refcursor;
myrecord <some_table_or_type>;
BEGIN
select table_name into my_table_name
from mapping_table
where source='CL Table';
v_mystring := 'SELECT * from '||my_table_name;;
OPEN v_my_ref_cursor FOR v_mystring;
LOOP
FETCH v_my_ref_cursor INTO myrecord;
-- your processing
END LOOP;
CLOSE v_my_ref_cursor;
END;

Oracle datasets from two different tables without joining

I need to write a stored procedure that will provide the data from two different tables. Say table1 and table2. These two tables doesn't have any relationship.
Now in SQL Server i can simply create a stored procedures like:
create procedure abc
as
begin
select * from table1;
select * from table2:
end;
Now in oracle, I usually create a SYS_REFCURSOR and do something like:
Open SYS_REFCURSOR_VAR For Select * from table1;
but I don't know how to provide the two result sets from two different tables table1 and table2. I tried to create two different SYS_REFCURSOR one for each table. But when I executed the procedure I got the data from first table only. The second SYS_REFCURSOR doesn't seems to be working.
Anyone have any idea, how to accomplish this?
Try this
create or replace procedure tst
(p_refcursor1 out sys_refcursor,p_refcursor2 out sys_refcursor)
is
begin
open p_refcursor1 for
select * from dual;
open p_refcursor2 for
select * from dual;
end;
So I assume the records you want to pull from each table are effectively the same. e.g. id, name, price. In that case just write your query like
SELECT t1.id AS id, t1.name AS name, t1.unit_price AS price FROM t1
UNION
SELECT t2.id AS id, t2.description AS name, t2.price AS price FROM t2
Not sure if its required, but always good form to have a union return data sets with the same "column" names. So I used the AS in teh example to demonstrate this
You need to give more details about your problem.. specifically
1) the code that you have tried so far
2) How are you accessing your ref cursor to see the results? (SQLPLUS or Java or VB.net)?
Based on your SQL Server code, I am assuming you are trying to get the UNION of the rows from the two tables. Here is the code in Oracle that lets you do that. As you can see, my client tool here is SQLPLUS and I am able to see the values from both the tables.
create table t1(
id number,
name varchar2(10)
);
create table t2(
id number,
name varchar2(10)
);
create or replace procedure get_t1_and_t2(
o_cur out sys_refcursor) is
begin
open o_cur for
select id from t1
union all
select id from t2;
end;
/
Procedure created.
SQL> var rc refcursor;
SQL> exec get_t1_and_t2( :rc);
PL/SQL procedure successfully completed.
SQL> print rc;
ID
----------
1
2
This script shows #Maxim Shevtsov answer working. Run it in SQL*Plus
SET serveroutput ON size 100000
DECLARE
c1 SYS_REFCURSOR;
c2 SYS_REFCURSOR;
v1 VARCHAR2(10);
v2 VARCHAR2(10);
n2 NUMBER;
PROCEDURE tst ( p_refcursor1 OUT SYS_REFCURSOR
, p_refcursor2 OUT SYS_REFCURSOR)
IS
BEGIN
OPEN p_refcursor1 FOR
SELECT 'val1' FROM DUAL;
OPEN p_refcursor2 FOR
SELECT 'val2', 42 FROM DUAL;
END tst;
BEGIN
tst( c1, c2 );
LOOP
FETCH c1 INTO v1;
EXIT WHEN c1%NOTFOUND;
DBMS_OUTPUT.put_line( 'CURSOR1: ' || v1 );
END LOOP;
LOOP
FETCH c2 INTO v2, n2;
EXIT WHEN c2%NOTFOUND;
DBMS_OUTPUT.put_line( 'CURSOR2: ' || v2 || ' ' || n2 );
END LOOP;
END;
/

Resources