Fetch all columns into one column oracle - sql

How do i achieve to fetch all columns into one column.
I have below sample query
WITH da AS(
SELECT 'a' col1, 'b' col2, 'c' col3 FROM dual UNION ALL
SELECT '1' col1, '2' col1, '3' col1 FROM dual UNION ALL
SELECT 'x' col1, 'y' col1, 'z' col1 FROM dual
)
SELECT * FROM da;
//RESULTS
COL1|COL2|COL3|
----|----|----|
a |b |c |
1 |2 |3 |
x |y |z |
But what i want is the result to be as below with | as the delimiter.
ONE_COL|
-------|
a|b|c |
1|2|3 |
x|y|z |
My biggest problem is i don't know the column names, therefore i cant do as:
SELECT col1||'|'||col2||'|'||col3 AS ONE_COL FROM da;
how can i do this.

The following is just to demonstrate a way to achieve what you asked for. (Assuming I didn't misunderstand what you asked for.)
First some preparation.
Create the DA table (according to the sample data in your question).
create table DA (COL1 char(1), COL2 char(1), COL3 char(1));
Add rows to the table (again, according to the sample data in your question).
insert into DA values ('a','b','c');
insert into DA values ('1','2','3');
insert into DA values ('x','y','z');
I understand that you want to insert the data from table DA into another table, so I created a second database table according to what I understood from your question.
create table RESULT (ONE_COL char(5));
PL/SQL code that retrieves the names of the columns (assuming you know the table name) from the data dictionary, then creates a dynamic cursor to retrieve the values from that table (again in the format described in your question) and inserts the rows fetched by the dynamic cursor into a second database table (since this is your requirement as I understood it from your question).
declare
L_COL varchar2(128);
L_CUR sys_refcursor;
L_ONE char(5);
L_SQL varchar2(2000);
--
cursor C_COLS is
select COLUMN_NAME
from USER_TAB_COLUMNS
where TABLE_NAME = 'DA';
begin
L_SQL := 'select ';
open C_COLS;
fetch C_COLS into L_COL;
L_SQL := L_SQL || L_COL;
while C_COLS%found
loop
fetch C_COLS into L_COL;
if C_COLS%found then
L_SQL := L_SQL || '||';
L_SQL := L_SQL || '''|''||';
L_SQL := L_SQL || L_COL;
end if;
end loop;
close C_COLS;
L_SQL := L_SQL || ' from DA';
open L_CUR for L_SQL;
loop
fetch L_CUR into L_ONE;
exit when L_CUR%notfound;
insert into RESULT values (L_ONE);
end loop;
close L_CUR;
end;
Finally, after running the above PL/SQL code...
select * from RESULT;
which returns
ONE_C
-----
abc
123
xyz

Concat the dual columns within the with clause, that way you don't need know what the da columns are called.
WITH da AS(
SELECT 'a' || '|' || 'b' || '|' || 'c' AS one_col FROM dual UNION ALL
SELECT '1' || '|' || '2' || '|' || '3' AS one_col FROM dual UNION ALL
SELECT 'x' || '|' || 'y' || '|' || 'z' AS one_col FROM dual
)
SELECT * FROM da;

The database knows the column names. So use the dbms_sql package and parse your sql.
https://docs.oracle.com/en/database/oracle/oracle-database/19/arpls/DBMS_SQL.html#GUID-C96D5BAA-29A9-4AB5-A69E-E31228ECC9E9
There are many solutions to extract the data afterwards so I don't get your point why you think that you don't know any column names

Related

oracle dynamic pivot error: SQL command not properly ended

I have a table whose data is obtained according to the desired output with pivot. But I want to create the number of columns dynamically.
my table :
create table myTable(ROW_NAME varchar(10),COLUMN_NAME varchar(10),COLUMN_NAME_VALUE varchar(10));
table data :
insert into myTable (ROW_NAME,COLUMN_NAME,COLUMN_NAME_VALUE)
select 'ROW1','COL1','R1C1' from dual
union all select 'ROW1','COL2','R1C2' from dual
union all select 'ROW1','COL3','R1C3' from dual
union all select 'ROW2','COL1','R2C1' from dual
union all select 'ROW2','COL2','R2C2' from dual
union all select 'ROW2','COL3','R2C3' from dual
union all select 'ROW3','COL1','R3C1' from dual
union all select 'ROW3','COL2','R3C3' from dual
union all select 'ROW3','COL3','R3C3' from dual
my query :
select * from myTable
pivot (
max (COLUMN_NAME_VALUE)
for COLUMN_NAME
in (
'COL1' as COL1,'COL2' as COL2,'COL3' as COL3
)
)
ORDER BY ROW_NAME;
The above query works but I want to get the columns dynamically.
my dynamic query :
DECLARE
mycols VARCHAR2(1000);
sqlCommand varchar2(1000);
TYPE PivotCurTyp IS REF CURSOR;
pivot_cv PivotCurTyp;
piv_rec mytable%ROWTYPE;
BEGIN
select (select LISTAGG(COLUMN_NAME, ',') from myTable group by ROW_NAME FETCH FIRST 1 ROWS ONLY) into mycols from dual;
select Concat('select * from myTable pivot ( max (COLUMN_NAME_VALUE) for COLUMN_NAME in (',Concat(mycols,')) ORDER BY ROW_NAME;')) into sqlCommand from dual;
OPEN pivot_cv FOR sqlCommand;
LOOP
FETCH pivot_cv INTO piv_rec;
EXIT WHEN pivot_cv%NOTFOUND;
DBMS_OUTPUT.PUT_LINE('ROW_NAME: ' || piv_rec.ROW_NAME || ' COL1: ' ||
piv_rec.COLUMN_NAME_VALUE || 'COL2: ' || piv_rec.COLUMN_NAME_VALUE || 'COL3: ' || piv_rec.COLUMN_NAME_VALUE);
END LOOP;
CLOSE pivot_cv;
END;
/
Note : The equivalent of the above query can be generated on SQL Server and I have created it.
demo in db<>fiddle
Thanks for any help
There are 3 problems in your script:
semicolon-terminated dynamic query (that's the cause of "SQL command not properly ended")
identifiers in in clause instead of string literals (you can use 'foo' or 'foo' as foo but not foo alone)
improper piv_rec type - use table format after pivot, not before pivot
Summary:
DECLARE
mycols VARCHAR2(1000);
sqlCommand varchar2(1000);
TYPE PivotCurTyp IS REF CURSOR;
pivot_cv PivotCurTyp;
type pivotted is record (row_name myTable.row_name%type, col1 myTable.column_name_value%type, col2 myTable.column_name_value%type, col3 myTable.column_name_value%type);
piv_rec pivotted;
BEGIN
select (select LISTAGG('''' || COLUMN_NAME || '''', ',') from myTable group by ROW_NAME FETCH FIRST 1 ROWS ONLY) into mycols from dual;
select Concat('select * from myTable pivot ( max (COLUMN_NAME_VALUE) for COLUMN_NAME in (',Concat(mycols,')) ORDER BY ROW_NAME')) into sqlCommand from dual;
DBMS_OUTPUT.PUT_LINE(sqlCommand);
OPEN pivot_cv FOR sqlCommand;
LOOP
FETCH pivot_cv INTO piv_rec;
EXIT WHEN pivot_cv%NOTFOUND;
DBMS_OUTPUT.PUT_LINE('ROW_NAME: ' || piv_rec.ROW_NAME || ' COL1: ' ||
piv_rec.COL1 || ' COL2: ' || piv_rec.COL2 || ' COL3: ' || piv_rec.COL3);
END LOOP;
CLOSE pivot_cv;
END;
/
updated db fiddle (BTW composing fiddle was very motivating to help)

List columns of table on which there is at least one row with a non null value for a specific query

I'm trying to find the proper query to :
Get all the column names of a table on which there is at least one row with a non null value for a specific query.
Meaning : I will see which columns have at least one value set in the record returned by my given query.
I hope I'm clear enough.
I think you need something as the following:
SELECT CASE WHEN MAX(col1) IS NOT NULL THEN 'COL1' END ||','
|| CASE WHEN MAX(col2) IS NOT NULL THEN 'COL2' END ||','
...
FROM T
Then use REGEXP_REPLACE to replace duplicated ,. You can use user_tab_columns to generate this query dynamically as mentioned.
Please check below anonymous block for your case , tablename should be given at 3 places in below query
Declare
v_columnlist varchar2(32627);
v_noofcolumns number;
Columnnm varchar2(1000);
Finalcolumns varchar2(32627);
v_count number:=0;
plsql_block varchar2(32627);
Begin
select LISTAGG(column_name,',') within group (order by column_id),max(column_id)
into v_columnlist , v_noofcolumns
from user_tab_columns
where table_name='tablename';
FOR Lcntr IN 1..v_noofcolumns
LOOP
select REGEXP_SUBSTR(v_columnlist,'[^,]+',1,Lcntr)
into Columnnm
from dual;
plsql_block := 'select count(*) from tablename where '|| Columnnm || ' is not null ';
EXECUTE IMMEDIATE plsql_block into v_count;
IF v_count > 1 THEN
Finalcolumns:= LTRIM(Finalcolumns ||','||Columnnm,',');
END IF;
END LOOP;
plsql_block := 'select ' || Finalcolumns ||' from tablename ';
DBMS_OUTPUT.PUT_LINE(plsql_block);
END;

Need help on writing sql query with dynamic columns

I have to write a query which does below. I tried but couldn't write. Please help me.
I have table which returns below result set.
select *
from table1; --(rowid and ColumnName are columns of the table)
Output:
rowid ColumnName
------------------------------
1 Segment1
2 Segment2
I have another table which has below structure : (Segment1 and Segment2 are columns here)
select *
from table2;
Output:
appId Segment1 Segment2 Segment3
---------------------------------------------
a1 fld1 fld2 per
a2 cmp1 hcd4 klp
I need to write a query, which reads the "ColumnName" values from first table and retrieves column values in the second table.
That means, from the table1, I will know what are the available columns I the table2 and from table2, I will know what is the data stored against those columns.
Please let me know if I am not clear.
This query is in Oracle SQL
As mentioned in the comment you need a PLSQL block with dynamic sql. See below an example:
Tables:
create table table1 (row_id number,
ColumnName varchar2(100))
create table table2 (appId number,
Segment1 varchar2(100),
Segment2 varchar2(100),
Segment3 varchar2(100));
Insert all
into TABLE1 (ROW_ID, COLUMNNAME) Values (1, 'Segment1')
into TABLE1 (ROW_ID, COLUMNNAME) Values (2, 'Segment2')
into TABLE2 (APPID, SEGMENT1, SEGMENT2, SEGMENT3) Values (1, 'RRR', 'KKK', 'MMM')
into TABLE2 (APPID, SEGMENT1, SEGMENT2, SEGMENT3) Values (2, 'ZZZ', 'PPP', 'QQQ')
into TABLE2 (APPID, SEGMENT1, SEGMENT2, SEGMENT3) Values (3, 'LLL', 'NNN', 'DDD')
select * from dual;
Code:
DECLARE
var VARCHAR2 (1000);
v_sql VARCHAR2 (2000);
TYPE x_var IS TABLE OF VARCHAR2(1000);
z_var x_var;
num number:=0;
BEGIN
FOR rec IN ( SELECT DISTINCT columnname
FROM table1
ORDER BY 1)
LOOP
num := num +1;
if num = 1 then
var:= rec.columnname;
else
var := var || ' || '' , ''||' || rec.columnname;
end if;
END LOOP;
var := RTRIM (LTRIM (var, ','), ',');
v_sql := 'select '|| var ||' from table2';
EXECUTE IMMEDIATE v_sql BULK COLLECT INTO z_var;
FOR i IN 1 .. z_var.COUNT
LOOP
DBMS_OUTPUT.put_line (z_var(i));
END LOOP;
END;
Output:
SQL> /
RRR , KKK
ZZZ , PPP
LLL , NNN
Dynamic columns in a SQL statement are almost always a bad idea. There's usually a way to avoid these kind of problems and build a simpler solution.
But if this is one of those rare times when you really need to run dynamic SQL in SQL then you'll need to install and run something like my open source project Method4.
For example:
create table table1 as
select 1 id, 'Segment1' columnName from dual union all
select 2 id, 'Segment2' columnName from dual;
create table table2 as
select 'a1' appId, 'fld1' Segment1, 'fld2' Segment2, 'per' Segment3 from dual union all
select 'a2' appId, 'cmp1' Segment1, 'hcd4' Segment2, 'klp' Segment3 from dual;
select * from table(method4.dynamic_query(
q'[
select
'select appID, '
||listagg(columnName, ',') within group (order by id)
||' from table2'
sql_statement
from table1
]'
));
APPID SEGMENT1 SEGMENT2
----- -------- --------
a1 fld1 fld2
a2 cmp1 hcd4
There are a lot of downsides to running this way. The code is complicated, slow, and has some odd behavior. For an explanation of how this works, see this article
by Adrian Billington.
Will the below PL SQL block help your requirement.
BEGIN
FOR iter IN (
SELECT column_name
FROM all_tab_columns
WHERE upper(table_name) = 'table1'
AND UPPER(column_name) LIKE 'SEGMENT%'
)
LOOP
SELECT iter.column_name INTO temp_table FROM table1
dbms_output.put_line(temp_table.column_name);
END LOOP;
END;
/
Say you have tables like the following:
SQL> select * from someTable;
COLUMN1 COLUMN2 COLUMN3
---------- ---------- ----------
1 2 3
2 4 6
3 6 9
SQL> select * from tableOfColumns;
COLUMNN
-------
column1
column3
You may need something like the following:
SQL> declare
2 type tListOfResults is table of varchar2(1000);
3 vSQL varchar2(1000);
4 vResult tListOfResults ;
5 begin
6 select 'select ' || listagg (columnName, ' || '', '' || ') within group (order by columnName) || ' from someTable'
7 into vSQL
8 from tableOfColumns;
9 --
10 execute immediate vSQL bulk collect into vResult;
11 if vResult.count() > 0 then
12 for i in vResult.first .. vResult.last loop
13 dbms_output.put_line(vResult(i));
14 end loop;
15 end if;
16 end;
17 /
1, 3
2, 6
3, 9
PL/SQL procedure successfully completed.

fetch table name from a column for from clause

I have a view t with me which has a column for table name and another column which has where clause condition.
id| name|table_in| where_clause
1 | Sam | t1 | age = 22
2 | John| t2 | age = 23 and sex = 'male'
and so on...
Now, I have put the records in a cursor and I want to run each query.
create or replace procedure create_cursor
is
CURSOR v_records is
select * from t ;
begin
FOR temp IN v_records LOOP
INSERT INTO myTable (id, name)
select temp.id, temp.name
from temp.table where temp.where_clause;
END LOOP;
end;
/
myTable is another table in which I want to put the records for next purpose.
#Akshay,
Please find the code below for your reference.
Create or replace procedure create_cursor is
l_statement varchar2(32767);
cursor v_records is
select * from t;
begin
for temp in v_records
loop
l_statement := 'INSERT INTO myTable (id, name) select '||temp.id||','
||temp.name|| ' from ' || temp.table1
|| ' where ' || temp.where_clause;
execute immediate l_statement;
end loop;
end;
/
You need dynamic sql to do this:
CREATE OR REPLACE PROCEDURE create_cursor
IS
l_statement VARCHAR2(32767);
CURSOR v_records
IS
SELECT * FROM t;
BEGIN
FOR temp IN v_records
LOOP
l_statement := 'INSERT INTO myTable (id, name)
select id, name from ' || temp.table ||
' where ' || temp.where_clause;
EXECUTE immediate l_statement;
END LOOP;
END;
/

How to select columns from a table which have non null values?

I have a table containing hundreds of columns many of which are null, and I would like have my select statement so that only those columns containing a value are returned. It would help me analyze data better. Something like:
Select (non null columns) from tablename;
I want to select all columns which have at least one non-null value.
Can this be done?
Have a look as statistics information, it may be useful for you:
SQL> exec dbms_stats.gather_table_stats('SCOTT','EMP');
PL/SQL procedure successfully completed.
SQL> select num_rows from all_tables where owner='SCOTT' and table_name='EMP';
NUM_ROWS
----------
14
SQL> select column_name,nullable,num_distinct,num_nulls from all_tab_columns
2 where owner='SCOTT' and table_name='EMP' order by column_id;
COLUMN_NAME N NUM_DISTINCT NUM_NULLS
------------------------------ - ------------ ----------
EMPNO N 14 0
ENAME Y 14 0
JOB Y 5 0
MGR Y 6 1
HIREDATE Y 13 0
SAL Y 12 0
COMM Y 4 10
DEPTNO Y 3 0
8 rows selected.
For example you can check if NUM_NULLS = NUM_ROWS to identify "empty" columns.
Reference: ALL_TAB_COLUMNS, ALL_TABLES.
Use the below:
SELECT *
FROM information_schema.columns
WHERE table_name = 'Table_Name' and is_nullable = 'NO'
Table_Name has to be replaced accordingly...
select column_name
from user_tab_columns
where table_name='Table_name' and num_nulls=0;
Here is simple code to get non null columns..
I don't think this can be done in a single query. You may need some plsql to first test what columns contain data and put together a statement based on that information. Of course, if the data in your table changes you have to recreate the statement.
declare
l_table varchar2(30) := 'YOUR_TABLE';
l_statement varchar2(32767);
l_test_statement varchar2(32767);
l_contains_value pls_integer;
-- select column_names from your table
cursor c is
select column_name
,nullable
from user_tab_columns
where table_name = l_table;
begin
l_statement := 'select ';
for r in c
loop
-- If column is not nullable it will always contain a value
if r.nullable = 'N'
then
-- add column to select list.
l_statement := l_statement || r.column_name || ',';
else
-- check if there is a row that has a value for this column
begin
l_test_statement := 'select 1 from dual where exists (select 1 from ' || l_table || ' where ' ||
r.column_name || ' is not null)';
dbms_output.put_line(l_test_statement);
execute immediate l_test_statement
into l_contains_value;
-- Yes, add column to select list
l_statement := l_statement || r.column_name || ',';
exception
when no_data_found then
null;
end;
end if;
end loop;
-- create a select statement
l_statement := substr(l_statement, 1, length(l_statement) - 1) || ' from ' || l_table;
end;
select rtrim (xmlagg (xmlelement (e, column_name || ',')).extract ('//text()'), ',') col
from (select column_name
from user_tab_columns
where table_name='<table_name>' and low_value is not null)
This block determines all columns in the table, loops through them in dynamic SQL and checks if they are null, then constructs a DBMS output query of the non-null query.
All you have to do is run the returned query.
I've included the exclusion of PKs and BLOB columns.
Obviously, this is quite slow as going through columns one by one, and it's not going to be great for very hot tables, as data may change too quickly, but this works for me as I control traffic in dev env.
DECLARE
l_table_name VARCHAR2(255) := 'XXXX';
l_counter NUMBER;
l_sql CLOB;
BEGIN
FOR r_col IN (SELECT *
FROM user_tab_columns tab_col
WHERE table_name = l_table_name
AND data_type NOT IN ('BLOB')
AND column_name NOT IN (SELECT column_name
FROM user_cons_columns con_col
JOIN user_constraints cons ON con_col.constraint_name = cons.constraint_name AND con_col.table_name = cons.table_name
WHERE con_col.table_name = tab_col.table_name
AND constraint_type = 'P')
ORDER BY column_id)
LOOP
EXECUTE IMMEDIATE 'SELECT COUNT(1) FROM '||l_table_name||' WHERE '||r_col.column_name||' IS NOT NULL'
INTO l_counter;
IF l_counter > 0 THEN
IF l_sql IS NULL THEN
l_sql := r_col.column_name;
ELSE
l_sql := l_sql||','||r_col.column_name;
END IF;
END IF;
END LOOP;
l_sql := 'SELECT '||l_sql||CHR(10)
||'FROM '||l_table_name;
----------
DBMS_OUTPUT.put_line(l_sql);
END;
What you're asking to do is establish a dependency on each row in the whole result. This is in fact not ever what you want. Just think of the ramifications if in one row every column had a value of '0' -- suddenly the schema of your result set grows to include all of those previously "empty" columns. You're effectively growing the badness of '*' exponentially, now your result set is not dependent on just the table's meta-data -- but your whole result set is dependent on the plain data.
What you want to do is just select the fields that have what you want, and not deviate from this simple plan.