I am using Oracle sqlplus and am trying to use the result being selected from the codes below and select the column again from Table_1
SELECT *
FROM
(
SELECT Column_Name
FROM All_Tab_columns
WHERE Table_Name=UPPER('Table_1')
)
INTERSECT
(
SELECT Column_Name
FROM All_Tab_columns
WHERE Table_Name=UPPER('Table_2')
);
How can I perform something like that:
SELECT <Columns_That_Intersected>
FROM Table_1;
Is it possible to store the Columns_That_Intersected in a variable/function/procedure and use it again for other select statement?
Yes, it's possible. Just save the aggregated result of the query in a substitution variable. Something like this:
create table items1 as
select rownum id, 'me '||rownum name, 1 dummy
from xmlTable ('1 to 3');
create table items2 as select id, name from items1;
set verify off
col Columns_That_Intersected new_value Columns_That_Intersected noprint
select listagg (column_name, ',') within group (order by null) Columns_That_Intersected
from (
select column_name
from All_Tab_Columns
where Table_Name=UPPER('items1')
intersect
select column_name
from All_Tab_columns
where Table_Name=UPPER('items2')
);
prompt Columns_That_Intersected=&Columns_That_Intersected
select &Columns_That_Intersected
from items1;
Output:
Columns_That_Intersected=ID,NAME
ID NAME
---------- -------------------------------------------
1 me 1
2 me 2
3 me 3
About col[umn] command
Just to fetch the column names which exists in both the table table1 and table2, you can use below query:
SELECT Column_Name
FROM all_tab_columns t1
WHERE table_name = 'Table1'
AND EXISTS (SELECT 1
FROM all_tab_columns t2
WHERE table_name = 'Table2'
AND t2.Column_Name = t1.Column_Name);
Then to fetch these column values from Table1, you can use below PL/SQL construct:
DECLARE
v_sql_statement VARCHAR2(2000);
v_cols VARCHAR2(2000);
BEGIN
FOR cn IN (SELECT Column_Name
FROM all_tab_columns t1
WHERE table_name = 'Table1'
AND EXISTS (SELECT 1
FROM all_tab_columns t2
WHERE table_name = 'Table2'
AND t2.Column_Name = t1.Column_Name))
LOOP
v_cols := v_cols || ', ' || cn.column_name;
END LOOP;
v_cols := ltrim(v_cols, ',');
v_sql_statement := 'SELECT ' || v_cols || ' FROM Table1';
EXECUTE IMMEDIATE v_sql_statement;
END;
you can, for example, work with a dynamic sql.
The result of your selects can be aggregated to a list using the listagg function
SELECT listagg( Column_Name,',') WITHIN GROUP (ORDER BY 1) cols
FROM
(
SELECT Column_Name
FROM All_Tab_columns
WHERE Table_Name=UPPER('tab1')
INTERSECT
SELECT Column_Name
FROM All_Tab_columns
WHERE Table_Name=UPPER('tab2')
);
As result you get then eg. col1, col2, col3
with the help of this list you can then create a cursor in a plsql block and then loop through it to print out wat you want.
declare
v_cols VARCHAR2(2000);
v_select_string varchar(2000);
csr SYS_REFCURSOR;
v1 VARCHAR2(2000);
BEGIN
SELECT listagg( Column_Name,',') WITHIN GROUP (ORDER BY 1) cols
into v_cols
FROM
(
SELECT Column_Name
FROM All_Tab_columns
WHERE Table_Name=UPPER('tab1')
INTERSECT
SELECT Column_Name
FROM All_Tab_columns
WHERE Table_Name=UPPER('tab2')
);
v_select_string := 'SELECT ' || v_cols ||
' FROM tab1';
OPEN csr FOR v_select_string;
LOOP
FETCH csr INTO v1;
EXIT WHEN csr%NOTFOUND;
DBMS_OUTPUT.PUT_LINE( v1);
END LOOP;
CLOSE csr;
END P1;
Related
I need to 'recreate' over 50 tables (in Oracle) with CREATE AS SELECT statements. However, all this tables will have one column modified using data from another table. Is there a way to achieve this without declaring each column in the SELECT statement?
Something like:
CREATE TABLE table_name_copy AS SELECT *, (SELECT col_name FROM other_table WHERE other_table.col_id = table_name.col_id) AS col_name FROM table_name`
Basically on all tables I have a column which needs to be replaced with the data in the other_table column.
Generate the SQL string as such:
SELECT 'CREATE TABLE table_name_copy AS SELECT '
|| LISTAGG (column_name, ', ') WITHIN GROUP (ORDER BY column_name)
|| ', (SELECT col_name FROM other_table
WHERE other_table.col_id = table_name.col_id) AS col_name'
|| ' FROM table_name'
FROM all_tab_cols
WHERE owner = 'OWNER'
AND table_name = 'TABLE_NAME'
AND column_name != 'COL_NAME'
If you want to run the above statement, you could use EXECUTE IMMEDIATE:
DECLARE
v_sql VARCHAR2(10000);
BEGIN
SELECT 'CREATE TABLE table_name_copy AS SELECT '
|| LISTAGG (column_name, ', ') WITHIN GROUP (ORDER BY column_name)
|| ', (SELECT col_name FROM other_table
WHERE other_table.col_id = table_name.col_id) AS col_name'
|| ' FROM table_name'
INTO v_sql
FROM all_tab_cols
WHERE owner = 'OWNER'
AND table_name = 'TABLE_NAME'
AND column_name != 'COL_NAME';
EXECUTE IMMEDIATE v_sql;
END;
/
If col_id column is fixed for both of the joined tables,
you may use user_tab_columns and user_tables dictionary views through the schema to produce new tables named as "table_name_copy" by using the following mechanism :
declare
v_ddl varchar2(4000);
v_cln varchar2(400);
begin
for c in ( select *
from user_tables t
where t.table_name in
( select c.table_name
from user_tab_columns c
where c.column_name = 'COL_ID' )
order by t.table_name )
loop
v_ddl := 'create table '||c.table_name||'_copy as
select ';
for d in ( select listagg('t1.'||column_name, ',') within group ( order by column_name ) cln
from user_tab_columns
where table_name = c.table_name
and column_name != 'COL_ID' )
loop
v_cln := v_cln||d.cln;
end loop;
v_ddl := v_ddl||v_cln;
v_ddl := v_ddl||', t2.col_id t2_id
from '||c.table_name||' t1
left outer join other_table t2 on ( t1.col_id = t2.col_id )';
execute immediate v_ddl;
v_ddl := null;
v_cln := null;
end loop;
end;
Maybe you can use a simple join and an asterisk to return all columns from the first table, like that:
CREATE TABLE table_name_copy AS
SELECT * FROM (
SELECT tab1.*, tab2.column_name
FROM table_name tab1 LEFT JOIN other_table tab2 ON tab1.col_id = tab2.col_id
);
I would try this (but I don't have Oracle SQL to test on so please leave me the benefit of the doubt)
CREATE TABLE table_name_copy AS
SELECT * FROM (
SELECT *, (SELECT col_name FROM other_table WHERE other_table.col_id = table_name.col_id) as col_name
FROM table_name`
)
edit:
then run
ALTER TABLE table_name_copy DROP COLUMN <old column>
to remove the column you don't need any more
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;
I need a query/script to just display a distinct count of values in each column of a table. I'm using it to tie out to a legacy report where each column is a two way freq of the column by disctinct count. Something like below:
select distinct field1,count(*)
from EBL_CLIENT.EAP_FACT
where run_id = '205572'
select distinct field2,count(*)
from EBL_CLIENT.EAP_FACT
where run_id = '205572'
select distinct fieldetc...,count(*)
from EBL_CLIENT.EAP_FACT
where run_id = '205572'
Please find below script for generating SQL query:
declare
v_col varchar2(64) := 'run_id';
v_val varchar2(64) := '205572';
v_table varchar2(64) := 'EAP_FACT';
v_schema varchar2(64) := 'EBL_CLIENT';
begin
dbms_output.put_line('select *'||chr(10)||'from (select ');
for i in (select t.COLUMN_NAME, rownum rn
from all_tab_columns t
where t.TABLE_NAME = upper(v_table)
and t.OWNER = upper(v_schema)
and t.COLUMN_NAME <> upper(v_col)
order by t.COLUMN_ID)
loop
dbms_output.put_line(' '||case when i.rn=1 then ' ' else ',' end||
'count(distinct '||i.column_name||') '||i.column_name);
end loop;
dbms_output.put_line(' from '||v_schema||'.'||v_table||' t where t.'||v_col||' = '''||v_val||''')'
||chr(10)||'unpivot'||chr(10)||'(cnt');
for i in (select listagg (t.COLUMN_NAME,',') within group (order by t.COLUMN_ID) lst
from all_tab_columns t
where t.TABLE_NAME = upper(v_table)
and t.OWNER = upper(v_schema)
and t.COLUMN_NAME <> upper(v_col))
loop
dbms_output.put_line(' '||'for col in ('||i.lst||')');
end loop;
dbms_output.put_line(')'||chr(10)||'order by cnt desc');
end;
You will get some query like this:
select *
from (select
count(distinct t.field1) field1
,count(distinct t.field2) field2
,count(distinct t.field3) field3
from EBL_CLIENT.EAP_FACT t where t.run_id = '205572')
unpivot
(cnt
for col in (field1,field2,field3)
)
order by cnt desc
And after run this query result will be like this:
col cnt
field2 5
field1 3
field3 1
This would give the results for each column in a single row
select Field1Count = count(distinct field1)
,Field2Count = count(distinct field2)
,fieldetcCount = count(fieldetc)
from EBL_CLIENT.EAP_FACT
where run_id = '205572'
I don't know if this helps as it will still run 117 queries but you won't have to manually create them. Run the query which will return 117 select statements. Copy them and run them to get the counts.
SELECT 'SELECT ''' || COLUMN_NAME || ''' AS ColumnName , COUNT(DISTINCT '
|| COLUMN_NAME || ') AS Count FROM ' || Table_Schema || '.' || Table_Name
FROM INFORMATION_SCHEMA.columns
WHERE TABLE_SCHEMA = 'EBL_CLIENT'
AND TABLE_NAME = 'EAP_FACT'
sql query to find table_name and count(table_name) in a schema:
Example:
I get the table_name from this query:
SELECT * FROM USER_TABLES
and count(table_name) from
select count(*) from employee
select count(*) from dept
select count(*) from subjects
now i want to get the result like this:
table_name count(table_name)
Employee 100
dept 21
subjects 56
Try like this
select
table_name,
to_number(
extractvalue(
xmltype(
dbms_xmlgen.getxml('select count(*) c ' ||
' from '||owner||'.'||table_name))
,'/ROWSET/ROW/C')) count
from all_tables
where table_name In (
SELECT table_name from user_tables )
OR Using Join
select
a.table_name,
to_number(
extractvalue(
xmltype(
dbms_xmlgen.getxml('select count(*) c ' ||
' from '||owner||'.'||a.table_name))
,'/ROWSET/ROW/C')) count
from all_tables a JOIN user_tables u ON
a.table_name=u.table_name AND a.owner = user
Looks like a typical use case for a simple UNION ALL:
select
'Employee' table_name,
count(*) from employee
union all
select
'dept' table_name,
count(*) from dept
union all
select
'subjects' table_name,
count(*) from subjects
If you want to automate this, you can iterate over USER_TABLES:
declare
l_cnt pls_integer;
begin
for cur in (select table_name from user_tables order by table_name)
loop
execute immediate 'select count(*) from ' || cur.table_name into l_cnt;
dbms_output.put_line(cur.table_name || ' ' || l_cnt);
end loop;
end;
Instead of simply printing the result, you can also build a SQL statement dynamically and use that afterwards:
declare
l_sql varchar2(4000);
begin
for cur in (select table_name from user_tables order by table_name)
loop
l_sql := l_sql || ' select ' || cur.table_name || ' as table_name, count(*) as cnt from ' || cur.table_name || ' union all';
end loop;
-- remove trailing UNION ALL
l_sql := regexp_replace(l_sql, 'union all$', '');
dbms_output.put_line(l_sql);
end;
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;