Query to find all empty tables - oracle

Considering that I have a Schema named SBST
I want to find all empty tables list in this SBST Schema. Is there any PL/SQL procedure to find that. I found few. But those were using user tables where I was not able specify the Schema name SBST.
I was using this
select table_name from dba_tables where owner ='SBST'
having count(*)=0 group by table_name
What's wrong in the above query?

You can fire below query to find the list of tables haveing no data
select * from ALL_TABLES
where owner ='SBST'
AND NUM_ROWS = 0;

Similar to #shareef's answer, but using dynamic SQL to avoid having to create the temporary .sql file. You'll need dbms_output to be visible, e.g. with set serveroutput on in SQL*Plus - don't know about Toad.
declare
cursor c(p_schema varchar2) is
select 'select ''' || table_name || ''' from ' ||
p_schema ||'.' || table_name || ' where rownum < 2 ' ||
' having count(*) = 0' as query
from all_tables
where owner = p_schema
order by table_name;
l_table all_tables.table_name%TYPE;
begin
for r in c('SBST') loop
begin
execute immediate r.query into l_table;
exception
when no_data_found then continue;
end;
dbms_output.put_line(l_table);
end loop;
end;
/
Using all_tables seems more useful than dba_tables here so you know you can select from the tables it lists. I've also included the schema in the from clause in case there other users have tables with the same name, and so you can still see it if you're connected as a different user - possibly avoiding synonym issues too.
Specifically what's wrong with your query... you've got the having and group by clauses the wrong way around; but it will always return no data anyway because if SBST has any tables then count (*) from dba_tables must be non-zero, so the having always matches; and if it doesn't then, well, there's no data anyway so there's nothing for the having to match against. You're counting how many tables there are, not how many rows are in each table.

the straight forward answer is
select 'select ''' || table_name || ''' from ' || table_name || '
having count(*) = 0;' from dba_tables where owner='SBST';
EXPLANATION
You can run this... it will just output the table names of those having no rows:
assuming you use sqlplus but i used toad to test it
and it worked very well
set echo off heading off feedback off lines 100 pages 0;
spool tmp.sql
select 'select ''' || table_name || ''' from ' || table_name || '
having count(*) = 0;' from user_tables where owner='SBST';
spool off;
#tmp.sql
If you open the "tmp.sql" file, you'll see for all tables....
select 'PERSONS' from PERSONS having count(*) = 0;
select 'DEPARTMENT' from DEPARTMENT having count(*)=0;
in your case you want a schema and schema is a user right the above code if you connect with the user SBST but if you connect with other then you have to use DBA_TABLES and assign owner attribute to SBST
USER_TABLES is tables which you own ALL_TABLES is tables which own,
and tables owner by other users, which you have been granted excplicit
access to DBA_TABLES is all tables in the database
like this
set echo off heading off feedback off lines 100 pages 0;
spool tmp.sql
select 'select ''' || table_name || ''' from ' || table_name || '
having count(*) = 0;' from dba_tables where owner='SBST';
spool off;
#tmp.sql
All three are views of the underlying SYS tables, but the USER_ and
ALL_ views joing in your username/security info to limit the results
**SUMMARY **
PLEASE JUST RUN THIS QUERY
select 'select ''' || table_name || ''' from ' || table_name || '
having count(*) = 0;' from dba_tables where owner='SBST';

If table stats are up to date then you could use:
SELECT TABLE_NAME
FROM ALL_TAB_STATISTICS
WHERE (OWNER = 'ME')
AND (NUM_ROWS = 0);

Related

Is it possible to drop tables using regular expressions instead of specyfing the string names individually? (Oracle)

I am using an oracle database. In the database I have I have a lot of tables. The names start with "temp_....": e.g. TEMP_EXP1, TEMP_EXP2, Is it possible to drop all of them at once with one query, e.g. by specifying a regular expression?
You can't drop table with a query. You must use DROP TABLE xxxx DDL.
But you can write a query that produces the required DROPstatements, such as
select 'DROP TABLE '||owner||'.'|| table_name||';' as ddl
from dba_tables where
owner = 'xx' and
table_name <<< put here your REGEXP filter
Than copy the produced DROP statements and execute them in you IDE or SQL*Plus.
In case you have no access to the dictionary view DBA_TABLES and you drop the tables in a schema you are connected use this statement
select 'DROP TABLE '|| table_name||';' as ddl
from user_tables
where
table_name <<< put here your REGEXP filter
You can use some dynamic SQL to drop your tables; for example:
begin
for i in 1..3 loop
execute immediate 'drop table TEMP_EXP' || i;
end loop;
end;
You could loop through the tables and drop it using EXECUTE IMMEDIATE.
DBMS_OUTPUT is added for displaying the name of the table being dropped. Be careful before running!
SET SERVEROUTPUT ON
EXEC dbms_output.enable(NULL);
BEGIN
FOR t IN (SELECT owner, table_name
FROM all_tables
WHERE REGEXP_LIKE (table_name, 'TEMP_EXP\d+')) --or any other REGEX that suits you.
LOOP
DBMS_OUTPUT.PUT_LINE (
'DROPPING TABLE ' || t.owner || '.' || t.table_name);
EXECUTE IMMEDIATE 'DROP TABLE ' || t.owner || '.' || t.table_name;
END LOOP;
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;

Oracle 11g temporary list object from query

I'm trying to find if there is a way in Oracle 11g, that I can store the resulting list from a SELECT into a variable (object) and then loop through that list performing a second query on the results?
Basically what I am trying to do, is get a list of all tables with a column name, then removing certain data from them tables.
Something like:
var PRODUCTID_TABLE = SELECT table_name
FROM user_tab_columns
WHERE column_name = 'PRODUCT_ID'
AND table_name NOT LIKE 'BIN%';
FOR T IN PRODUCTID_TABLE LOOP
DELETE FROM T.TABLE_NAME WHERE PRODUCT_ID = {value};
END LOOP;
COMMIT;
Thanks in advance
KS
You can always generate the DELETE statements like this:
SELECT 'DELETE FROM ' || table_name ||
' WHERE PRODUCT_ID = {value}; '
FROM user_tab_columns
WHERE column_name = 'PRODUCT_ID' AND table_name NOT LIKE 'BIN%';
Or, if using PL/SQL is an option, you can use EXECUTE IMMEDIATE inside a PL/SQL block:
BEGIN
FOR v_rec IN (SELECT table_name
FROM user_tab_columns
WHERE column_name = 'PRODUCT_ID'
AND table_name NOT LIKE 'BIN%')
LOOP
EXECUTE IMMEDIATE 'DELETE FROM ' || v_rec.table_name ||
' WHERE PRODUCT_ID = {value}; ';
END LOOP;
END;

Drop Schema in Oracle: SQL Error: No more data to read from socket

I'm trying to drop a schema in oracle 11g on our dev environment and I get back SQL Error: No more data to read from socket. There is no load on the schema as it's just a dev db. It's a small db without anything crazy going on. I see this error all the time. Restarting the instance sometimes resolves the problem. I can't seem to find any information that would point to a solution. Thanks!
I understand that this message often arises due to a bug. Also, when it appears an entry in your alert log and/or a trace file will contain more detail on what the error might actually be. To find your trace file for the session run:
select U_DUMP.value
|| '/'
|| DB_NAME.value
|| '_ora_'
|| V$PROCESS.SPID
|| nvl2(V$PROCESS.TRACEID, '_' || V$PROCESS.TRACEID, null)
|| '.trc'
"Trace File"
from V$PARAMETER U_DUMP
cross join V$PARAMETER DB_NAME
cross join V$PROCESS
join V$SESSION
on V$PROCESS.ADDR = V$SESSION.PADDR
where U_DUMP.NAME = 'user_dump_dest'
and DB_NAME.NAME = 'db_name'
and v$session.audsid=sys_context('userenv','sessionid');
A dba at my company gave me this one. It's
CREATE OR REPLACE PROCEDURE "SYS"."DROP_SCHEMA_FAST" (pSchema IN
VARCHAR2)
IS
cnt NUMBER(5) := 0;
sql1 varchar2(4000);
x PLS_INTEGER;
--disable constraints:
cursor cur1 is select 'alter table ' || OWNER ||'.'||table_name||' disable constraint '||constraint_name sql2
from all_constraints where owner=pSchema and status='ENABLED'
and table_name not like 'BIN$%' and constraint_name not like 'SYS_%' and constraint_name not like '%PK%';
cursor cur2 is select 'alter table ' || OWNER ||'.'||table_name||' disable constraint '||constraint_name sql2
from all_constraints where owner=pSchema and status='ENABLED'
and table_name not like 'BIN$%' and constraint_name not like 'SYS_%';
--truncate all tables:
cursor cur3 is select 'truncate table ' || OWNER ||'.'||table_name sql2 from all_tables where owner=pSchema
and table_name not like 'BIN$%';
BEGIN
SELECT COUNT(*) INTO cnt FROM dba_users WHERE UPPER(username) = UPPER(pSchema);
IF (cnt <= 0) THEN
RETURN;
END IF;
sql1 := 'ALTER USER ' || UPPER(pSchema) || ' ACCOUNT LOCK';
EXECUTE IMMEDIATE sql1;
--disable constraints:
FOR ao_rec IN cur1 LOOP
EXECUTE IMMEDIATE ao_rec.sql2;
END LOOP;
FOR ao_rec IN cur2 LOOP
EXECUTE IMMEDIATE ao_rec.sql2;
END LOOP;
--truncate all tables:
FOR ao_rec IN cur3 LOOP
EXECUTE IMMEDIATE ao_rec.sql2;
END LOOP;
--drop schema:
sql1 := 'DROP USER ' || UPPER(pSchema) || ' CASCADE';
EXECUTE IMMEDIATE sql1;
exception when others then null;
END;
Also had this problem, got fixed by setting "PLScope identifiers:" to "None" in Tools->Preferences ->Database->PL/SQL Compiler

Resources