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

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;

Related

Oracle query logic to identify email addresses across the dwh schemas

I was performing an activity to identify eMail addresses based on certain pattern (#xyz.de). I initially tried checking the DBA_TAB_COLS [data dictionary] view but this just finds email column names and I manually need to check the big list of tables. Instead of doing that, is there is a more effective way to just fetch the the pattern value #xyz.de ?
Database - oracle 11g
Query used
SET SERVEROUTPUT ON 100000
DECLARE
lv_count number(10):=0;
l_str varchar2 (1000);
lv_col_name varchar2(255) :='EMAIL';
BEGIN
FOR V1 IN
(select distinct table_name
from dba_tab_columns
where column_name = lv_col_name
order by table_name)
LOOP
dbms_output.put_line(lv_col_name||' '||v1.table_name);
END LOOP;
END;
Please note that
I don't exactly know the table or column names.
The value #xyz.de can be in any schema and any table and any column. This has to be identified but in an effective way.
Any suggestions?
i have used the above block query to fetch the email column along with the table name , but how can i achieve by searching certain value #xyz.de using the dynamic sql ?
I don't know what you want to do with the values that you are trying to extract, so the below code just prints them. Refer to PL/SQL Dynamic SQL from the Oracle documentation.
declare
type EMAILS is ref cursor;
L_CURSOR EMAILS;
cursor EMAIL_COLS is
select OWNER
,TABLE_NAME
,COLUMN_NAME
from DBA_TAB_COLS
where COLUMN_NAME like '%EMAIL%'
and OWNER <> 'SYS';
L_SQL varchar2(200);
L_EMAIL varchar2(500);
begin
for REC in EMAIL_COLS
loop
L_SQL := 'select ' || REC.COLUMN_NAME || ' from ' || REC.OWNER || '.' || REC.TABLE_NAME || ' where ' || REC.COLUMN_NAME || ' like ''%xyz.de''';
open L_CURSOR for L_SQL;
loop
fetch L_CURSOR into L_EMAIL;
exit when L_CURSOR%notfound;
DBMS_OUTPUT.PUT_LINE(L_EMAIL);
end loop;
end loop;
end;

Data not inserting to destination table

I have the following block of PL-SQL code in Oracle:
DECLARE TAB VARCHAR(100);
COL VARCHAR(100);
CURSOR C_COLS IS
select DISTINCT table_name, column_name
from all_tab_columns
where OWNER = 'MyDB' AND DATA_TYPE LIKE '%VARCHAR%';
BEGIN
OPEN C_COLS;
LOOP
FETCH C_COLS INTO TAB, COL;
EXIT WHEN C_COLS%notfound;
INSERT INTO TargetTable (TABLE_NAME, COLUMN_NAME, COLUMN_VALUE)
SELECT DISTINCT TAB,
COL,
(SELECT COL FROM TAB)
FROM TAB
WHERE REGEXP_LIKE(COL, '([ABCDEFGHIJKLMNOPQRSTUVWXYZ])\d\d\d\d\d\d([ABCDEFGHIJKLMNOPQRSTUVWXYZ])', 'ix');
END LOOP;
CLOSE C_COLS;
END;
The idea is to determine which tables in my rather large database contain a certain pattern of data and to find them.
So I want to return three columns: TableName, ColumnName, Value of ColumnName.
The above runs but returns no data and I can't understand why. The query in the cursor returns results, and if I hard code the table values into a simple select statement containing my Regex, I get results. I just want one result set that contains the thousands of results I expect.
Could it be the (SELECT COL FROM TAB) I'm using to dynamically find the column_value? I wasn't sure if I could express it this way.
If you want to select columns dynamically you may wish to try dynamic SQL.
DECLARE
w_sql VARCHAR2(32767);
BEGIN
DBMS_OUTPUT.enable(32767);
FOR s_cols IN (
select DISTINCT
table_name
, column_name
from all_tab_columns
where owner = 'MyDB'
AND data_type LIKE '%VARCHAR%'
)
LOOP
w_sql := q'!
INSERT
INTO TargetTable (TABLE_NAME, COLUMN_NAME, COLUMN_VALUE)
SELECT DISTINCT
':TAB'
, ':COL'
, :COL
FROM :TAB
WHERE REGEXP_LIKE(:COL, '([ABCDEFGHIJKLMNOPQRSTUVWXYZ])\d\d\d\d\d\d([ABCDEFGHIJKLMNOPQRSTUVWXYZ])', 'ix')
!';
w_sql := REPLACE(w_sql, ':TAB', s_cols.table_name);
w_sql := REPLACE(w_sql, ':COL', s_cols.column_name);
EXECUTE IMMEDIATE w_sql;
END LOOP;
COMMIT;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.put_line('Error for SQL :'|| w_sql ||'; error is :'|| SQLERRM);
END;

Find a Value in Table Using SQL Developer

I inherited a large database and nobody seems to know which table/column a particular data set is coming from. I've spent a lot of time going through table by table in Oracle's SQL Developer, but I can't find it. Is there a way in SQLDeveloper to search the entire table for a single value. Something like:
select table_name from all_tab_columns where column_value='desired value';
The db has around 1K+ tables each with lots of columns so manually combing through this isn't working.
You can use the following script to search for a value in all columns of your schema. The execution time for the script will depend on the number of tables in your schema and the number of rows in each of your table.
Replace 'abc' with the value which you intend to search. Also, right now the script will search all VARCHAR2 columns. You can also insert the table names and counts into a table instead of doing a DBMS_OUTPUT.PUT_LINE.
DECLARE
CURSOR cur_tables
IS
SELECT table_name,
column_name
FROM user_tab_columns
WHERE data_type = 'VARCHAR2';
v_sql VARCHAR2(4000);
v_value VARCHAR2(50);
v_count NUMBER;
BEGIN
v_value := 'abc';
FOR c_tables IN cur_tables LOOP
v_sql := 'SELECT count(1) FROM ' || c_tables.table_name || ' WHERE ' || c_tables.column_name || ' = :val' ;
EXECUTE IMMEDIATE v_sql INTO v_count USING v_value;
IF v_count > 0 THEN
DBMS_OUTPUT.PUT_LINE('Table Name ' || c_tables.table_name || ' Column Name ' || c_tables.column_name || ' Row Count ' || v_count);
END IF;
END LOOP;
END;

For loop with Table name in Stored Procedures

I am working on Oracle stored procedures.
My requirement is below
IF variable1 := 'true"
THEN
tableName=abr
ELSE
tableName=mvr
END IF;
FOR i IN (select unique(row1) as sc from tableName t where t.row2 = 'name') LOOP
BEGIN
-- required Logic
END
END LOOP;
But here I am not able to pass the table name in tableName parameter. How to do it?
You'll need to use Execute Immediate - it's designed for operations that aren't known until run time.
For normal operations, Oracle must know the tables and columns at compile time. You can't do SELECT * FROM tableName because it has no idea what tableName is and therefore it can't be compiled correctly.
Instead, you can do EXECUTE IMMEDIATE 'SELECT * FROM ' || tableName;
You can select your results INTO a variable, loop the result set, or BULK COLLECT into a structure and then iterate that.
For a simple select into, you can do this:
EXECUTE IMMEDIATE 'SELECT COL1, COL2 FROM ' || tableName INTO V_COL1, V_COL2
V_COL1 & V_COL2 are just local variables, tableName is a string representing your table name, and COL2 and COL2 are columns in the table you're selecting from. You can use the likes of ALL_TAB_COLUMNS to get the structure of a table dynamically.
Here is an example from Oracle docs:
CREATE OR REPLACE PROCEDURE query_invoice(
month VARCHAR2,
year VARCHAR2) IS
TYPE cur_typ IS REF CURSOR;
c cur_typ;
query_str VARCHAR2(200);
inv_num NUMBER;
inv_cust VARCHAR2(20);
inv_amt NUMBER;
BEGIN
query_str := 'SELECT num, cust, amt FROM inv_' || month ||'_'|| year
|| ' WHERE invnum = :id';
OPEN c FOR query_str USING inv_num;
LOOP
FETCH c INTO inv_num, inv_cust, inv_amt;
EXIT WHEN c%NOTFOUND;
-- process row here
END LOOP;
CLOSE c;
END;
/
http://docs.oracle.com/cd/B12037_01/appdev.101/b10795/adfns_dy.htm
You are going to have to build a for loop for each table then use your logic to determine which loop you will execute.

How to trim all columns in all rows in all tables of type string?

In Oracle 10g, is there a way to do the following in PL/SQL?
for each table in database
for each row in table
for each column in row
if column is of type 'varchar2'
column = trim(column)
Thanks!
Of course, doing large-scale dynamic updates is potentially dangerous and time-consuming. But here's how you can generate the commands you want. This is for a single schema, and will just build the commands and output them. You could copy them into a script and review them before running. Or, you could change dbms_output.put_line( ... ) to EXECUTE IMMEDIATE ... to have this script execute all the statements as they are generated.
SET SERVEROUTPUT ON
BEGIN
FOR c IN
(SELECT t.table_name, c.column_name
FROM user_tables t, user_tab_columns c
WHERE c.table_name = t.table_name
AND data_type='VARCHAR2')
LOOP
dbms_output.put_line(
'UPDATE '||c.table_name||
' SET '||c.column_name||' = TRIM('||c.column_name||') WHERE '||
c.column_name||' <> TRIM('||c.column_name||') OR ('||
c.column_name||' IS NOT NULL AND TRIM('||c.column_name||') IS NULL)'
);
END LOOP;
END;
Presumably you want to do this for every column in a schema, not in the database. Trying to do this to the dictionary tables would be a bad idea...
declare
v_schema varchar2(30) := 'YOUR_SCHEMA_NAME';
cursor cur_tables (p_schema_name varchar2) is
select owner, table_name, column_name
from all_tables at,
inner join all_tab_columns atc
on at.owner = atc.owner
and at.table_name = atc.table_name
where atc.data_type = 'VARCHAR2'
and at.owner = p_schema;
begin
for r_table in cur_tables loop
execute immediate 'update ' || r.owner || '.' || r.table_name
|| ' set ' || r.column_name || ' = trim(' || r.column_name ||');';
end loop;
end;
This will only work for fields that are VARCHAR2s in the first place. If your database contains CHAR fields, then you're out of luck, because CHAR fields are always padded to their maximum length.

Resources