select data of specific column from table where column names are returned by query - sql

I have written 2 separate queries
1)
SELECT COLUMN_NAME
FROM ALL_TAB_COLUMNS
WHERE TABLE_NAME =
(SELECT DISTINCT UT.TABLE_NAME
FROM USER_TABLES UT
WHERE UT.TABLE_NAME = 'MY_TABLE')
AND COLUMN_NAME NOT IN ('AVOID_COLUMN')
2)
SELECT *
FROM MY_TABLE MT
WHERE MT.COL1 = '1'
The 1st query returns the names of all the columns except the one I want to avoid. The 2nd one returns data of all the columns from the table.
Is there some way to merge these queries so that only those column's data is selected from the 2nd query, which are returned from the 1st query?
Thanks in advance

You'll have to use dynamic SQL for this (BTW, I got rid of the subselect for the USER_TABLES query - it's unnecessary):
var cur refcursor
/
declare
v_stmt varchar2(4000);
begin
v_stmt := 'SELECT ';
for cur in (
SELECT COLUMN_NAME
FROM ALL_TAB_COLUMNS
WHERE TABLE_NAME =
'MY_TABLE'
AND COLUMN_NAME NOT IN ('AVOID_COLUMN')
)
loop
v_stmt := v_stmt || cur.column_name || ',';
end loop;
-- get rid of trailing ','
v_stmt := regexp_replace(v_stmt, ',$', '');
v_stmt := v_stmt || ' from my_table MT WHERE MT.COL1 = ''1''';
dbms_output.put_line(v_stmt);
open :cur for v_stmt;
end;

Related

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;

ORACLE SQL CURSOR / FOR LOOP

I need to verify converted data, distinct values and records counts. I would like to write statements so that I can enter a table name, then retrieve it's columns and use them in a query to get its distinct values (the actual values, not just a count of how many distinct) and their count.
I think I need to a CURSOR or CURSOR FOR LOOP and create something like this:
declare
cursor field_name
is
select COLUMN_NAME
from user_tab_cols
where table_name='TABLE1'
c_field_name field_name%ROWTYPE;
BEGIN
OPEN field_name
loop
fetch field_name INTO c_field_name;
exit when field_name%NOTFOUND;
end loop;
CLOSE field_name;
end;
Then run a query using that above in something like
select field_name, count(*)
from table1
group by field_name
Do I need to create 2 loop statements? I've not yet created one and can't quite get the context to get my results so far.
BEGIN
FOR myrow in (select field_name, count(*) as "count" from table1 group by field_name)
loop
dbms_output.put_line(myrow.field_name);
dbms_output.put_line(myrow.count);
end loop;
end;
Considering you will be giving the table name as parameter below code will print all the values of all the columns one by one along with the count of the values
create or replace PROCEDURE PR_PREP(
P_TABLE_NAME IN VARCHAR2)
IS
CURSOR CUR_COLUMNS (PA_TABLE_NAME VARCHAR2)
IS
SELECT COLUMN_NAME FROM ALL_TAB_COLUMNS WHERE TABLE_NAME = PA_TABLE_NAME;
COL_NAMES CUR_COLUMNS%ROWTYPE;
TYPE TYP_RECORD
IS
RECORD
(
FIELD_NAME VARCHAR2(255),
CNT INT);
TYPE TYP_OP_TABLE
IS
TABLE OF TYP_RECORD;
OP_TABLE TYP_OP_TABLE;
I INT;
V_SQL VARCHAR2(2000);
BEGIN
FOR COL_NAMES IN CUR_COLUMNS(P_TABLE_NAME)
LOOP
V_SQL := 'SELECT ' || COL_NAMES.COLUMN_NAME || ' AS FIELD_NAME ,
COUNT(*) AS CNT FROM ' ||
P_TABLE_NAME || ' GROUP BY ' || COL_NAMES.COLUMN_NAME ;
-- DBMS_OUTPUT.PUT_LINE (V_SQL);
EXECUTE IMMEDIATE V_SQL BULK COLLECT INTO OP_TABLE;
dbms_output.put_line('columna name = ' ||COL_NAMES.COLUMN_NAME);
FOR I IN OP_TABLE.FIRST .. OP_TABLE.LAST
LOOP
DBMS_OUTPUT.PUT_LINE('FIELD VALUE '||OP_TABLE(I).FIELD_NAME || ' COUNT = ' || OP_TABLE(I).CNT);
END LOOP;
DBMS_OUTPUT.PUT_LINE('ONE FILED ENDED , NEXT STARTED');
END LOOP;
END;

Iterate through row's columns

I have table with 100 columns with not correlated names (ABC1, DA23, EE123 - there is no common pattern there).
I want to iterate through every row and every column in this table.
My current script:
BEGIN
FOR single_row IN (
SELECT *
FROM MY_TABLE)
LOOP
--iterate through columns of 'single_row'
--for each nullable column do insert with real current column name and column value)
--I assume each column is nullable except of ID
INSERT INTO ANOTHER_TABLE VALUES (single_row.id, column_name, column_value);
END LOOP;
END;
So for example, if MY_TABLE contains 2 rows:
ID|ABC1|DA23|EE123|...
1|123|456|789|...
2|321|654|987|...
After running my script, my ANOTHER_TABLE will contain:
MY_TABLE_ID|COLUMN_NAME|COLUMN_VALUE
1|ABC1|123
1|DA23|456
1|EE123|789
... other columns from row 1
2|ABC1|321
2|DA23|654
2|EE123|987
... other columns from row 2
How I can do this?
I'm using Oracle 11g
EDIT
#vkp provided great solution, but there is one more thing to solve. I don't want to specify all columns in in clause. I would love to use some kind of query there or * or anything else, just to not be forced to list all of them.
I have tried something like this:
select *
from MY_TABLE t
unpivot (
column_value for column_name in (select column_name
from user_tab_columns
where table_name = 'MY_TABLE'
and nullable = 'Y')
) u
but it returns error:
ORA-00904: : invalid identifier
00904. 00000 - "%s: invalid identifier"
This is an application of unpivot.
select *
from my_table m
unpivot (column_value for column_name in (ABC1,DA23,EE123)) u
null values for any of the columns for an id won't be shown in the result.
If you have to include null values in the output, use the option INCLUDE NULLS.
select *
from my_table m
unpivot include nulls (column_value for column_name in (ABC1,DA23,EE123)) u
Edit: To include column names dynamically, use
DECLARE
sql_stmt VARCHAR2(4000);
var_columns VARCHAR2(4000); --use clob datatype if the column names can't fit in with this datatype
BEGIN
SELECT LISTAGG(column_name,',') WITHIN GROUP(ORDER BY column_name)
INTO var_columns
FROM user_tab_columns
WHERE table_name='MY_TABLE' AND column_name<>'ID';
sql_stmt:='select * from my_table m
unpivot
(column_value for column_name in (' || var_columns || ')) u';
EXECUTE IMMEDIATE sql_stmt;
END;
/
First option. With dynamic sql.
declare
v_ctx number;
v_query varchar2(500);
v_total NUMBER;
v_desctab DBMS_SQL.DESC_TAB;
v_column_cnt NUMBER;
v_value varchar2(32767);
v_result clob := '';
v_rownum number := 0;
begin
v_ctx := dbms_sql.open_cursor;
v_query := 'select * from user_objects where rownum < 100';
dbms_sql.parse(v_ctx,v_query,dbms_sql.v7);
v_total := dbms_sql.execute(v_ctx);
DBMS_SQL.DESCRIBE_COLUMNS(v_ctx, v_column_cnt, v_desctab);
for i in 1 .. v_column_cnt loop
dbms_sql.define_column(v_ctx, i, v_value /* data_type varchar2*/, 32767 /* max_length*/);
end loop;
loop
exit when dbms_sql.fetch_rows(v_ctx) = 0;
v_rownum := v_rownum +1;
for i in 1 .. v_column_cnt loop
dbms_sql.column_value(v_ctx, i, v_value);
dbms_output.put_line(v_rownum||' - '||v_desctab(i).col_name||' - '||v_value);
end loop;
end loop;
dbms_sql.close_cursor(v_ctx);
exception
when others then
dbms_sql.close_cursor(v_ctx);
raise;
end;
/
2nd option with xquery.
select t1.id,t2.* from xmltable('for $i in ora:view("<you_table_here>")/ROW
return $i'
columns id FOR ORDINALITY
, row_value xmltype path'.'
) t1
,xmltable('for $i in $row_value/ROW/* return $i'
passing t1.row_value as "row_value"
columns col_index for ORDINALITY ,
column_name varchar2(100) path 'name()',
column_value varchar2(100) path 'text()'
) t2
Here is a simple solution using REF CURSOR.
I've tried this code and it's working at my end.
DECLARE
query_2 VARCHAR2(1000);
TYPE icur IS REF CURSOR;
ic icur;
col_val VARCHAR2(100);
BEGIN
FOR j IN
(SELECT * FROM user_tab_cols WHERE table_name = UPPER('MY_TABLE'))
LOOP
dbms_output.put_line(j.column_name);
query_2 := 'SELECT ' || j.column_name|| ' FROM MY_TABLE';
OPEN ic FOR query_2;
LOOP
FETCH ic INTO col_val;
EXIT WHEN ic%NOTFOUND;
INSERT INTO ANOTHER_TABLE VALUES( j.column_name, col_val);
END LOOP;
END LOOP;
END;
/

Count the number of rows in a schema based on a where condition

I have engine_id column in various tables in the schema. So I want to count the number of rows based on engine_id column in the whole schema where this column exists.
Select count(*)
from table_name
where table_name.engine_id = 8;
Without user-defined PL/SQL, using only built-in Oracle functionality...
Oracle 11g (and maybe even lower) query:
select TC.table_name, X.*
from user_tab_columns TC
cross join xmltable(
'/ROWSET/ROW/CNT'
passing
dbms_xmlgen.getXMLType('
select count(1) as cnt
from '||TC.table_name||'
where &columnName = &columnValueAsLiteral
')
columns
cnt integer
) X
where TC.column_name = '&columnName'
;
Oracle 12c+ query:
select TC.table_name, X.*
from user_tab_columns TC
cross apply xmltable(
'/ROWSET/ROW/CNT'
passing
dbms_xmlgen.getXMLType('
select count(1) as cnt
from '||TC.table_name||'
where &columnName = &columnValueAsLiteral
')
columns
cnt integer
) X
where TC.column_name = '&columnName'
;
In your case supply
&columnName as ENGINE_ID,
&columnValueAsLiteral as 8.
Note: It could also be possible with the with-PLSQL clause of 12c's but I somehow can't make it work, hence I'm not posting that solution here.
These are the steps which you should be following
Get all the tables which have the column.
select table_name from all_tab_columns
where column_name = 'ENGINE_ID';
Create the above as a cursor and run a loop on it
for records in above_cursor
loop
execute immediate 'select count(*) from ' || records.table_name || 'where engine_id = 8' into some_temp_number_var;
some_total_number_var := some_temp_number_var + some_total_number_var;
end loop;
You will need PL code that would query the data dictionary view USER_TABLES to find out which tables have the column you seek and then build a dynamic SQL query as per your filter requirements.
Your function would be something like this:
create or replace function find_count (p_column_name varchar2,
p_column_value number)
return number is
v_sql clob; HERE
v_count number;
begin
for i in (select table_name
from user_tab_cols
where column_name = upper(v_column_name)) loop
v_sql :=
v_sql
|| 'select count(*) as cnt from '
|| i.table_name
|| ' where '
|| p_column_name
|| ' = '
|| p_column_value;
v_sql := v_sql || chr (10) || 'union all ';
end loop;
v_sql := substr (v_sql, 1, length (v_sql) - 11);
v_sql := 'select sum(cnt) from (' || v_sql || ')';
execute immediate v_sql into v_count;
return v_count;
end find_count;
/
You can trigger this function using a query like this:
select find_count('ENTITY_ID', 1012) as engine_count from dual;

Query for particular integer value from multiple columns with number datatype

I try to search a number from multiple columns (datatype number), but get ORA-01722: invalid number error.
My Query:
SELECT *
FROM CAMPAIGN
WHERE 1481125 IN (select column_name
from all_tab_columns
where table_name = 'CAMPAIGN'
AND data_type = 'NUMBER');
What is wrong with it?
You are comparing your number 1481125 with the names of the each column, not the values of each column in your table.
To go from a column's name (from dba_tab_columns) to the values in that column, you need to use some form of dynamic SQL. Here's a relatively simple example:
DECLARE
-- Since I don't have your CAMPAIGN table or data, I'm using DBA_OBJECTS in it's place.
l_table_name VARCHAR2 (30) := 'DBA_OBJECTS';
l_search_number NUMBER := 20; -- 1481125 in your example
l_record dba_objects%ROWTYPE;
l_sql VARCHAR2 (32000);
l_column_number NUMBER := 0;
l_cur SYS_REFCURSOR;
BEGIN
-- First: build dynamic SQL statement of the form:
-- SELECT * FROM table_name WHERE
-- ( ( col_name_a = 20 ) OR ( col_name_b = 20 ) OR ... )
l_sql := 'SELECT * FROM dba_objects WHERE ( ';
FOR r_number_column IN (SELECT column_name
FROM dba_tab_columns
WHERE table_name = l_table_name
AND data_type = 'NUMBER'
ORDER BY column_id) LOOP
IF l_column_number > 0 THEN
l_sql := l_sql || ' OR ';
END IF;
l_column_number := l_column_number + 1;
l_sql := l_sql || '(' || r_number_column.column_name || ' = ' || l_search_number || ')';
END LOOP;
IF l_column_number = 0 THEN
-- No number columns in table, so there should be no matches
l_sql := l_sql || ' 1=0';
END IF;
l_sql := l_sql || ')';
DBMS_OUTPUT.put_line (l_sql);
OPEN l_cur FOR l_sql;
LOOP
FETCH l_cur INTO l_record;
EXIT WHEN l_cur%NOTFOUND;
DBMS_OUTPUT.put_line ('Object Name ' || l_record.object_name || ' has search number ' || l_search_number);
END LOOP;
END;
Your query is:
SELECT * FROM CAMPAIGN WHERE 1481125 IN
(select column_name from all_tab_columns where table_name = 'CAMPAIGN' AND data_type='NUMBER')
Breaking that down we have:
SELECT * FROM CAMPAIGN WHERE 1481125 IN (<a set of numbers>)
and the subquery:
select column_name from all_tab_columns
where table_name = 'CAMPAIGN'
AND data_type='NUMBER'
That subquery is going to return a list of column names e.g.
CAMPAIGN_COUNT
CAMPAIGN_ID
CAMPAIGN_NUMBER_OF_SOMETHINGS
Your query is thus equivalent to:
SELECT * FROM CAMPAIGN WHERE 1481125 IN
('CAMPAIGN_COUNT', 'CAMPAIGN_ID', 'CAMPAIGN_NUMBER_OF_SOMETHINGS')
You can see why you would get the ORA-01722 error there?
You would need to write dynamic SQL to achieve your aim.