I have a table "TEST_TABLE" with two columns TABLE_NAME and RECORD_COUNT.
enter image description here
We want to update the column RECORD_COUNT by taking the total records in table specified in TABLE_NAME.
You could do this with dynamic SQL but why be complicated?
Create a view
Create view my_tables as
Select
'table_1' as "table name",
count(*) as "rows"
From table_1
Add the following for each table
Union all
Select 'table_2',count(*) From table_2
You can then use the view like a table:
Select * from my_tables;
OK, but - why wouldn't you use information Oracle already provides for you? You should regularly gather statistics anyway, so:
SQL> execute dbms_stats.gather_schema_stats(ownname => 'SCOTT', estimate_percent => null);
PL/SQL procedure successfully completed.
and then fetch num_rows from user_tables:
SQL> select table_name, num_rows from user_tables where rownum <= 10;
TABLE_NAME NUM_ROWS
-------------------- ----------
EMP 14
DEPT 4
BONUS 0
SALGRADE 5
DUMMY 1
TBL_ERROR 1
AUDIT_TAB 2
SOURCE_DET 3
EXAMPLE 1
FLIGHT 0
10 rows selected.
SQL>
It can't be much worse than your attempt (due to possible frequent changes to tables' contents; inserts and deletes) because you'd collect your own data periodically (how often?) anyway.
If it has to be your way, then you'd use dynamic SQL, looping through all tables in your schema and actually count number of rows:
SQL> create table test_table
2 (table_name varchar2(30),
3 num_rows number);
Table created.
SQL> create or replace procedure p_test as
2 l_cnt number;
3 begin
4 execute immediate 'truncate table test_table';
5 for cur_R in (select table_name from user_tables) loop
6 execute immediate 'select count(*) from ' ||cur_R.table_name into l_Cnt;
7 insert into test_table (table_name, num_rows) values (cur_r.table_name, l_cnt);
8 end loop;
9 end;
10 /
Procedure created.
Running the procedure and viewing the result:
SQL> exec p_test;
PL/SQL procedure successfully completed.
SQL> select * From test_Table where rownum <= 10;
TABLE_NAME NUM_ROWS
-------------------- ----------
EMP 14
DEPT 4
BONUS 0
SALGRADE 5
DUMMY 1
TBL_ERROR 1
AUDIT_TAB 2
SOURCE_DET 3
EXAMPLE 1
FLIGHT 0
10 rows selected.
SQL>
Note that performance will suffer as number of tables and number of rows stored in them grows.
If I were you, I'd go for the first option.
Related
Example scenario:
5 tables are there and one common field among them is com_field (DATE data type). Now, I need to find the maximum of com_field in each of the five tables. Can someone give the logic?
I know UNION could be used but I need the flexibility not to miss any new table added to the OWNER.
The result I am expecting is like the below.
Table Max(com_field)
Tbl1 10/21/2019
Tbl2 10/18/2019
Tbl3 10/28/2019
Tbl4 09/30/2019
Tbl5 09/09/2019
Run this query:
SELECT
'SELECT '''||OWNER||'.'||TABLE_NAME ||''' AS TABLE_NAME , '||'MAX(COM_FIELD)AS COM_FIELD FROM ' ||OWNER||'.'||TABLE_NAME ||' UNION ALL'
FROM ALL_TAB_COLUMNS
WHERE COLUMN_NAME ='COM_FIELD'
then copy the outpu and delete last union all keyword. Then run the sql statement.
You can order it and see the max value
One option is to use dynamic SQL in a function that returns refcursor. Here's an example.
First, test case:
SQL> create table taba (com_field date);
Table created.
SQL> create table tabb (com_field date);
Table created.
SQL> create table tabc (com_field date);
Table created.
SQL> insert all
2 into taba values (sysdate)
3 into taba values (sysdate - 2)
4 into taba values (sysdate - 3)
5 into tabb values (sysdate + 2)
6 into tabc values (sysdate + 4)
7 into tabc values (sysdate + 5)
8 select * From dual;
6 rows created.
SQL>
Function:
SQL> create or replace function f_maxcom
2 return sys_refcursor
3 is
4 l_str varchar2(1000);
5 rc sys_refcursor;
6 begin
7 for cur_r in (select table_name
8 from user_tab_columns
9 where column_name = 'COM_FIELD'
10 )
11 loop
12 l_str := l_str ||
13 'select ' || chr(39) || cur_r.table_name || chr(39) || ', ' ||
14 'max(com_field) from ' || cur_r.table_name || ' union all ';
15 end loop;
16
17 l_str := rtrim(l_str, ' union all');
18
19 open rc for l_str;
20 return rc;
21 end;
22 /
Function created.
SQL>
Let's try it:
SQL> select f_maxcom from dual;
F_MAXCOM
--------------------
CURSOR STATEMENT : 1
CURSOR STATEMENT : 1
'TAB MAX(COM_FI
---- ----------
TABA 29.10.2019
TABB 31.10.2019
TABC 03.11.2019
SQL>
Add another table to see what happens; function will stay as is:
SQL> create table littlefoot (id number, com_field date);
Table created.
SQL> insert into littlefoot values (100, sysdate);
1 row created.
SQL> select f_maxcom from dual;
F_MAXCOM
--------------------
CURSOR STATEMENT : 1
CURSOR STATEMENT : 1
'LITTLEFOO MAX(COM_FI
---------- ----------
LITTLEFOOT 29.10.2019
TABA 29.10.2019
TABB 31.10.2019
TABC 03.11.2019
SQL>
Seems to be OK, eh?
I want to run a query on different schemes to get data and export it. I use the following code
DECLARE
sql_statment VARCHAR2(2000);
BEGIN
FOR c IN (SELECT brchcode FROM brchs) LOOP
sql_statment := 'select distinct ''' || c.brchcode ||''', t.risuid from ' || c.brchcode ||
'.reg_individualacnt_detail t
where t.historytypecode = 60';
EXECUTE IMMEDIATE sql_statment;
END LOOP;
END;
where brchcode is the name of different schemes
I can't see any output. what can I do?
Code you wrote can't work as you have to return the result into something; it is PL/SQL and requires an INTO clause. As you chose to return two values (columns) and multiple rows, that can't be a scalar variable; you could pick ref cursor or a collection, for example.
Here's an example which shows one option.
I'll be using two schemas: SCOTT (current schema) and HR. Both will be having the DEPT table.
As Scott already has it, I'll create one in HR schema and grant access to Scott (otherwise, Scott won't even see it and the procedure (i.e. the function) will fail):
SQL> connect hr/hr
Connected.
SQL> create table dept (deptno number, dname varchar2(10), loc varchar2(10));
Table created.
SQL> insert into dept values (55, 'IT', 'Zagreb');
1 row created.
SQL> grant select on dept to scott;
Grant succeeded.
SQL> commit;
Commit complete.
Back to Scott, to create a table (which contains schema names I'll be selecting from) and a function. I chose to return REF CURSOR; you could return something else, if you want.
SQL> connect scott/tiger
Connected.
SQL> create table brchs (brchcode varchar2(10));
Table created.
SQL> insert into brchs (brchcode)
2 select 'scott' from dual union all
3 select 'hr' from dual;
2 rows created.
SQL> create or replace function f_br
2 return sys_refcursor
3 is
4 l_str varchar2(4000);
5 l_rc sys_refcursor;
6 begin
7 for cur_r in (select brchcode from brchs) loop
8 l_str := l_str ||
9 'union all
10 select ' || chr(39)|| cur_r.brchcode ||chr(39) || ', d.dname
11 from ' || cur_r.brchcode ||'.dept d
12 where d.deptno > 0';
13 end loop;
14
15 l_str := ltrim(l_str, 'union all');
16
17 open l_rc for l_str;
18 return l_rc;
19 end;
20 /
Function created.
SQL>
Finally, testing:
SQL> select f_br from dual;
F_BR
--------------------
CURSOR STATEMENT : 1
CURSOR STATEMENT : 1
'SCOT DNAME
----- --------------
scott ACCOUNTING
scott RESEARCH
scott SALES
scott OPERATIONS
hr IT
SQL>
I have an Oracle database that has 20 very large tables, each with hundreds of partitions. I can compress a table but it takes over 2 hours. This would mean over 40 hours to complete all 20 tables. I would like to run the partition compression simultaneously (1 per table). Because the partitions are added daily I need a utility to generate the "alter table ..." syntax at the time of the run. So far, all I can think of is to create a SQL for each of the 20 tables and their partitions, then run them in 20 SQLPlus sessions.
Is there a better, more automated way to do this?
You could submit several jobs, which will - in turn - run your code simultaneously. Here's an example:
Test tables - I want to modify ID's datatype to VARCHAR2(10)
SQL> create table t1 (id varchar2(2));
Table created.
SQL> create table t2 (id varchar2(2));
Table created.
SQL> create table t3 (id varchar2(2));
Table created.
A procedure which will utilize EXECUTE IMMEDIATE and is called from DBMS_JOB (see below):
SQL> create or replace procedure p_exe (par_what in varchar2) is
2 begin
3 execute immediate par_what;
4 end;
5 /
Procedure created.
Create jobs which will run the ALTER TABLE simultaneously
SQL> declare
2 l_str varchar2(200);
3 l_job number;
4 begin
5 for cur_r in (select 't1' table_name from dual union all
6 select 't2' from dual union all
7 select 't3' from dual)
8 loop
9 l_str := 'alter table ' || cur_r.table_name || ' modify id varchar2(10)';
10 dbms_output.put_line(l_str);
11 dbms_job.submit(l_job,
12 'p_exe(' || chr(39) || l_str || chr(39) ||');',
13 sysdate
14 );
15 commit;
16 end loop;
17 end;
18 /
PL/SQL procedure successfully completed.
Jobs are now running; in a few moments (in my case, as it is a simple one - you'd check that somewhat later), check what's being done:
SQL> desc t1;
Name Null? Type
----------------------- -------- ----------------
ID VARCHAR2(10)
SQL> desc t3;
Name Null? Type
----------------------- -------- ----------------
ID VARCHAR2(10)
SQL> desc t3;
Name Null? Type
----------------------- -------- ----------------
ID VARCHAR2(10)
SQL>
I need to execute some statements within the IF clause only if a table exists.
But the issue I am facing is, even when the condition is false, the statements are getting executed.
DECLARE
count_matching_row NUMBER := 0;
count_matching_tbl NUMBER := 0;
BEGIN
SELECT COUNT(*)
INTO count_matching_tbl
FROM user_tables
WHERE LOWER(table_name) = 'tab1';
IF(count_matching_tbl = 1)
THEN
SELECT COUNT (*)
INTO count_matching_row
FROM test1
WHERE ID IN (SELECT ID FROM tab1);
IF(count_matching_row = 0)
THEN
INSERT INTO review_case
SELECT
DISTINCT ID, d,e
FROM tab1
WHERE ID IS NOT NULL;
INSERT INTO review_case_payer
SELECT
a,b,c
FROM tab1
WHERE a IS NOT NULL;
COMMIT;
END IF;
END IF;
END;
/
Whenever I execute these statements, if the table 'tab1' exists it works fine.
If the table tab1 does not exist I get the error
"ORA-06550: line 13, column 14:
PL/SQL: ORA-00942: table or view does not exist"
I get similar errors for each line where I try to access table "tab1"
I tried with ref cursor but still the same, I cannot use it for insert statements.
Your error is due to the fact that you're using a table that may not exist; this error is thrown because the script has compile problems, not data problems, so the way you try to use the IF is not enough to handle your situation.
You need to use some dynamic SQL to handle an object that could not exist; for example, see the following.
If the table does not exist, nothing will be done:
SQL> select * from tab1;
select * from tab1
*
ERROR at line 1:
ORA-00942: table or view does not exist
SQL> declare
2 vCountTab number;
3 begin
4 select count(1)
5 into vCountTab
6 from user_tables
7 where table_name = 'TAB1';
8
9 if vCountTab = 1 then
10 execute immediate 'insert into TAB1 values (1, 2)';
11 end if;
12 end;
13 /
PL/SQL procedure successfully completed.
If the table exists, the insert will be done:
SQL> create table tab1(a number, b number);
Table created.
SQL> declare
2 vCountTab number;
3 begin
4 select count(1)
5 into vCountTab
6 from user_tables
7 where table_name = 'TAB1';
8
9 if vCountTab = 1 then
10 execute immediate 'insert into TAB1 values (1, 2)';
11 end if;
12 end;
13 /
PL/SQL procedure successfully completed.
SQL> select * from tab1;
A B
---------- ----------
1 2
SQL>
Is it possible to check the count of a table before any changes happen and the count after the insert and match them inside the same trigger?
for ex: old.count and new.count (before and after insert) ?
old.count and new.count (before and after insert)
Nothing stops you from using SELECT COUNT(*) in a before insert trigger. Of course, you won't do it in a after insert trigger, since a select count(*) on the same table on which an after trigger is defined would throw mutating table error. One way is autonomous transaction. But, in your case, it isn't that complex.
You could define a BEFORE INSERT TRIGGER and take the table count. The after insert count could be taken manually after the actual insert is done.
For example, I have a table t1 with one row. I have defined a before insert trigger on it, which would give me the table count before the insert happens.
SQL> DROP TABLE t1 PURGE;
Table dropped.
SQL>
SQL> CREATE TABLE t1 (A NUMBER);
Table created.
SQL>
SQL> INSERT INTO t1 VALUES (1);
1 row created.
SQL>
SQL> SELECT * FROM t1;
A
----------
1
SQL>
SQL> CREATE OR REPLACE TRIGGER trg
2 BEFORE INSERT
3 ON t1
4 FOR EACH ROW
5
6 DECLARE
7 val number;
8 BEGIN
9 SELECT COUNT(*)
10 INTO val
11 FROM t1;
12
13 DBMS_OUTPUT.PUT_LINE('TABLE COUNT BEFORE INSERT = '||val);
14
15 END;
16 /
Trigger created.
SQL>
SQL> set serveroutput on
SQL> INSERT INTO t1 VALUES (1);
TABLE COUNT BEFORE INSERT = 1
1 row created.
SQL>
SQL> SELECT COUNT(*) FROM t1;
COUNT(*)
----------
2
SQL>
So, you see TABLE COUNT BEFORE INSERT = 1 and then after insert the count is 2.