LOOP through and select from multiple tables in SQL developer - sql

I am trying to loop through 5 tables all with a similar naming format. I am able to return the tables names by using the query
select table_name from all tables
where table_name like '
order by table_name
My intention is to insert the data from all the 5 tables into one table where a particular date condition is met. I know I can do this via union all but this makes my script very long as I have to paste the same script for 5 tables, I was wondering if there is a way to do without using union all but rather using dynamic sql? I am new to dynamic sql.

It might look like this:
begin
for cur_r in (select table_name
from all_tables
where table_name like 'ABC%'
)
loop
execute immediate
'insert into target_table (id, name, address) ' ||
'select id, name, address from ' || cur_r.table_name ||
' where date_column = ' || trunc(sysdate);
end loop;
end;
/
loop through all_tables, fetching only those you're interested in
compose insert statement, presuming that all tables (from previous step) share the same set of common columns
include where clause, if you want

Related

How can I loop to get counts from multiple tables?

I am trying to derive a table with counts from multiple tables. The tables are not on my schema. The table names on the schema that I am interested in all start with 'STAF_' and end with '_TS'. The criteria i am looking for is where SEP = 'MO'. So for example, the query in its base form is:
select area, count(SEP) areacount
from mous.STAF_0001_TS
where SEP = 'MO'
group by area;
I have about 1000 tables that i'd like to do this for.
Ultimatly, I'd like the output to be a table on my schema that looks like the following:
area| areacount
0001| 3
0002| 7
0003| 438
Thank you.
As a first step I'd write an SQL query that generates an SQL query:
SELECT 'SELECT area, count(*) FROM '||c.table_name||'UNION ALL' as run_me
FROM all_tables c
WHERE c.table_name LIKE 'STAF\_%\_MS' escape '\'
Running this will produce an output that is another SQL query. Copy the result text out of your results grid and paste it back into your query pane. Delete the final UNION ALL and run it
Once you dig how to write an SQL query that generate an SQL query, you can look at turning it into a view, or creating a dynamic query in a string.
Gotta say, this is a horrible way to store data; you'd be better off using ONE table with an extra column containing whatever is in xxx of STAF_xxx_MS right now
In Oracle 12c, you can embed a FUNCTION that will query the number of rows in any given table. Then you can use that function in your main query. Here is an example:
WITH FUNCTION cnt ( p_owner VARCHAR2, p_table_name VARCHAR2 ) RETURN NUMBER IS
l_cnt NUMBER;
BEGIN
EXECUTE IMMEDIATE 'SELECT count(*) INTO :cnt FROM ' || p_owner || '.' || p_table_name INTO l_cnt;
RETURN l_cnt;
EXCEPTION WHEN OTHERS THEN
RETURN NULL; -- This will happen for entries in ALL_TABLES that are not directly accessible (e.g., IOT overflow tables)
END cnt;
SELECT t.owner, t.table_name, cnt(t.owner, t.table_name)
FROM all_tables t
where t.table_Name like 'STAF\_%\_MS' escape '\';

How to iterate through the result of a PLSQL Select

I am trying to find how many occurrences of a particular value there are in a particular column in an Oracle database. The column is used in dozens of tables and I'm going to have to run the queries many times, so I don't want to query each table individually. I can get a list of the tables to search with something like:
Select table_name from all_tab_cols
join all_tables using (table_name)
where column_name = 'EmployeeId' and num_rows > 0
The next step is to iterate through that list of table names and output each table that contains a particular value in the EmployeeId column. For example, output might be something like:
**Table Name Column_name # Rows for EmployeeId = '123456'**
Table 1 EmployeeId 1
Table 2 EmployeeId 12
etc.
I'm not a developer and don't have experience using cursors in SQL scripts, so any help would be greatly appreciated.
try using CURSOR FOR LOOP.
Probably it may look as shown below (not tried).
BEGIN
FOR item IN
(Select table_name,column_name,num_rows from all_tab_cols
join all_tables using (table_name)
where column_name = 'EmployeeId' and num_rows > 0)
LOOP
DBMS_OUTPUT.PUT_LINE
(item.table_name || ' ' || item.column_name ||' '||item.num_rows);
END LOOP;
END;

Collecting the last updates of multiple tables into a single table

I have a problem in that I want my output to be a single table (lets call it Output) with 2 columns: one for the "TableName" and one for the DateTime of the last update (using the scn_to_timestamp(max(ora_rowscn)) command).
I have 100 tables and I want to pull in the last update date/times for all these tables into the Output table.
So I can do this:
insert into Output(TableName)
select table_name
from all_tables;
which will put all the tables I have from my database into the TableName column. But I don't know how to loop through each entry and use the tablename as a variable and pass this into the scn_to_timestamp(ora_rowscn).
I thought I would try something like below:
for counter in Output(TableName) LOOP
insert into Output(UpdateDate)
select scn_to_timestamp(max(ora_rowscn))
from counter;
END LOOP;
Any suggestions?
Thank you
This query is a little bit clumsy as it uses xmlgen to execute dynamic sql in a query, but it might work for you.
select x.*
from all_tables t,
xmltable('/ROWSET/ROW' passing
dbms_xmlgen.getxmltype('select ''' || t.table_name ||
''' tab_name, max(ora_rowscn) as la from ' ||
t.table_name)
COLUMNS tab_name varchar2(30) PATH 'TAB_NAME',
max_scn number PATH 'LA') x
Here is a sqlfiddle demo
You can also use PLSQL and then use execute immediate

Create table from many tables with specific data

We have oracle11g.
I need to write stored procedure (or may be another solution like creating view) that will truncute old table and insert new updated data.
We have many tables in database with name %table_name%+number_prefix.
For example: Country_1, Country_2.
Here is data from this tables:
id code country
103858 834 TZ
103878 834 UA
103859 800 UG
103860 894 ZM
103861 716 ZW
103862 24 AO
Also we have table Country_all, with specific information from all this tables.
If users add new table from web gui new Country_%prefix% table, i have to update Country_all table. This table should group by code and put all country in one row (TZ, UA, UG ...):
table code countries
91 2005202000 ,AD,AL,AQ,AS,AT,
91 2005400000 ,AD,AL,AQ,AS,AT,
91 2005995000 ,AD,AL,AQ,AS,AT,
91 2005997000 ,AD,AL,AQ,AS,AT,
91 2006003100 ,AD,AL,AQ,AS,AT,
Here 91 means that this data from table Country_91
I use this to get prefix from table:
EXECUTE IMMEDIATE 'truncate table COUNTRY_ALL';
FOR r IN (SELECT regexp_replace(table_name,'(.*)_', '\1') ADD_NUM
FROM all_tables
WHERE upper(table_name)
LIKE ('COUNTRY_%'))
But when i try to use r.ADD_NUM i have trouble.
INSERT INTO COUNTRY_ALL
SELECT r.ADD_NUM, code, listagg(code, ',') WITHIN GROUP (ORDER BY code) AS GI_COUNTRIES
FROM 'COUNTRY_' || r.ADD_NUM
GROUP BY code;
I also try to declare some variable and assign COUNTRY_ || r.ADD_NUM to the variable, but it also don't want to query from variable.
UPDATE
Finally i did it yesterday!
I did it in two ways. The first one is exactly what i was asking:
BEGIN
EXECUTE IMMEDIATE 'truncate table Countries_ALL';
FOR r IN (SELECT regexp_replace(table_name,'_(.*)', '\1') ADD_NUM
FROM all_tables
WHERE upper(table_name)
LIKE ('COUNTRY_%')
AND owner='owner_name')
loop
EXECUTE IMMEDIATE 'INSERT COUNTRIES_ALL
SELECT '|| r.ADD_NUM ||' as ADD_NUM, code, listagg(countries, '','') WITHIN GROUP (ORDER BY countries) AS COUNTRIES
FROM COUNTRY_' || r.ADD_NUM || '
GROUP by code;
END loop;
END;
The second one is better. I create 2 temporary tables: CURRENT and FOR_UPDATING.
To fill CURRENT:
INSERT INTO CURRENT
SELECT DISTINCT ADD_NUM FROM COUNTRIES_ALL;
And procedure that will update only not existing table, unlike previous one:
DECLARE
BEGIN
execute IMMEDIATE 'TRUNCATE table for_updating';
INSERT INTO for_updating
SELECT regexp_replace(table_name,'_(.*)', '\5 ') as ADD_NUM
FROM all_tables
WHERE owner='owner_name'
AND table_name LIKE 'COUNTRY_%';
for num in (
SELECT b.add_num FROM CURRENT A
RIGHT JOIN for_updating b
ON A.add_num=b.add_num
WHERE A.add_num IS NULL)
loop
EXECUTE IMMEDIATE 'INSERT INTO COUNTRIES_ALL
SELECT '||num.add_num||' as ADD_NUM, code, listagg(countries, '','') WITHIN GROUP (ORDER BY countries) AS GI_COUNTRIES
FROM COUNTRY_' || num.ADD_NUM || '
GROUP by code';
end loop;
END;
In this procedure i compare what tables i already have in COUNTRIES_ALL and what i have in all_tables. Then all new tables will be inserted.
Thanks to Przemyslaw Kruglej for his patience!
You can't run a query with dynamic name of the table.
You have to either generate a query for each of the tables, use dbms_sql package or EXECUTE IMMEDIATE.
Do you know the number of the table you are supposed to work on? If so, you can use EXECUTE IMMEDIATE:
EXECUTE IMMEDIATE
'INSERT INTO COUNTRY_ALL
SELECT r.ADD_NUM, code, listagg(code, '','') WITHIN GROUP (ORDER BY code) AS GI_COUNTRIES
FROM COUNTRY_' || tab_number ||
' GROUP BY code';

How can I find columns which have non-null values?

I have many columns in oracle database and some new are added with values. I like to find out which columns have values other than 0 or null. So I am looking for column names for which some sort of useful values exists at least in one row.
How do I do this?
Update: This sounds very close. How do I modify this to suit my needs?
select column_name, nullable, num_distinct, num_nulls
from all_tab_columns
where table_name = 'SOME_TABLE'
You can query all the columns using the dba_tab_cols view and then see if there are columns which have values other than 0 or null.
create or replace function f_has_null_rows(
i_table_name in dba_tab_cols.table_name%type,
i_column_name in dba_tab_cols.table_name%type
) return number is
v_sql varchar2(200);
v_count number;
begin
v_sql := 'select count(*) from ' || i_table_name ||
' where ' || i_column_name ' || ' is not null and '
|| i_column_name || ' <>0 ';
execute immediate v_sql into v_count;
return v_count;
end;
/
select table_name, column_name from dba_tab_Cols
where f_has_null_rows (table_name, column_name) > 0
If you have synonyms in some schemas, you mighty find some of the tables are repeated. You'll have to change the code to cater to that.
Also, the check "is not equal to zero" might not be valid for columns that are not integers and will give errors if columns are of date datatype. You'll need to add the conditions for those cases. use the Data_type column in dba_tab_cols and add the condition as needed.
Select Column_name
from user_tab_columns
where table_name='EMP' and num_nulls=0;
This finds columns which does not have any values so you can perform any actions to that.
Sorry, I misread the question the first time.
From this post on Oracle's forums
Assuming your stats are up to date:
SELECT t.table_name,
t.column_name
FROM user_tab_columns t
WHERE t.nullable = 'Y'
AND t.num_distinct = 0;
Will return you a list of table names and columns that are null. You might want to add something like:
AND t.table_name = upper('Your_table_name')
in there to limit the results to just your table.
select 'cats' as mycolumname from T
where exists (Select id from T where cats is not null)
union
select 'dogs' as mycolumnname from T
where exists (select id from T where dogs is not null)
# ad nauseam
is how to do it in SQL. EDIT: Different flavors of SQL might let you optimize with LIMIT or TOP 'n' in the subquery. Or maybe they're even smart enough to realize that EXIST() only needs one row and optimize silently/transparently. P.S. Add your test for zero to the subquery.