sql query to find table_name and count(table_name) in a schema - sql

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;

Related

How to Get max created date for tables selected from all_tab_columns

I have selected (from all_tab_columns) some tables and columns of DATE datatype. I would like to know the MAX date for each table. Could you help me out with a dynamic way to do it? Table names and column names could be different depending on my where clause when selecting from all_tab_columns.
Sample data:
WITH
tabs (TABLE_NAME, COLUMN_NAME, DATA_TYPE) AS
(
Select 'A_ZR_6', 'CREATED_DATE', 'DATE' From dual Union All
Select 'A_ZR_8', 'CREATEDDATE', 'DATE' From dual Union All
Select 'A_ZR_2', 'CREATED_DATE', 'DATE' From dual Union All
Select 'A_ZR_4', 'CREATED_DATE', 'DATE' From dual Union All
Select 'A_ZR_9', 'CREATED_DATE', 'DATE' From dual
)
TABLE_NAME
COLUMN_NAME
DATA_TYPE
A_ZR_6
CREATED_DATE
DATE
A_ZR_8
CREATEDDATE
DATE
A_ZR_2
CREATED_DATE
DATE
A_ZR_4
CREATED_DATE
DATE
A_ZR_9
CREATED_DATE
DATE
The expected result should look like here:
TABLE_NAME
MAX_DATE
A_ZR_6
07-NOV-22
A_ZR_8
12-DEC-22
A_ZR_2
03-OCT-22
A_ZR_4
01-NOV-22
A_ZR_9
31-DEC-22
CODE:
select
table_name,
column_name
from
all_tab_columns
where
owner='ABC' and
table_name not like 'V_%' and
lower(column_name) like '%create%' and
lower(column_name) like '%date%'
group by
table_name,
column_name
I believe that the only method that you can use is a PL/SQL program because the name of table in a SQL statement Oracle cannot dynamic:
SET LINE 100;
SET SERVEROUTPUT ON;
DECLARE
CURSOR QCursor is
SELECT
X.table_name,
'SELECT MAX(' || X.column_name || ') from ' || owner || '.' || X.table_name as query
FROM (
SELECT
owner,
table_name,
column_name
FROM
all_tab_columns
WHERE
owner = 'ABC'
AND table_name not like 'V_%'
AND lower(column_name) like '%create%'
AND lower(column_name) like '%date%'
GROUP BY owner,
table_name,
column_name
) X;
--# RIGA DEL CURSORE CONTROLLO
ROW_QCursor QCursor%ROWTYPE;
v_max_date DATE;
BEGIN
DBMS_OUTPUT.ENABLE(1000000);
OPEN ROW_QCursor;
LOOP
FETCH QCursor INTO ROW_QCursor;
EXIT WHEN QCursor%NOTFOUND;
EXECUTE IMMEDIATE ROW_QCursor.query INTO v_max_date;
DBMS_OUTPUT.PUT_LINE('Table_name :' || ROW_QCursor.table_name || ' Max date is : ' || v_max_date);
END LOOP;
CLOSE ROW_QCursor;
END;
/
Alternatively, if the query is manually (es. Sql Developer) you can run this query:
select
X.table_name,
'SELECT MAX(' || X.column_name || ') from ' || owner || '.' || X.table_name as query
from (
select
owner,
table_name,
column_name
from
all_tab_columns
where
owner = 'ABC'
and table_name not like 'V_%'
and lower(column_name) like '%create%'
and lower(column_name) like '%date%'
group by owner,
table_name,
column_name
) X;
and then copy the output and execute manually:
TABLE_ QUERY
------ ----------------------------------------
A_ZR_2 SELECT MAX(CREATED_DATE) from ABC.A_ZR_2
A_ZR_9 SELECT MAX(CREATED_DATE) from ABC.A_ZR_9
A_ZR_8 SELECT MAX(CREATED_DATE) from ABC.A_ZR_8
A_ZR_6 SELECT MAX(CREATED_DATE) from ABC.A_ZR_6
A_ZR_4 SELECT MAX(CREATED_DATE) from ABC.A_ZR_4
I hope I was helpful.
Thank you.

sqlplus SELECT from the selected result

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;

CREATE AS SELECT * but with one column obtained from another table

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

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;

plsql List all table.column containing null values

I'd like to find all column of a set of table with null values in them.
I can find the table and column names
SELECT TABLE_NAME, COLUMN_NAME
FROM user_tab_cols
where nullable='Y'
and table_name in ('myTalbe', 'myTable2');
And also check if the are nulls
select count(*) from myTable where myColumn is null;
but how can I put this toghether to have as result
table_name column_name
myTable myColumn
myTable myCol2
myTable2 col4
An approach could be with some dynamic SQL, but this would require some PL/SQL code; the following only uses SQL to get the result you need:
select *
from (
select table_name,
column_name,
to_number(extractvalue(xmltype(dbms_xmlgen.getxml('select count(*) c from '||table_name || ' where ' || column_name || ' is null')),'/ROWSET/ROW/C')) as rowcount
from user_tab_columns
where nullable='Y'
and table_name in ('myTalbe', 'myTable2')
)
where rowcount > 0
This could be an approach with dynamic SQL:
declare
type tableOfNumber is table of number;
type tableOfChar is table of varchar2(30);
--
vSQl varchar2(4000);
vListNumbers tableOfNumber;
vListTables tableOfChar;
vListColumns tableOfChar;
begin
select listagg( 'select ''' ||
table_name || ''' as table_name, ''' ||
column_name || ''' as column_name, count(*) as rowCount from ' ||
table_name ||
' where ' ||
column_name ||
' is null having count(*) > 0' ,
' UNION ALL '
) within group ( order by table_name, column_name)
into vSQL
from user_tab_columns
where nullable='Y'
and table_name in ('myTalbe', 'myTable2');
--
dbms_output.put_line(vSQL);
/* whatever you may want to do with the query */
/* for example, fetch into some variables and print the result */
execute immediate vSQL
bulk collect into vListTables, vListColumns, vListNumbers;
--
if vListTables.count() > 0 then
for i in vListTables.first .. vListTables.last loop
dbms_output.put_line('Table ' || vListTables(i) ||
', column ' || vListColumns(i) ||
', number of nulls: ' || vListNumbers(i)
);
end loop;
end if;
end;
Here's a routine I wrote a while back to do exactly that. It will output the required DDL to make those columns that are nullable (but do not contain any nulls) not nullable.
https://connormcdonald.wordpress.com/2016/03/11/tightening-up-your-data-model/
It can do the task for a schema or a single table.