Oracle dynamic find number of rows and columns per table - oracle

I have the following query, which returns the number of rows per table in a schema.
Can this also be modified to RETURN the number of columns per table too?
CREATE table stats (
table_name VARCHAR2(128),
num_rows NUMBER,
num_cols NUMBER
);
/
DECLARE
val integer;
BEGIN
for i in (SELECT table_name FROM all_tables WHERE owner = 'Schema')
LOOP
EXECUTE IMMEDIATE 'SELECT count(*) from ' || i.table_name INTO val;
INSERT INTO stats VALUES (i.table_name,val);
END LOOP;
END;
/

You can use the ALL_TAB_COLS dictionary table:
DECLARE
val integer;
BEGIN
FOR i IN (
SELECT table_name,
COUNT(*) AS num_cols
FROM all_tab_cols
WHERE owner = 'Schema'
GROUP BY table_name
)
LOOP
EXECUTE IMMEDIATE 'SELECT count(*) from ' || i.table_name INTO val;
INSERT INTO stats VALUES (i.table_name,val, i.num_cols);
END LOOP;
END;
/
db<>fiddle here

Related

problem during creating column in existing table

SET SERVEROUTPUT ON SIZE 1000000
DECLARE
v_col_exists NUMBER;
BEGIN
SELECT COUNT (*)
INTO v_col_exists
FROM user_tab_cols
WHERE column_name = 'EFFECTIVE_DATE' AND table_name = 'MEMBERS';
IF (v_col_exists = 0)
THEN
EXECUTE IMMEDIATE 'ALTER TABLE members ADD effective_date DATE';
ELSE
DBMS_OUTPUT.PUT_LINE ('The column effective_date already exists');
END IF;
END;

Create procedure in oracle DB for finding and deleting foreign key constraints for specific tables

I am new to Oracle, I am trying to write a procedure in Oracle to delete foreign key constraints for a table. I have already done this for MySQL and its working. I am not sure with the syntax, apologies for that, but my query is working If I run it individually. I want to do the same thing(removing foreign key constraints) for multiple tables and don't want to write queries multiple times. hence first I am finding the foreign keys associated with that table , storing them in cursor and later removing all of the foreign keys by creating and executing drop constraints query associated with that table. following code is giving multiple errors to me.
CREATE OR REPLACE PROCEDURE removeConstraintsForTable(vTableName IN varchar2) IS
BEGIN
cName VARCHAR(2048);
sql_stmt VARCHAR2(2048);
CURSOR cur IS
SELECT DISTINCT CONSTRAINT_NAME
FROM ALL_CONSTRAINTS WHERE OWNER= sys_context('userenv','current_schema')
AND TABLE_NAME = vTableName AND CONSTRAINT_TYPE='R';
BEGIN
OPEN cur;
LOOP
FETCH cur INTO cName;
EXIT WHEN cur%notfound;
sql_stmt := CONCAT(CONCAT(CONCAT('ALTER TABLE ',vTableName),CONCAT(' DROP FOREIGN KEY ',cName)),';');
SELECT sql_stmt FROM dual;
INSERT INTO TEMP(Name) VALUES(sql_stmt);
COMMIT;
END LOOP;
END
/
CALL removeConstraintsForTable('table1');
CALL removeConstraintsForTable('table2');
CALL removeConstraintsForTable('table3');
CALL removeConstraintsForTable('table4');
COMMIT;
You have an extra BEGIN right at the start of your procedure, and the final END is missing a semicolon. You shouldn't really be using VARCHAR, and you could declare the cName variable using the data dictionary anyway; however an implicit loop will be simpler, as will using the concatenation operator || instead of nested CONCAT() calls, and the generated statement should not end in a semicolon:
create or replace procedure removeconstraintsfortable(p_table_name in varchar2) is
sql_stmt varchar2(2048);
begin
for rec in (
select owner, constraint_name
from all_constraints
where owner = sys_context('userenv','current_schema')
and table_name = p_table_name
and constraint_type = 'R'
)
loop
sql_stmt := 'ALTER TABLE "' || rec.owner || '"."' || p_table_name || '"'
|| ' DROP CONSTRAINT "' || rec.constraint_name || '"';
insert into temp(name) values(sql_stmt);
end loop;
commit;
end;
/
As pointed out in comments, the generated statement should be drop constraint.
I'm not sure why you're inserting into a table or where you execute the statement, but you can do it all in one if you prefer:
create or replace procedure removeconstraintsfortable(p_table_name in varchar2) is
sql_stmt varchar2(2048);
begin
for rec in (
select owner, constraint_name
from all_constraints
where owner = sys_context('userenv','current_schema')
and table_name = p_table_name
and constraint_type = 'R'
)
loop
sql_stmt := 'ALTER TABLE "' || rec.owner || '"."' || p_table_name || '"'
|| ' DROP CONSTRAINT "' || rec.constraint_name || '"';
dbms_output.put_line(sql_stmt);
execute immediate sql_stmt;
end loop;
end;
/
The dbms_output call just shows you the generated statement(s), before execute immediate executes it, well, immediately.
Quick demo; very basic table set-up:
create table t42 (id number primary key);
create table t43 (id number references t42 (id));
select table_name, constraint_name, constraint_type
from all_constraints
where table_name in ('T42', 'T43');
TABLE_NAME CONSTRAINT_NAME C
------------------------------ ------------------------------ -
T43 SYS_C00138153 R
T42 SYS_C00138152 P
Then call the procedure which shows the generated statement:
set serveroutput on
exec removeConstraintsForTable('T43');
ALTER TABLE "STACKOVERFLOW"."T43" DROP CONSTRAINT "SYS_C00138153"
PL/SQL procedure successfully completed.
and then check the constraint has gone:
select table_name, constraint_name, constraint_type
from all_constraints
where table_name in ('T42', 'T43');
TABLE_NAME CONSTRAINT_NAME C
------------------------------ ------------------------------ -
T42 SYS_C00138152 P
I fixed a number of syntax issues for you. Try this.
CREATE OR REPLACE PROCEDURE removeConstraintsForTable(vTableName IN varchar2) IS
cName VARCHAR2(30); -- identifiers are max 30 chars
sql_stmt VARCHAR2(2048);
CURSOR cur IS
SELECT DISTINCT CONSTRAINT_NAME
FROM USER_CONSTRAINTS
WHERE TABLE_NAME = vTableName AND CONSTRAINT_TYPE='R';
BEGIN
OPEN cur;
LOOP
FETCH cur INTO cName;
EXIT WHEN cur%notfound;
sql_stmt := 'ALTER TABLE ' || vTableName || ' DROP CONSTRAINT ' || cName;
INSERT INTO RANGERADMIN1.TEMP(Name) VALUES(sql_stmt);
END LOOP;
COMMIT;
END removeConstraintsForTable;
/
call removeConstraintsForTable('table1');
call removeConstraintsForTable('table2');
call removeConstraintsForTable('table3');
call removeConstraintsForTable('table4');
-- COMMIT; -- not necessary

PL/SQL nested loop (loop within a loop)

Below is a PL/SQL I'm working on
declare
v_sql varchar2(500);
BEGIN
for t in (
SELECT distinct ID
FROM TABLEB
) loop
for c in (
select * from (
select 'delete from ' as test
from dual
union all
select 'TABLEA'||' where ' as test
from dual
union all
select 'ID='||t.ID
from dual
)
) loop
v_sql := v_sql || c.test;
end loop;
dbms_output.put_line(v_sql);
end loop;
END;
/
The result I'm getting is this
delete from TABLEA where ID=1
delete from TABLEA where ID=1delete from TABLEA where ID=2
I want
delete from TABLEA where ID=1
delete from TABLEA where ID=2
Any help on the PLSQL will be appreciated
What is the purpose of the inner FOR loop? It does nothing that requires a loop, and can be simply rewritten like this:
declare
v_sql varchar2(500);
begin
for t in (select distinct id from tableb) loop
v_sql := 'delete from tablea where id = ' || t.id ||';';
dbms_output.put_line(v_sql);
end loop;
end;
/
BTW, it seems that you're missing the terminating semicolon in line v_sql := ...
Demonstration on HR's DEPARTMENTS table:
SQL> declare
2 v_sql varchar2(500);
3 begin
4 for t in (select distinct department_id id from departments) loop
5 v_sql := 'delete from tablea where id = ' || t.id ||';';
6 dbms_output.put_line(v_sql);
7 end loop;
8 end;
9 /
delete from tablea where id = 10;
delete from tablea where id = 20;
delete from tablea where id = 30;
delete from tablea where id = 40;
delete from tablea where id = 50;
delete from tablea where id = 60;
<snip>
You're not clearing the buffer after you've printed the statement, so you're appending the next statement to the first one. To clear the buffer, add
v_sql := NULL;
after the line which reads
dbms_output.put_line(v_sql);
Best of luck.

oracle read column names from select statement

I wonder how read column names in oracle. Ok, I know that there is table named USER_TAB_COLUMNS which gives info about it, but if I have 2 or 3 level nested query and I don't know column names. Or I just have simple query with join statement and i want to get column names. How to do that? any idey?
select * from person a
join person_details b where a.person_id = b.person_id
thanks
I would go for:
select 'select ' || LISTAGG(column_name , ',') within group (order by column_id) || ' from T1'
from user_tab_columns
where table_name = 'T1';
to get a query from database. To get columns with types to fill map you can use just:
select column_name , data_type
from user_tab_columns
where table_name = 'T1';
I assume you are looking for this:
DECLARE
sqlStr VARCHAR2(1000);
cur INTEGER;
columnCount INTEGER;
describeColumns DBMS_SQL.DESC_TAB2;
BEGIN
sqlStr := 'SELECT a.*, b.*, SYSDATE as "Customized column name"
FROM person a JOIN person_details b
WHERE a.person_id = b.person_id';
cur := DBMS_SQL.OPEN_CURSOR;
DBMS_SQL.PARSE(cur, sqlStr, DBMS_SQL.NATIVE);
DBMS_SQL.DESCRIBE_COLUMNS2(cur, columnCount, describeColumns);
FOR i IN 1..columnCount LOOP
DBMS_OUTPUT.PUT_LINE ( describeColumns(i).COL_NAME );
END LOOP;
DBMS_SQL.CLOSE_CURSOR(cur);
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;

Resources