Oracle Dynamic Query for all tables - oracle

I have two queries,
first one to get the table name and avg_row_len
select Table_name, AVG_ROW_LEN from all_tables
where Table_name = 'COMPU_ERROR_FACT';
and then i need to pass the avg_row_len to
SELECT COUNT(1) as "Number of Count",
ROUND(COUNT(1) * avg_row_len/1024/1024) AS "Size in MB" ,
TO_CHAR(CREATE_DATE, 'YYYY') AS YEAR,
'CREATE_DATE' AS DATTT
FROM COMPU_ERROR_FACT --table name
GROUP BY TO_CHAR(CREATE_DATE, 'YYYY')
order by 3;
I have more than 2k tables and is there a way to get the details of all tables at one-time execution.
Please suggest,

Not sure if you need just a query or a PLSQL block.
So, if you need a query, you may build it with the following PLSQL code. Run it and run huge sql from the output. Don't forget to remove trailing "union all"
begin
for t in (select * from all_tables) loop
dbms_output.put_line('select count(1) "number of count", ROUND(COUNT(1) * avg_row_len/1024/1024) "Size in MB", TO_CHAR(CREATE_DATE, ''YYYY'') AS YEAR, ''CREATE_DATE'' AS DATTT
FROM t.table_name
GROUP BY TO_CHAR(CREATE_DATE, ''YYYY'')
order by 3');
dbms_output.put_line('union all');
end loop;
end;
In case if PLSQL is acceptable you may use this
declare
cnt number;
avg_len number;
create_year varchar2(4);
dattt varchar(10); -- in youe example it will get constatly populated with 'CREATE_DATE', check if you need it at all
begin
for t in (select * from all_tables) loop
execute immediate 'select count(1) "number of count", ROUND(COUNT(1) * avg_row_len/1024/1024) "Size in MB", TO_CHAR(CREATE_DATE, ''YYYY'') AS YEAR, ''CREATE_DATE'' AS DATTT
FROM ' ||t.owner || '.' || t.table_name ||
' GROUP BY TO_CHAR(CREATE_DATE, ''YYYY'')
order by 3'
into cnt, avg_len, create_year, dattt;
-- do something with data you've just selected
end loop;
end;
And please note that all_tables has names of all tables you have access to. So there will be system tables and tables from another schemas. this is why I added "t.owner".
Perhaps you need user_tables view which has names of objects your user has created only. Check if using of "user_tables" system view will do better job here.
FYI: table size in MB may be obtained from dba_segments table:
select bytes / 1024 / 1024 from dba_segments where segment_name = 'TABLE_NAME'

Related

Why does this PL/SQL not find a table?

I have 2 table DOQUANGDUC1 and DOQUANGDUC, in table DOQUANGDUC list table in database I need check:
declare
BEGIN
FOR x IN (select table_name,column_name from DOQUANGDUC) LOOP
INSERT INTO DOQUANGDUC1 (table_name,column_name)
SELECT count(x.column_name),column_name FROM x.table_name GROUP BY column_name having count(x.column_name) >2;
END LOOP;
END;
I run sql error
Error at line 1
ORA-06550: line 9, column 57:
PL/SQL: ORA-00942: table or view does not exist
ORA-06550: line 8, column 10:
PL/SQL: SQL Statement ignored
I am sure two table already use.
PL/SQL: ORA-00942: table or view does not exist
The cause of the error message is this line:
FROM x.table_name
This is telling Oracle to query a table called table_name in a schema called X and that does not exist. Hence the failure.
I think what you're trying to do is loop through a list of tables and columns and count how many values each column has, and insert the results into another table. You can't do that with pure SQL, you need to use dynamic SQL to format the statement.
declare
stmt varchar2(32767);
begin
for x in (select table_name, column_name from doquangduc) loop
stmt := 'insert into doquangduc1(table_name, column_name, col_count) '
||' select '''||x.table_name||''', '''||x.column_name||''', count('||x.column_name||') '
||' from '||x.table_name
||' having count('||x.column_name||') > 2';
execute immediate stmt;
end loop;
end;
/
Here is a working demo on db<>fiddle.
Note that I have changed the insert statement to populate three columns in doquangduc1: correct this to fit your actual table structure (which you haven't posted).
It seems that the CMS_CIF_MUREX_NAME table has 292 records I add 1 duplicate record when running your command it shows the result column col_count = 293
My interpretation of your requirement is to count how many rows are populated for each column. Maybe that is not your actual requirement. Such misunderstandings are always a risk when you post a piece of code and expect us to reverse engineer your business logic. It is easier if you tell us what you're actually trying to achieve.
If you want counts for each distinct value in the column you can add a GROUP clause before the HAVING clause in my query.
'group by '||x.column_name ||
You may also want to add the value to the records inserted into doquangduc1, so you know which value has which count.
Approaches to Inserting into a Table through an Implicit Cursor or a Select Statement
Our Requirement:
We are going through the records in doquangduc and finding instances where the column_name appears more than twice.
Next, we are placing these records in another table, doquangduc1, while preserving the counts (placing them in doquangduc1).
Ultimately, these records are needed so they can be viewed.
Comments about this requirement:
It is not really clear that we really need to perform a transformation of the table, doquangduc, and then insert into another table to view the resulting records. This answer assumes this is necessary.
We create and execute DDL for tables needed first.
CREATE TABLE doquangduc
AS
SELECT
table_name,
column_name
FROM
user_tab_columns;
We create the table, doquangduc1, and make sure there is a column called col_count:
CREATE TABLE doquangduc1
AS
SELECT
table_name,
column_name,
1 col_count
FROM
user_tab_columns
WHERE
table_name IS NULL;
We sort through the doquangduc table to identify scenarios you have identified.
BEGIN
FOR x IN (
WITH qry AS (
SELECT
table_name,
column_name,
COUNT(1)
OVER(PARTITION BY column_name) AS col_count
FROM
user_tab_columns
)
SELECT
table_name,
column_name,
col_count
FROM
qry
WHERE
col_count > 2
) LOOP
INSERT INTO doquangduc1 (
table_name,
column_name,
col_count
) VALUES (
x.table_name,
x.column_name,
x.col_count
);
END LOOP;
END;
As various people have commented, do we really need an implicit cursor to perform this insert into the table, doquangduc1, so that we can view the results?
We could just perform a INSERT INTO with a SELECT statement to more efficiently insert these records into doquangduc1:
INSERT INTO doquangduc1
WITH qry AS (
SELECT
table_name,
column_name,
COUNT(1)
OVER(PARTITION BY column_name) AS col_count
FROM
user_tab_columns
)
SELECT
table_name,
column_name,
col_count
FROM
qry
WHERE
col_count > 2;

Data not inserting to destination table

I have the following block of PL-SQL code in Oracle:
DECLARE TAB VARCHAR(100);
COL VARCHAR(100);
CURSOR C_COLS IS
select DISTINCT table_name, column_name
from all_tab_columns
where OWNER = 'MyDB' AND DATA_TYPE LIKE '%VARCHAR%';
BEGIN
OPEN C_COLS;
LOOP
FETCH C_COLS INTO TAB, COL;
EXIT WHEN C_COLS%notfound;
INSERT INTO TargetTable (TABLE_NAME, COLUMN_NAME, COLUMN_VALUE)
SELECT DISTINCT TAB,
COL,
(SELECT COL FROM TAB)
FROM TAB
WHERE REGEXP_LIKE(COL, '([ABCDEFGHIJKLMNOPQRSTUVWXYZ])\d\d\d\d\d\d([ABCDEFGHIJKLMNOPQRSTUVWXYZ])', 'ix');
END LOOP;
CLOSE C_COLS;
END;
The idea is to determine which tables in my rather large database contain a certain pattern of data and to find them.
So I want to return three columns: TableName, ColumnName, Value of ColumnName.
The above runs but returns no data and I can't understand why. The query in the cursor returns results, and if I hard code the table values into a simple select statement containing my Regex, I get results. I just want one result set that contains the thousands of results I expect.
Could it be the (SELECT COL FROM TAB) I'm using to dynamically find the column_value? I wasn't sure if I could express it this way.
If you want to select columns dynamically you may wish to try dynamic SQL.
DECLARE
w_sql VARCHAR2(32767);
BEGIN
DBMS_OUTPUT.enable(32767);
FOR s_cols IN (
select DISTINCT
table_name
, column_name
from all_tab_columns
where owner = 'MyDB'
AND data_type LIKE '%VARCHAR%'
)
LOOP
w_sql := q'!
INSERT
INTO TargetTable (TABLE_NAME, COLUMN_NAME, COLUMN_VALUE)
SELECT DISTINCT
':TAB'
, ':COL'
, :COL
FROM :TAB
WHERE REGEXP_LIKE(:COL, '([ABCDEFGHIJKLMNOPQRSTUVWXYZ])\d\d\d\d\d\d([ABCDEFGHIJKLMNOPQRSTUVWXYZ])', 'ix')
!';
w_sql := REPLACE(w_sql, ':TAB', s_cols.table_name);
w_sql := REPLACE(w_sql, ':COL', s_cols.column_name);
EXECUTE IMMEDIATE w_sql;
END LOOP;
COMMIT;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.put_line('Error for SQL :'|| w_sql ||'; error is :'|| SQLERRM);
END;

Oracle Query to Sum up count from multiple tables

I need to take the count from multiple tables(1000+ Tables) and Sum up the same into a single value.
For example: Tables starting with TA .. Only these tables needs to be considered.
I want the actual counts and hence dont want to use DBA_TABLES/ALL_TABLES.
Please suggest me the best way to do this. I just need the single value of Sum of all the records in those tables.
Thanks
An example:
CREATE TABLE TA_1 AS select * FROM all_objects;
CREATE TABLE TA_2 AS select * FROM TA_1;
CREATE TABLE TA_abcd AS select * FROM TA_1;
select count(*) FROM TA_1;
COUNT(*)
----------
73701
DECLARE
partial_cnt NUMBER;
final_cnt NUMBER := 0;
BEGIN
FOR tab_name IN ( SELECT table_name FROM user_tables WHERE table_name LIKE 'TA%' )
LOOP
EXECUTE IMMEDIATE 'SELECT count(*) FROM '|| tab_name.table_name INTO partial_cnt;
final_cnt := final_cnt + partial_cnt;
END LOOP;
DBMS_OUTPUT.PUT_LINE( 'Total count = ' || final_cnt );
END;
/
Total count = 221103

Loop tables into pl/sql and display number of rows

I have loop for all the tables into db:
declare
V_TABL_NM ALL_TABLES.TABLE_NAME%TYPE;
BEGIN
FOR GET_TABL_LIST IN (SELECT TABLE_NAME FROM ALL_TABLES )LOOP
V_TABL_NM := GET_TABL_LIST.TABLE_NAME;
DBMS_OUTPUT.PUT_LINE(V_TABL_NM);
END LOOP;
END;
How can I sort my result and add number of records for each tables?
I try below but it does not work:
declare
V_TABL_NM ALL_TABLES.TABLE_NAME%TYPE;
table_row number;
BEGIN
FOR GET_TABL_LIST IN (SELECT TABLE_NAME FROM ALL_TABLES )LOOP
V_TABL_NM := GET_TABL_LIST.TABLE_NAME;
table_row: = select count(*) from TABLE_NAME;
DBMS_OUTPUT.PUT_LINE(V_TABL_NM, table_row);
END LOOP;
END;
You can not make a query that way; TABLE_NAME has no meaning there (and you're missing to use the cursor name), so you need to build a dynamic SQL and run it to put the value into a variable.
Besides, the PUT_LINE does not accept that parameters.
This should work:
DECLARE
table_row NUMBER;
BEGIN
FOR GET_TABL_LIST IN ( SELECT OWNER || '.' || TABLE_NAME AS TABLE_NAME
FROM ALL_TABLES
ORDER BY TABLE_NAME)
LOOP
EXECUTE IMMEDIATE 'select count(*) from ' || GET_TABL_LIST.TABLE_NAME INTO table_row;
DBMS_OUTPUT.PUT_LINE(GET_TABL_LIST.TABLE_NAME || ' - ' || table_row);
END LOOP;
END;
About the ordering, simply add an ORDER BY to the query looping through the tables
This assumes that you have rights to query all the tables listed in ALL_TABLES If you simply need to query all the tables of your schema, use USER_TABLES instead of ALL_TABLES.
to sort the results add order by clausel:
FOR GET_TABL_LIST IN
(
SELECT TABLE_NAME
FROM ALL_TABLES
ORDER BY TABLE_NAME --
)LOOP
to count the records use dynamic sql :
execute immediate 'select count(*) from ' || V_TABL_NM INTO table_row;
This can be done completely without PL/SQL:
select table_name,
to_number(extractvalue(xmltype(dbms_xmlgen.getxml('select count(*) c from '||table_name)),'/ROWSET/ROW/C')) as rowcount
from user_tables
order by rowcount;
also I did:
select TABLE_NAME, NUM_ROWS, LAST_ANALYZED
from user_tables
order by 1;

Oracle select across a group of tables by partial name

I'm trying to select information from a group of tables. Originally, I had
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 like 'PAY0%' OR table_name like 'PAY1%'
Then looped through all the tables in my code using
foreach(table_name in tables){
SELECT CUST_NUMBER
FROM #table_name#
}
Now I'm trying to combine the two by doing the following in order to get the cust_number from every table, but it's saying '"CUST_NUMBER": invalid identifier'
select CUST_NUMBER
from ( 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 like 'PAY0%' OR table_name like 'PAY1%') PayTables
I know the problem is the first query gets table names and not the data, but I've no idea how to get the data as well without looping through them seperately.
If I correctly understand your need, something like the following could help:
declare
vSQL varchar2(32767);
type tabNum is table of number;
vResult tabNum;
begin
select listagg( 'select cust_number from ' || owner || '.' || table_name,
' UNION ALL '
) within group ( order by null)
into vSQL
from dba_tables
where table_name like 'PAY0%' OR table_name like 'PAY1%';
--
dbms_output.put_line(vSQL);
--
execute immediate vSQL bulk collect into vResult;
--
for i in vResult.first .. vResult.last loop
dbms_output.put_line(vResult(i));
end loop;
end;
It dynamically builds an SQL statement that extracts values from all the tables matching you criteria; in the example I run the statement fetching the result into a structure, but you can open a cursor, or do whatever you need.

Resources