Find all join referenced tables in SQL - oracle

I need to extract from V$SQL all the objects (tables or views) that are referenced in all the joins of the sql.
For example: if i have this sql in the V$SQL
select * from ADANPT
join BTABP on column1=column2
left outer join HSTUDI on column3=column4
full outer join TERW on column5=column6;
i need to extract this:
BTABP
HSTUDI
TERW
I i have just one join in my sql, it is quite simple, using substr and instr functions (so that i extract the string from JOIN to ON words).
But i can't find a way if i have 2 or more joins. Maybe with loop?

You could use the SQL_ID and take advantage of the explain plan functionality:
DECLARE
l_buff VARCHAR2(32000);
BEGIN
FOR sql_query IN (SELECT sql_ID
FROM v$sql
)
LOOP
l_buff := NULL;
dbms_output.put_line('---------------'||sql_query.sql_ID||'---------------');
FOR text_line IN (SELECT sql_text
FROM v$sqltext
WHERE sql_id = sql_query.sql_id
ORDER BY piece ASC)
LOOP
l_buff := l_buff || text_line.sql_text;
END LOOP;
dbms_output.put_line(l_buff);
EXECUTE IMMEDIATE 'explain plan for '|| l_buff;
dbms_output.put_line('---------------TABLES---------------');
FOR table_name IN (SELECT DISTINCT object_name
FROM plan_table
WHERE object_type = 'TABLE')
LOOP
dbms_output.put_line(table_name.object_name);
END LOOP;
ROLLBACK;
END LOOP;
END;

Related

Is there a way to make a PLSQL script that lists all columns that IS NULL for every record in a table?

I am working with a huge database with several columns and records. I want to browse a specific table and make a list of the columns that are empty for every record.
Is this possible without refering to all the specific column names?
Thanks for help!
It's possible but if you have a lot data it will last a long time.
create table xxx as select * from dba_objects where rownum < 10000;
prepare test table get table stats. It can be long lasting process.
begin
dbms_stats.gather_table_stats(user,'XXX',estimate_percent =>100);
-- ..
-- others tables to analizye
end;
Generate reports.
select table_name,column_name from user_tab_cols where coalesce(low_value,high_value) is null and table_name in('XXX');
You can use the below script to find out the null columns in your database -
DECLARE
COUNT_COL INT;
SQL_STR VARCHAR2(100);
BEGIN
FOR I IN (SELECT OBJECT_NAME, COLUMN_NAME
FROM USER_OBJECTS UO
JOIN USER_TAB_COLS UTC ON UO.OBJECT_NAME = UTC.TABLE_NAME) LOOP
SQL_STR := 'SELECT COUNT(1) FROM ' || I.OBJECT_NAME || ' WHERE ' || i.COLUMN_NAME || ' IS NOT NULL';
EXECUTE IMMEDIATE SQL_STR INTO COUNT_COL;
IF COUNT_COL = 0 THEN
DBMS_OUTPUT.PUT_LINE(I.COLUMN_NAME);
END IF;
END LOOP;
END;
Here is the fiddle.
Try for all record in table:
SELECT a.owner, a.table_name, b.column_name
FROM all_tables a, all_tab_columns b
WHERE a.table_name = '<TABLE_NAME>'
AND a.table_name = b.table_name
AND a.num_rows = b.num_nulls
For all table
SELECT a.owner, a.table_name, b.column_name
FROM all_tables a, all_tab_columns b
WHERE a.table_name = b.table_name
AND a.num_rows = b.num_nulls

Identify and retrieve values of all varchar columns in a Oracle database

I have the following query that gives me a result set of all tables and columns in my Oracle database of VARCHAR columns:
SELECT ATC.OWNER, ATC.TABLE_NAME, ATC.COLUMN_NAME
FROM all_tab_columns ATC
WHERE DATA_TYPE LIKE '%VARCHAR%'
To this I want to add a 4th column that displays the value of ATC.COLUMN_NAME. Is there an easy way of doing this?
I thought of doing a join to a SQL statement that loops through ATC.COLUMN_NAME and outputting the value. The join would be done on the table name.
I don't know if I'm complicating it and I can't think of the SQL. I've tried declaring the above statement in a variable and then using a CTE to interrogate it but I would still need to loop through the table_name and column_name values.
Is there a simpler way?
Edit: Sample data
You need to use dynamic SQL. this is a proof of concept, it will not scale well when run against a large database.
declare
stmt varchar2(32767);
val varchar2(4000);
rc sys_refcursor;
begin
for r in ( SELECT ATC.OWNER, ATC.TABLE_NAME, ATC.COLUMN_NAME
FROM all_tab_columns ATC
WHERE DATA_TYPE LIKE '%VARCHAR%' )
loop
stmt := ' select distinct '|| r.column_name ||
' from '|| r.owner||'.'||r.table_name;
open rc for stmt;
loop
fetch rc in val;
exit when rc%notfound;
dbms_output.put_line ( r.owner||'.'||r.table_name ||'.'|| r.column_name
||': '|| val );
end loop;
end loop;
end;

Fetch subquery value from cursor to parameter in PL/SQL

I have below query to select the count of subquery:
SELECT COUNT(*) FROM (select crs_cust.CUSTOMER_ID,
subset.NEW_REFERENCE_ID FROM CRS_CUSTOMERS crs_cust INNER JOIN DAY0_SUBSET subset ON crs_cust.CUSTOMER_ID=subset.CURRENT_CUSTOMER_ID);
Above query is executed from a cursor in PL/SQL, how can I fetch columns from subquery (CUSTOMER_ID and NEW_REFERENCE_ID) into respective parameter?
The cursor is expected to have multiple records.Similiar like the following:
p_Count := SELECT COUNT(*) FROM DAY0_SUBSET;
OPEN c1;
LOOP
FETCH c1 into p_Current_CustomerId,p_New_Cust_Ref_ID; -->query from cursor's subquery
EXIT WHEN c1%NOTFOUND;
EXIT WHEN (c1%ROWCOUNT <> p_Count);
FOR i IN c1 LOOP
<do manipulation of subquery values>
END LOOP;
END IF;
CLOSE c1;
The columns of sub queries are not projected so you can't reference them. If you want the CUSTOMER_ID and NEW_REFERENCE_ID in your program you will have to select them in the top level SELECT clause.
The easiest answer to your solution is to just open a cursor for your
subquery itself, for eample:
BEGIN
FOR cur IN (SELECT crs_cust.CUSTOMER_ID, subset.NEW_REFERENCE_ID
FROM CRS_CUSTOMERS crs_cust
INNER JOIN DAY0_SUBSET subset ON
crs_cust.CUSTOMER_ID=subset.CURRENT_CUSTOMER_ID)
LOOP
DBMS_OUTPUT.PUT_LINE(cur.customer_id || ', ' || cur.new_reference_id);
END LOOP;
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;

Returning a value from a select statement through EXECUTE IMMEDIATE

I've searched for some examples of how return a value from a select statement using the execute immediate, but it keeps returning me an error "ORA-00903: invalid table name".
Here is a sample of the code:
DECLARE
LAST_NUMBER NUMBER;
CURSOR C_SEQUENCES IS
SELECT
A.SEQUENCE_NAME, B.TABLE_NAME, C.COLUMN_NAME
FROM
USER_SEQUENCES A
INNER JOIN
USER_CONSTRAINTS B ON A.SEQUENCE_NAME = REPLACE(B.CONSTRAINT_NAME, 'PK', 'SEQ_ID')
INNER JOIN
USER_CONS_COLUMNS C ON B.CONSTRAINT_NAME = C.CONSTRAINT_NAME
WHERE
B.CONSTRAINT_TYPE = 'P';
BEGIN
FOR REG IN C_SEQUENCES LOOP
EXECUTE IMMEDIATE 'SELECT MAX(:1) FROM :2' INTO LAST_NUMBER USING REG.COLUMN_NAME, REG.TABLE_NAME;
END LOOP;
END;
Yes, the table does exists.
Any ideas?
I think the only way is to concatenate those both variables:
EXECUTE IMMEDIATE 'SELECT MAX('||REG.COLUMN_NAME||') FROM '||REG.TABLE_NAME INTO LAST_NUMBER;
And I also suggest you to make a validation function for the column and table names in case of SQL injection.

Resources