Get the count of each table - oracle

I was trying to create a procedure to fetch the total count of rows of all tables corresponding to a schema.
I am proceeding with a cursor which store the total list of tables and and the same is iterate further. Even though the functionality is not tested.The procedure creation compiled with the following errors.
create or replace
PROCEDURE PROC_TABLE_COUNT
AS
table_count NUMBER;
CURSOR total_tables
IS
SELECT table_name FROM dba_tables WHERE owner = 'OWNER_NAME';
BEGIN
FOR i IN total_tables
LOOP
SELECT COUNT (*) INTO table_count FROM dba_tables db where db.table_name = i.table_name;
END LOOP;
END PROC_TABLE_COUNT;
1)Error(7,6): PL/SQL: SQL Statement ignored
2)Error(7,33): PL/SQL: ORA-00942: table or view does not exist
3)Error(11,1): PL/SQL: SQL Statement ignored
4)Error(11,76): PL/SQL: ORA-00904: "I"."TABLE_NAME": invalid identifier
5)Error(11,76): PLS-00364: loop index variable 'I' use is invalid
Question 1:
Is the error 2(at dba_tables) is due to the grant being denied? By right clicking on the procedure name ,I tried to assign the privilege to debug and execute. But still the error persists.
Question 2:
Regarding the invalid identifier. Why is this error coming?
UPDATE:
As per one of the valuable comment I have changed the query and the compile error is gone. Now there is an issue in the logic.
create or replace procedure proc_tab_count as
table_count NUMBER;
CURSOR total_tables
IS
SELECT table_name FROM user_tables;
BEGIN
FOR i IN total_tables
LOOP
SELECT COUNT (*) INTO table_count FROM user_tables WHERE db.table_name = i.table_name; --Wrong logic here
DBMS_OUTPUT.put_line(i.table_name||'-COUNT:'||table_count);
END LOOP;
end proc_tab_count;
output is coming like:
Table1 -COUNT:1
Table2 -COUNT:1
Table3 -COUNT:1
Table3 -COUNT:1
Table4 -COUNT:1
Table5 -COUNT:1

Guess, you want to count rows in all your tables, and we need a dynamic SQL here.
EXCEUTE IMMEDIATE is used to frame the dynamic SQL making the OWNER.TABLE_NAME dynamic.
create or replace procedure proc_tab_count as
table_count NUMBER;
CURSOR total_tables
IS
SELECT table_name FROM user_tables;
BEGIN
FOR i IN total_tables
LOOP
/* Handle Exceptions */
BEGIN
EXECUTE IMMEDIATE 'SELECT COUNT (*) FROM '|| i.table_name INTO table_count;
DBMS_OUTPUT.put_line(i.table_name||'-COUNT:'||table_count);
EXCEPTION WHEN OTHERS
THEN
DBMS_OUTPUT.put_line('Error while Querying '||i.table_name||'-Error:'||SQLERRM);
END;
END LOOP;
end proc_tab_count;

I'm not so sure that the accepted answer works. Does user_tables contain owner? Otherwise, it's spot on.
Anyway. Another approach using SYS_REFCURSOR:
CREATE OR REPLACE PROCEDURE proc_table_count
CURSOR total_tables IS
SELECT table_name
FROM user_tables
ORDER BY table_name;
table_count NUMBER;
v_sql VARCHAR2(100);
v_cursor SYS_REFCURSOR;
BEGIN
FOR i IN total_tables
LOOP
BEGIN
v_sql := 'SELECT COUNT (*) FROM '||i.table_name;
OPEN v_cursor FOR v_sql;
FETCH v_cursor INTO table_count;
CLOSE v_cursor;
DBMS_OUTPUT.put_line(i.table_name||'-COUNT:'||table_count);
EXCEPTION WHEN OTHERS
THEN
DBMS_OUTPUT.put_line('Error while Querying '||i.table_name||'-Error:'||SQLERRM);
END;
END LOOP;
end proc_tab_count;

Related

Select INTO with binding parameters not working with execute immediate

I have the following query that returns 1 when executed:
SELECT COUNT(*) FROM TABLE_NAME WHERE Column1='x' AND Column2='y';
In my PL/SQL block, I need to evaluate the condition above in order to execute the business logic. Below is a simplified version:
DECLARE
column1 VARCHAR(20):='x';
column2 VARCHAR(20):='y';
rows_no NUMBER :=0;
sql_query VARCHAR2(200) :='SELECT COUNT(*) FROM TABLE_NAME WHERE Column1=:1 AND Column2=:2';
BEGIN
EXECUTE IMMEDIATE sql_query INTO rows_no USING column1, column2;
DBMS_OUTPUT.PUT_LINE('ROWS NO: '|| rows_no);
END;
The result from the execution of the PL/SQL block is 0 that is different from the result when the query is executed that is 1. I think I abiding by the rules of the binding parameter to query select into in the PL/SQL. I will appreciate any help or guide.
Best Regards,
Rando.
P.S
When I make the modification below :
DECLARE
column1 VARCHAR(20):='x';
column2 VARCHAR(20):='y';
rows_no NUMBER :=0;
sql_query VARCHAR2(200) :='SELECT COUNT(*) FROM TABLE_NAME WHERE Column1=''x'' AND Column2=''y''';
BEGIN
EXECUTE IMMEDIATE sql_query INTO rows_no;
DBMS_OUTPUT.PUT_LINE('ROWS NO: '|| rows_no);
END;
The result is: 1
I have a loop that reads from an excel file and inserts it into the database table(Table_name). The result above is needed to prevent the insertion of dublicates records. The commit is issued at the end of the procedure.
That means that if a record is added from the procedure it remains uncommitted till the procedure finishes.
I doubt this is a problem, uncommitted inserts are not accessible from other connections but in the current connection, they should be accessible. The following has all the simplified logic of the plsql block:
DECLARE;
--declaration variables
BEGIN
LOOP
-- fetching information from excel file
EXECUTE IMMEDIATE sql_query INTO rows_no;
DBMS_OUTPUT.PUT_LINE('ROWS NO: '|| rows_no);
IF rows_no=0 THEN
-- insert the information read from excel in database table
rows_inserted:=rows_inserted+1;
END IF;
END LOOP;
IF rows_inserted>0 THEN
COMMIT;
DBMS_OUTPUT.PUT_LINE('ROWS INSERTED: '||rows_inserted);
ELSE
DBMS_OUTPUT.PUT_LINE('No rows were inserted');
END IF;
END
The problem is most probably outside from Oracle, please try to re-test following script
(The output is shown as comment)
create table tab (column1 varchar2(1), column2 varchar2(1));
set SERVEROUTPUT ON
DECLARE
column1 VARCHAR(20):='x';
column2 VARCHAR(20):='y';
rows_no NUMBER :=0;
sql_query VARCHAR2(200) :='SELECT COUNT(*) FROM TAB WHERE Column1=:1 AND Column2=:2';
BEGIN
EXECUTE IMMEDIATE sql_query INTO rows_no USING column1, column2;
DBMS_OUTPUT.PUT_LINE('ROWS NO: '|| rows_no);
END;
/
-- ROWS NO: 0
insert into tab (column1, column2) values ('x','y');
DECLARE
column1 VARCHAR(20):='x';
column2 VARCHAR(20):='y';
rows_no NUMBER :=0;
sql_query VARCHAR2(200) :='SELECT COUNT(*) FROM TAB WHERE Column1=:1 AND Column2=:2';
BEGIN
EXECUTE IMMEDIATE sql_query INTO rows_no USING column1, column2;
DBMS_OUTPUT.PUT_LINE('ROWS NO: '|| rows_no);
END;
/
-- ROWS NO: 1

Put Column Name in Variable and use it in output statement

Here is What i actually wanted to do, Fetch Data From a Table without knowing any columns but i.j.Column_Name gives an error, so i wanted to put it in a variable. After reading the comments i think it's not possible
DECLARE
CURSOR C1 IS SELECT * FROM Table_Name;
CURSOR C2 IS SELECT Table_Name,Column_Name FROM user_tab_columns
WHERE data_type='VARCHAR2';
v_table Varchar2(256);
v_Col varchar2(200);
BEGIN
FOR i in C1 LOOP
FOR j in (SELECT Column_Name FROM user_tab_columns WHERE
Table_Name='Table_Name') LOOP
dbms_output.put_line(i.j.Column_Name);
END LOOP;
END LOOP;
END;
/
No, There is no Column Named v_Col
You can't refer to a field in a record (which is what the cursor loop is giving you) dynamically. If you need to do flexibly then you can use dbms_sql (possibly adapting this approach), but in the scenario you've shown you could use dynamic SQl to only get the column you want in the cursor:
-- dummy data
create table table_name (id number, column_name varchar2(10), other_col date);
insert into table_name values (1, 'Test 1', sysdate);
insert into table_name values (2, 'Test 2', sysdate);
DECLARE
CURSOR C1 IS SELECT * FROM Table_Name;
v_Cur sys_refcursor;
v_Col varchar2(200);
v_Val varchar2(4000);
BEGIN
v_Col:= 'Column_Name';
OPEN v_Cur for 'SELECT ' || v_Col || ' FROM Table_Name';
LOOP
FETCH v_Cur INTO v_Val;
EXIT WHEN v_Cur%notfound;
dbms_output.put_line(v_val);
END LOOP;
END;
/
Test 1
Test 2
PL/SQL procedure successfully completed.
The downside of this is that whatever the data type of the target column is, you have to implicitly convert it to a string; but you would be doing that in the dbms_output call anyway. So if you change the column you want to print:
v_Col:= 'Other_Col';
then the output from my dummy data would be:
2018-08-23
2018-08-23
PL/SQL procedure successfully completed.
where the date value is being implicitly formatted as a string using my current NLS session settings.
You could get more advanced by checking the data type in user_tab_columns and changing the dynamic query and/or the fetch and handling, but it isn't clear what you really need to do.

PL/SQL function statement ignored error

i want to create funtion that truncate all user objects,
this is my script
CREATE OR REPLACE Function Truncate_user ( name_in IN varchar2 )
return number
is
cnumber number;
v_str1 varchar2(200) := null;
cursor get_sql is
select
'drop '||object_type||' '||owner||'. '|| object_name|| DECODE(OBJECT_TYPE,'TABLE',' CASCADE CONSTRAINTS PURGE') v_str1
from DBA_objects
where object_type in ('TABLE','VIEW','PACKAGE','TYPE','PROCEDURE','FUNCTION','TRIGGER','SEQUENCE','SYNONYM')
AND owner=name_in
order by object_type,object_name;
begin
open get_sql;
loop
fetch get_sql into v_str1;
if get_sql%notfound
then cnumber :=0;
end if;
execute immediate v_str1;
end loop;
RETURN 1;
close get_sql;
end;
/
after the execution i got these errors
Erreur(7,1): PL/SQL: SQL Statement ignored
Erreur(9,6): PL/SQL: ORA-00942: Table ou vue inexistante
but when i execute this code without make a function ,the operation is done!
Your user does not have the priviliges to access DBA_OBJECTS
Maybe there are objects that requires quoted identifiers (e.g. using lowercase letters), try
select 'drop '||object_type||' '||owner||'. "'|| object_name||'"'|| ...

For Loop in Oracle / Toad

I am new to Oracle and PL/SQL and am trying to do the following.
I am returning the column names from a table name that is stored in a variable
variable v_table varchar2(100)
begin
select 'mytable' into :v_table from dual;
end;
select column_name from all_tab_columns where table_name = :v_table
This returns a rowset
column_name
colname1
colname2
colname3
I would like to loop through the returned rowset and get some stats for each column.
select count distinct(colname1), min(colname1), max(colname1)
from :v_table
group by min(colname1), max(colname1)
However I cannot figure out how to loop through this rowset.
By using below you can display column names of a table and append your sql query as per your requirement...
declare
v_table varchar2(100):='TABLE_NAME';
TYPE NT_VAR1 IS TABLE OF VARCHAR2(30);
TYP_NT NT_VAR1;
begin
select column_name BULK COLLECT INTO TYP_NT from all_tab_columns where table_name =v_table
order by column_id;
FOR I IN 1..TYP_NT.COUNT
LOOP
DBMS_OUTPUT.PUT_LINE('COLUMN '||I||': '||TYP_NT(I));
END LOOP;
end;
Note: You should run select query dynamically means with the use of execute immediate why because you are passing table_name at run time.
Edit
After editing your query in for loop it will look like below.
declare
v_table varchar2(100):='TABLE_NAME';
TYPE NT_VAR1 IS TABLE OF VARCHAR2(30);
TYP_NT NT_VAR1;
var2 varchar2(2000);
var3 varchar2(2000);
begin
select column_name BULK COLLECT INTO TYP_NT from all_tab_columns where table_name =v_table
order by column_id;
FOR I IN 1..TYP_NT.COUNT
LOOP
DBMS_OUTPUT.PUT_LINE('COLUMN '||I||': '||TYP_NT(I));
EXECUTE IMMEDIATE 'SELECT to_char(min('||TYP_NT(I)||')),to_char(max('||TYP_NT(I)||')) from '||v_table into var2,var3;
dbms_output.put_line(var2||' '||var3);
END LOOP;
end;
Note: Find the max data length of a column present in your table and assign same data type to var2 and var3 variables.

PL/SQL EXECUTE IMMEDIATE inside LOOP (procedure to truncate all tables in schema)

I need to create procedure which will delete all data from tables in one schema. I try something like that
CREATE OR REPLACE PROCEDURE CLEAR_ALL
IS
sql_truncate VARCHAR2(50);
cursor c1 is
SELECT table_name
FROM all_tables
WHERE owner = 'KARCHUDZ_S';
BEGIN
sql_truncate := 'TRUNCATE TABLE :text_string';
FOR table_name in c1
LOOP
EXECUTE IMMEDIATE sql_truncate USING table_name;
END LOOP;
END CLEAR_ALL;
But it gives me two errors which i cannot understand and fix.
Error(13,7): PL/SQL: Statement ignored
Error(13,44): PLS-00457: Statment must be type of SQL <-- (This error
i had to translate, cause i use University Oracle 11g base which have
Polish lang)
Why not just generate the statement and call it, like this?
CREATE OR REPLACE PROCEDURE CLEAR_ALL
IS
vs_statement VARCHAR2(100);
cursor c1 is
SELECT table_name
FROM all_tables
WHERE owner = 'KARCHUDZ_S';
BEGIN
FOR table_rec in c1
LOOP
vs_statement := 'TRUNCATE TABLE ' || table_rec.table_name;
EXECUTE IMMEDIATE vs_statement;
END LOOP;
END CLEAR_ALL;
You can't use bind variables (i.e. your using clause) as a placeholder for an object name. If you could, you wouldn't need to use dynamic SQL in the first place. You'll have to use concatenation or substitution instead:
CREATE OR REPLACE PROCEDURE CLEAR_ALL
IS
sql_truncate CONSTANT VARCHAR2(50) := 'TRUNCATE TABLE [text_string]';
cursor c1 is
SELECT table_name
FROM all_tables
WHERE owner = 'KARCHUDZ_S';
BEGIN
FOR row in c1
LOOP
EXECUTE IMMEDIATE replace(sql_truncate, '[text_string]', row.table_name);
END LOOP;
END CLEAR_ALL;

Resources