Oracle 11g temporary list object from query - oracle

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;

Related

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.

Check all entries of column of type DATE if they are in the businessday calendar

I would like to check all columns of all tables for entries which are not contained in the table of valid dates.
In other words the elements of all columns with type date have to one of the entries of the table column CALENDAR.BD.
My problem is that executing the function by select checkAllDateColumns() from DUAL results only in
invalid table name, line 16
and I don't see why?
CREATE OR REPLACE FUNCTION checkAllDateColumns RETURN NUMBER IS
l_count NUMBER;
BEGIN
FOR t IN (SELECT table_name
FROM all_tables
WHERE owner = 'ASK_QUESTION')
LOOP
dbms_output.put_line ('Current table : ' || t.table_name);
FOR c IN (SELECT column_name
FROM all_tab_columns
WHERE TABLE_NAME = t.table_name AND data_type = 'DATE')
LOOP
execute immediate 'select count(*) from :1 where :2 not in (select BD from CALENDAR where is_business_day = 1)'
into l_count
using t.table_name, c.column_name;
IF (l_count > 0) THEN
RETURN 1;
END IF;
END LOOP;
END LOOP;
RETURN 0;
END checkAllDateColumns;
/
Btw: I am fine with the statement stopping on the first mismatch, currently I would like to figure out the dynamic sql...
You can't use a bind variable for the table or column names, only for column values. It's trying to interpret :1 as an identifier, it isn't using the value from the using clause. Your returning into clause should also just be into. This works:
execute immediate 'select count(*) from "' || t.table_name
|| '" where "' || c.column_name
|| '" not in (select BD from CALENDAR where is_business_day = 1)'
into l_count;
You might get a performance difference using a left-join approach, but it depends on your data:
execute immediate 'select count(*) from "' || t.table_name
|| '" t left join calendar c on c.bd = trunc(t."'
|| c.column_name || '") and c.is_business_day = 1 '
|| ' where c.bd is null'
into l_count;
I added a trunc() in case any of the fields might have times in them; you can do the same in the other version too of course.
And in both I've included double-quotes around the table and column names, just in case there are any quoted identifiers - without those there's a risk of getting an ORA-00904 'invalid identifier' error. But hopefully you don't have any to worry about anyway.
You also don't really need nested loops; you can either get the table name from all_tab_columns, or if you prefer, join all_tables and all_tab_columns in a single cursor. You should also be checking that the owner is the same in both tables, in case there are two versions of a table in different schemas.

Query to find all empty tables

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);

How to trim all columns in all rows in all tables of type string?

In Oracle 10g, is there a way to do the following in PL/SQL?
for each table in database
for each row in table
for each column in row
if column is of type 'varchar2'
column = trim(column)
Thanks!
Of course, doing large-scale dynamic updates is potentially dangerous and time-consuming. But here's how you can generate the commands you want. This is for a single schema, and will just build the commands and output them. You could copy them into a script and review them before running. Or, you could change dbms_output.put_line( ... ) to EXECUTE IMMEDIATE ... to have this script execute all the statements as they are generated.
SET SERVEROUTPUT ON
BEGIN
FOR c IN
(SELECT t.table_name, c.column_name
FROM user_tables t, user_tab_columns c
WHERE c.table_name = t.table_name
AND data_type='VARCHAR2')
LOOP
dbms_output.put_line(
'UPDATE '||c.table_name||
' SET '||c.column_name||' = TRIM('||c.column_name||') WHERE '||
c.column_name||' <> TRIM('||c.column_name||') OR ('||
c.column_name||' IS NOT NULL AND TRIM('||c.column_name||') IS NULL)'
);
END LOOP;
END;
Presumably you want to do this for every column in a schema, not in the database. Trying to do this to the dictionary tables would be a bad idea...
declare
v_schema varchar2(30) := 'YOUR_SCHEMA_NAME';
cursor cur_tables (p_schema_name varchar2) is
select owner, table_name, column_name
from all_tables at,
inner join all_tab_columns atc
on at.owner = atc.owner
and at.table_name = atc.table_name
where atc.data_type = 'VARCHAR2'
and at.owner = p_schema;
begin
for r_table in cur_tables loop
execute immediate 'update ' || r.owner || '.' || r.table_name
|| ' set ' || r.column_name || ' = trim(' || r.column_name ||');';
end loop;
end;
This will only work for fields that are VARCHAR2s in the first place. If your database contains CHAR fields, then you're out of luck, because CHAR fields are always padded to their maximum length.

Resources