Oracle select across a group of tables by partial name - oracle

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.

Related

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;

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;

How to determine row count for all tables in an Oracle Schema [duplicate]

I am trying to get the record counts of all tables in a schema. I am having trouble writing the PL/SQL. Here is what I have done so far, but I am getting errors. Please suggest any changes:
DECLARE
v_owner varchar2(40);
v_table_name varchar2(40);
cursor get_tables is
select distinct table_name,user
from user_tables
where lower(user) = 'SCHEMA_NAME';
begin
open get_tables;
fetch get_tables into v_table_name,v_owner;
INSERT INTO STATS_TABLE(TABLE_NAME,SCHEMA_NAME,RECORD_COUNT,CREATED)
SELECT v_table_name,v_owner,COUNT(*),TO_DATE(SYSDATE,'DD-MON-YY') FROM v_table_name;
CLOSE get_tables;
END;
This can be done with a single statement and some XML magic:
select table_name,
to_number(extractvalue(xmltype(dbms_xmlgen.getxml('select count(*) c from '||owner||'.'||table_name)),'/ROWSET/ROW/C')) as count
from all_tables
where owner = 'FOOBAR'
This should do it:
declare
v_count integer;
begin
for r in (select table_name, owner from all_tables
where owner = 'SCHEMA_NAME')
loop
execute immediate 'select count(*) from ' || r.table_name
into v_count;
INSERT INTO STATS_TABLE(TABLE_NAME,SCHEMA_NAME,RECORD_COUNT,CREATED)
VALUES (r.table_name,r.owner,v_count,SYSDATE);
end loop;
end;
I removed various bugs from your code.
Note: For the benefit of other readers, Oracle does not provide a table called STATS_TABLE, you would need to create it.
select owner, table_name, num_rows, sample_size, last_analyzed from all_tables;
This is the fastest way to retrieve the row counts but there are a few important caveats:
NUM_ROWS is only 100% accurate if statistics were gathered in 11g and above with ESTIMATE_PERCENT => DBMS_STATS.AUTO_SAMPLE_SIZE (the default), or in earlier versions with ESTIMATE_PERCENT => 100. See this post for an explanation of how
the AUTO_SAMPLE_SIZE algorithm works in 11g.
Results were generated as of LAST_ANALYZED, the current results may be different.
If you want simple SQL for Oracle (e.g. have XE with no XmlGen) go for a simple 2-step:
select ('(SELECT ''' || table_name || ''' as Tablename,COUNT(*) FROM "' || table_name || '") UNION') from USER_TABLES;
Copy the entire result and replace the last UNION with a semi-colon (';'). Then as the 2nd step execute the resulting SQL.
Get counts of all tables in a schema and order by desc
select 'with tmp(table_name, row_number) as (' from dual
union all
select 'select '''||table_name||''',count(*) from '||table_name||' union ' from USER_TABLES
union all
select 'select '''',0 from dual) select table_name,row_number from tmp order by row_number desc ;' from dual;
Copy the entire result and execute
You have to use execute immediate (dynamic sql).
DECLARE
v_owner varchar2(40);
v_table_name varchar2(40);
cursor get_tables is
select distinct table_name,user
from user_tables
where lower(user) = 'schema_name';
begin
open get_tables;
loop
fetch get_tables into v_table_name,v_owner;
EXIT WHEN get_tables%NOTFOUND;
execute immediate 'INSERT INTO STATS_TABLE(TABLE_NAME,SCHEMA_NAME,RECORD_COUNT,CREATED)
SELECT ''' || v_table_name || ''' , ''' || v_owner ||''',COUNT(*),TO_DATE(SYSDATE,''DD-MON-YY'') FROM ' || v_table_name;
end loop;
CLOSE get_tables;
END;

Row count of all tables in a schema

DECLARE
v_owner varchar2(40);
v_table_name varchar2(40);
cursor get_tables is
select distinct table_name
, user
from user_tables
where lower(user) = 'schema_name'
;
BEGIN
OPEN get_tables;
LOOP
FETCH get_tables
INTO v_table_name
, v_owner
;
EXIT WHEN get_tables%NOTFOUND;
EXECUTE IMMEDIATE
'INSERT
INTO STATS_TABLE
( TABLE_NAME
, SCHEMA_NAME
, RECORD_COUNT
, CREATED
)
SELECT '''
|| v_table_name
|| ''' , ''' || v_owner
|| ''' , COUNT(*)
, TO_DATE(SYSDATE,''DD-MON-YY'')
FROM ' || v_table_name
;
END LOOP;
CLOSE get_tables;
END;
I am using this to get row counts of all tables in a schema. I got this query from stackoverflow.
I ran this as a procedure its successfully compiled but i am unable to view the result y is it so?
I am new to pl/sql can anyone explain what does the select statement do after the excute immediate query I am unable to understand the logic behind.
Try this:
select TABLE_NAME, NUM_ROWS from dba_tables where owner = 'xxxxx'
You can simply get row count against tables by:
select owner, table_name, nvl(num_rows,-1)
from all_tables
order by nvl(num_rows,-1) desc
https://livesql.oracle.com/apex/livesql/file/content_EPJLBHYMPOPAGL9PQAV7XH14Q.html
The row counts are inserted into a table name STATS_TABLE. You need to run a
select *
from stats_table
after running the procedure
This procedure is running successfully
DECLARE
v_owner varchar2(40);
v_table_name varchar2(40);
cursor get_tables is
select distinct table_name, user
from user_tables
where lower(user) = 'coreown' ;
BEGIN
OPEN get_tables;
LOOP
FETCH get_tables
INTO v_table_name, v_owner;
EXIT WHEN get_tables%NOTFOUND;
EXECUTE IMMEDIATE 'INSERT
INTO STATS_TABLE
( TABLE_NAME
, SCHEMA_NAME
, RECORD_COUNT
, CREATED
)
SELECT ''' || v_table_name ||
''' , ''' || v_owner || ''' , COUNT(*)
, TO_DATE(SYSDATE,''DD-MON-YY'')
FROM ' || v_table_name;
END LOOP;
CLOSE get_tables;
END;
/
I tried at my end.
After executing USE COMMIT.So that it will save the change is DB.
Then check again.
Well, user_tables does not have a user column, so that query is invalid. Always test the queries in isolation -- you probably want all_tables or dba_tables. Also, do you think you need a DISTINCT on that query?
Use an implicit cursor instead of an explicit one wherever you can -- less code, less error prone in coding, and faster.
Do not store dates as character types -- you just insert sysdate, not a representation of it as a string.
Don't prefix variable names with "v_" -- just namespace them with the procedure or block name.
The Execute Immediate constructs a SQL statement that performs an insert into the stats_table (why does it have the word "table" in the name? That's redundant, surely?)
The query will be constructed as something like:
INSERT INTO STATS_TABLE
( TABLE_NAME
, SCHEMA_NAME
, RECORD_COUNT
, CREATED
)
SELECT 'MY_TABLE_NAME' , 'MY_USERNAME' , COUNT(*) , SYSDATE
FROM MY_TABLE_NAME;

Got an ORA-01788 error without using level or start with

I have just started to learn PL/SQL. Following a post in stackoverflow, I have written a script to search a certain value in Oracle as below:
DECLARE
match_count INTEGER;
v_owner VARCHAR2(255);
v_search_value NUMBER;
BEGIN
v_owner:='USERA USERB';
v_search_value:=4823.0;
EXECUTE IMMEDIATE
'INSERT INTO TMP SELECT owner, table_name, column_name FROM all_tab_cols WHERE instr(:1, owner)>0 AND data_type like ''%NUMBER%''' USING v_owner;
commit;
FOR t IN (SELECT owner, table_name, column_name FROM TMP) LOOP
EXECUTE IMMEDIATE
'SELECT COUNT(*) FROM '||t.owner||'.'||t.table_name||' WHERE '||t.column_name||'= :1'
INTO match_count
USING v_search_value;
IF match_count > 0 THEN
EXECUTE IMMEDIATE
'INSERT INTO RESULT VALUES(:1, :2, :3, :4)'
USING t.owner, t.table_name, t.column_name, match_count;
commit;
END IF;
END LOOP;
END;
The table TMP and RESULT have been properly created before executing the script. However, when I run the script, I get an ORA-01788 error saying that "connect by clause required" on line 12. I wonder why the code cause this error and how to change the script to be executed properly. Thanks!
You probably have a table with a column called level (as Florin suggested while I wasn't looking), which is a reserved word.
It may be in any case - not just level, but LEVEL, or Level - so to check you could do:
select owner, table_name, column_name
from all_tab_columns
where upper(column_name) = 'LEVEL';
Or to look for any reserved words in use (though this won't always find all potential problems):
select atc.owner, atc.table_name, atc.column_name, vrw.keyword
from all_tab_columns atc
join v$reserved_words vrw on vrw.keyword = upper(atc.column_name);
You could run similar queries against all_tables or all_objects to find potential problems elsewhere.
If I run a modified version of your query which doesn't bother with the TMP table - not sure why you'd want that; doesn't use dynamic SQL for the inserts; doesn't commit (which is an odd thing to do in a block - you don't need to do it after every insert anyway); and just for my benefit just displays the matches instead of needing to build a results table:
declare
match_count integer;
v_owner varchar2(255);
v_search_value number;
begin
v_owner := 'USERA USERB';
v_search_value := 4823.0;
for t in (
select owner, table_name, column_name
from all_tab_cols
where instr(v_owner, owner) > 0
and data_type like '%NUMBER%'
) loop
execute immediate
'select count(*) from ' || t.owner ||'.'|| t.table_name
|| ' where ' || t.column_name || ' = :1'
into match_count using v_search_value;
if match_count > 0 then
-- insert into results values (t.owner, t.table_name,
-- t.column_name, match_count);
dbms_output.put_line('Matched ' || t.owner ||'.'|| t.table_name
||'.'|| t.column_name ||': '|| match_count);
end if;
end loop;
end;
/
... this completes successfully. If I add a table with a level column:
create table t42 ("LEVEL" number);
... and then run the same block again then I also get:
ORA-01788: CONNECT BY clause required in this query block
ORA-06512: at line 14
01788. 00000 - "CONNECT BY clause required in this query block"
*Cause:
*Action:
Aside from avoiding reserved words in object names, you can make this work by enclosing all of your object names in double-quotes:
execute immediate
'select count(*) from "' || t.owner ||'"."'|| t.table_name
|| '" where "' || t.column_name || '" = :1'
into match_count using v_search_value;
This will also cope with mixed-case object names, which you should also avoid.

Resources