oracle sql dynamically select columns using a parameter table - sql

I have a table in oracle with 10 columns, say Table A with col_1, col_2, col_3 etc. I have another table, Table B with rows that has column names from table A co1_1, col_2, col_3. The rows in Table B can vary.
TABLE A
COL1 COL2 COL3 COL4 COL5 COL6 COL7 COL8 COL9 COL10
TABLE B
COL1
COL2
COL3
I want to write an oracle sql query which dynamically gets the select columns names based on the column names(rows) in table B.
If table B has 3 rows with corresponding column names then my query should look like this
select col_1, col_2, col_3 from A
If table B has 4 rows then my query should dynamically change to below
select col_1, col_2, col_3, col_4 from A

You need to use the dynamic query.
'SELECT '
|| (SELECT LISTAGG(COLNAME, ',') WITHIN GROUP (ORDER BY COLNAME) FROM TABLEB)
|| ' FROM TABLEA'

I think we should go to ALL_TAB_COLUMNS table for finding column names.
Like this.
SELECT
'SELECT '
||
(SELECT LISTAGG( y.COLNAME, ',') WITHIN GROUP (ORDER BY Y.COLNAME)
FROM TABLE_B x,ALL_TAB_COLUMNS y
where x.COLNAME=Y.COLUMN_NAME )
||
' FROM Table_A' script
FROM DUAL;

A ref cursor can be used to created dynamic columns. Many languages and applications support ref cursors, and if you add some details about your system someone may know exactly how to integrate them in your environment.
Below is a simple example of how to create a function that returns a ref cursor. How to call it depends on your system.
Sample Schema
--drop table a;
--drop table b;
create table a as
select 1 col1, 2 col2, 3 col3, 4 col4, 5 col5, 6 col6, 7 col7, 8 col8, 9 col9, 10 col10
from dual;
create table b as
select 'COL1' column_name from dual union all
select 'COL2' column_name from dual union all
select 'COL3' column_name from dual;
Function
create or replace function get_dynamic_results return sys_refcursor is
v_cursor sys_refcursor;
v_column_list varchar2(4000);
begin
--Split this into multiple SELECTS if you get an error like:
-- ORA-01489: result of string concatenation is too long error
select listagg(column_name, ',') within group (order by column_name) columns
into v_column_list
from b;
open v_cursor for 'select '||v_column_list||' from a';
return v_cursor;
end;
/

Related

How to convert sql query with in clauses into string

Hi I want to convert the sql statement into string for dynamic use. But I’m having trouble while concatenation
select
col1, col2
from
Table1
Where
Col3 in ( ‘cat’ , ‘dog’ );
I’m unable to figure out how to put quotes and || for the cat and dog
‘select
col1, col2
from
Table1
Where
Col3 in ( ‘||’‘cat’’ ||’, ‘’dog’ ‘||’)’ || Strcond;
One option is to use the q-quoting mechanism (see line #6), as it lets you use single quotes normally (also, your single quotes look fancy; if you planned to copy/paste from e.g. MS Word into your SQL client, that won't work).
Here's an example:
SQL> declare
2 l_str varchar2(500);
3 strcond varchar2(100) := ' and 1 = 1';
4 begin
5 l_str := 'select col1, col2 from table1 where col3 in ' ||
6 q'[('cat', 'dog')]' || strcond;
7 dbms_output.put_line(l_str);
8 end;
9 /
select col1, col2 from table1 where col3 in ('cat', 'dog') and 1 = 1
PL/SQL procedure successfully completed.
SQL>
One option it to use bind variables and put in as many bind variables as the maximum size of the IN list:
select col1, col2 from Table1 Where Col3 in ( :a001, :a002, :a003, :a004 )
Then:
you can use the same statement repeatedly and the SQL engine will not have to reparse it (after the first time).
you do not need to escape quotes in your list values in the dynamic SQL string.
your code is less vulnerable to SQL injection.
If you want to pass fewer values than the maximum then you can repeat values:
DECLARE
sql VARCHAR2(2000) := 'select col1, col2 from Table1 Where Col3 in ( :a001, :a002, :a003, :a004 )';
BEGIN
EXECUTE IMMEDIATE sql USING 'cat', 'dog', 'cat', 'cat';
END;
/
However, if you are going to do this then you can consider if you can eliminate dynamic SQL entirely:
DECLARE
a001 Table1.Col3%TYPE := 'cat';
a002 Table1.Col3%TYPE := 'dog';
a003 Table1.Col3%TYPE := a001;
a004 Table1.Col3%TYPE := a001;
col1_values SYS.ODCIVARCHAR2LIST;
col2_values SYS.ODCIVARCHAR2LIST;
BEGIN
select col1, col2
BULK COLLECT INTO col1_values, col2_values
from Table1
Where Col3 in ( a001, a002, a003, a004 );
END;
/

PostgreSQL: Get values of a register as multiple rows

Using PostgreSQL 9.3, I'm creating a Jasper reports template to make a pdf report. I want to create reports of different tables, with multiple columns, all with the same template. A solution could be to get values of register as pairs of column name and value per id.
By example, if I had a table like:
id | Column1 | Column2 | Column3
-------------------------------------------------
1 | Register1C1 | Register1C2 | Register1C3
I would like to get the register as:
Id | ColumnName | Value
-----------------------------
1 | Column1 | Register1C1
1 | Column2 | Register1C2
1 | Column3 | Register1C3
The data type of value columns can vary!
Is it possible? How can I do this?
If all your columns share the same data type and order of rows does not have to be enforced:
SELECT t.id, v.*
FROM tbl t, LATERAL (
VALUES
('col1', col1)
, ('col2', col2)
, ('col3', col3)
-- etc.
) v(col, val);
About LATERAL (requires Postgres 9.3 or later):
What is the difference between LATERAL and a subquery in PostgreSQL?
Combining it with a VALUES expression:
Crosstab transpose query request
SELECT DISTINCT on multiple columns
For varying data types, the common denominator would be text, since every type can be cast to text. Plus, order enforced:
SELECT t.id, v.col, v.val
FROM tbl t, LATERAL (
VALUES
(1, 'col1', col1::text)
, (2, 'col2', col2::text)
, (3, 'col3', col3::text)
-- etc.
) v(rank, col, val)
ORDER BY t.id, v.rank;
In Postgres 9.4 or later use the new unnest() for multiple arrays:
SELECT t.id, v.*
FROM tbl t, unnest('{col1,col2,col3}'::text[]
, ARRAY[col1,col2,col3]) v(col, val);
-- , ARRAY[col1::text,col2::text,col3::text]) v(col, val);
The commented alternative for varying data types.
Full automation for Postgres 9.4:
The query above is convenient to automate for a dynamic set of columns:
CREATE OR REPLACE FUNCTION f_transpose (_tbl regclass, VARIADIC _cols text[])
RETURNS TABLE (id int, col text, val text) AS
$func$
BEGIN
RETURN QUERY EXECUTE format(
'SELECT t.id, v.* FROM %s t, unnest($1, ARRAY[%s]) v'
, _tbl, array_to_string(_cols, '::text,') || '::text'))
-- , _tbl, array_to_string(_cols, ','))) -- simple alternative for only text
USING _cols;
END
$func$ LANGUAGE plpgsql;
Call - with table name and any number of column names, any data types:
SELECT * FROM f_transpose('table_name', 'column1', 'column2', 'column3');
Weakness: the list of column names is not safe against SQL injection. You could gather column names from pg_attribute instead. Example:
How to perform the same aggregation on every column, without listing the columns?
SELECT id
,unnest(string_to_array('col1,col2,col3', ',')) col_name
,unnest(string_to_array(col1 || ',' || col2 || ',' || col3, ',')) val
FROM t
Try following method:
My sample table name is t,to get the n columns name you can use this query
select string_agg(column_name,',') cols from information_schema.columns where
table_name='t' and column_name<>'id'
this query will selects all columns in your table except id column.If you want to specify schema name then use table_schema='your_schema_name' in where clause
To create select query dynamically
SELECT 'select id,unnest(string_to_array(''' || cols || ''','','')) col_name,unnest(string_to_array(' || cols1 || ','','')) val from t'
FROM (
SELECT string_agg(column_name, ',') cols -- here we'll get all the columns in table t
,string_agg(column_name, '||'',''||') cols1
FROM information_schema.columns
WHERE table_name = 't'
AND column_name <> 'id'
) tb;
And using following plpgsql function dynamically creates SELECT id,unnest(string_to_array('....')) col_name,unnest(string_to_array(.., ',')) val FROM t and execute.
CREATE OR replace FUNCTION fn ()
RETURNS TABLE (
id INT
,columname TEXT
,columnvalues TEXT
) AS $$
DECLARE qry TEXT;
BEGIN
SELECT 'select id,unnest(string_to_array(''' || cols || ''','','')) col_name,unnest(string_to_array(' || cols1 || ','','')) val from t'
INTO qry
FROM (
SELECT string_agg(column_name, ',') cols
,string_agg(column_name, '||'',''||') cols1
FROM information_schema.columns
WHERE table_name = 't'
AND column_name <> 'id'
) tb;
RETURN QUERY
EXECUTE format(qry);
END;$$
LANGUAGE plpgsql
Call this function like select * from fn()

displaying multiple tables in a schema of db

I have a schema and it consists of n tables . now the thing is to display those multiple tables independently like
Table1
Col1 col2 col3 . . . col n
Table2
Col1 col 2 col3 .....col n
.
.
.
Table N
Col1 col2 col3 ..... Col n
And I have to implement this using pl/sql this is the code I am trying with
DECLARE
name varchar2(50);
s_query varchar2(500);
cursor c_demo is select table_name from user_tables;
BEGIN
open c_demo;
loop
fetch c_demo into name;
EXIT WHEN c_demo%notfound;
select * into s_query from #name;
dbms_output.put_line(s_query);
end loop;
close c_demo;
end;
Assuming that you are using MS SQL. If you know the table names try using UNION/ UNION ALL. If the table definitions doesn't match, use aliases in your SELECT query as shown below.
SELECT T1COL1 AS COL1,T1COL2 AS COL2,T1COL3 AS COL3 FROM TABLE1
UNION ALL
SELECT T2COL1 AS COL1,T2COL2 AS COL2,T2COL3 AS COL3 FROM TABLE2
....
SELECT TNCOL1 AS COL1,TNCOL2 AS COL2,TNCOL3 AS COL3 FROM TABLEN

Customize SQL query

I have requirement where in I am creating a table dynamically(number of columns may change based on input parameter to my procedure through which I am creating this table) with data in the table like below.
PK col1 col2 col3
A null 1-2 3-4
B null null 4-5
C null 5-6 null
Now the requirement is I want to extract only the columns where at least there should be 1 record without null and spool the whole data into a file. My output should be like below (col1 exlluded from output as it has all nulls).
PK col2 col3
A 1-2 3-4
B null 4-5
C 5-6 null
Can anybody provide any hints to achieve this. Thanks in advance.
This won't be very efficient I suspect, but you can use COUNT() to determine if there are any only NULLS in a column because COUNT(column_here) will only add 1 for each non-null value. Hence if the count is zero that column is only NULLs.
This can then be combined into a query to generate a valid select statement and then that executed immediate (being careful of course to avoid sql injection).
Anyway, here's an example:
select
'select '
|| substr((
select
case when count(COL1) > 0 then ',col1' else '' end
|| case when count(COL2) > 0 then ',col2' else '' end
|| case when count(COL3) > 0 then ',col3' else '' end
from a_table
),2,8000)
|| ' from '
|| ' a_table'
as sql_string
from dual
;
see this sqlfiddle
result by the above is:
| SQL_STRING |
|--------------------------------|
| select col2,col3 from a_table |
Here is a try. First, create a function to generate your query, returning a REF CURSOR:
create or replace function select_non_nulls() return sys_refcursor as
myQuery varchar2(500);
myCur sys_refcursor;
begin
select 'select ' || listagg(col, ', ') within group (order by col) || ' from test'
into myQuery
from
(
select case when max(col1) is null then null else 'col1' end col from test
union all
select case when max(col2) is null then null else 'col2' end col from test
union all
select case when max(col3) is null then null else 'col3' end col from test
)
;
open myCur for myQuery;
return myCur;
end;
/
Then use it in SQL*Plus:
SQL> var rc refcursor
SQL> exec :rc := select_non_nulls;
SQL> print rc;
I used num_nulls from all_tab_cols and achieved result as per my requirement. Thank you.

Oracle select selected columns from a table whose column names are available in another table

Let me give an example :
Suppose I have a table TableA(Col1, Col2, Col3, Col4, Col5)
I have another table TableB where their are entries of the names of the columns of TableA that required to be fetched, for example Col2 and Col5
Now I want to write an SQL query that will only fetch the columns of TableA as defined in TableB .
Here is a start.
The idea is to build a concatenated list of column_names as a varchar
'col1, col2, col3, col4'
and to use it in a dynamic sql query.
declare
column_list xmltype;
column_names varchar(10000);
begin
SELECT
XMLAGG (XMLELEMENT (e, t1.column_name || ',')).EXTRACT ('//text()')
column_name
into column_list
FROM all_tab_cols t1
where t1.table_name = 'TABLEA'
and exists (select null
from TableB
where t1.column_name = <the field for the column_name in tableB>);
column_names := RTRIM(column_list.getClobVal(), ',');
--this will just display the sql query, you'll need to execute it to get your results with EXECUTE IMMEDIATE
dbms_output.put_line( 'SELECT '||column_names||' from TableA');
end;