A word of caution
If you are actually trying to replace a string value for every column in every table in your schema, please be aware that it is going to take hours, at least.
Disclaimer aside, similar scripts turn out to be useful in a lot of situations (maybe to change a column name, maybe to perform massive data cleaning, or to create your tables from a metadata table).
The general approach
Basically, you need to:
- Loop through every table in the target schema
- For each table, loop through every
VARCHAR2
column
- For each column, generate an
UPDATE
statement and execute it
A smarter solution generates only one UPDATE
statement for each table. Your solutions will then:
- Loop through every table in the target schema
- For each table, generate the first part of the
UPDATE
statement up to the SET
clause
- For each column, append the part of the
SET
clause to your statement
- Execute your statement
Example code for Oracle 10g/11g, using the well-known HR schema:
One UPDATE statement per column
DECLARE
schemaName VARCHAR2(30) := 'HR';
stmt VARCHAR2(32767); -- on 11g, use CLOB
BEGIN
FOR tr IN (
SELECT t.OWNER, t.TABLE_NAME
FROM ALL_TABLES t
WHERE t.OWNER = schemaName
ORDER BY 1, 2
)
LOOP
FOR cr IN (
SELECT c.COLUMN_NAME
FROM ALL_TAB_COLUMNS c
WHERE c.OWNER = tr.OWNER AND c.TABLE_NAME = tr.TABLE_NAME
AND c.DATA_TYPE = 'VARCHAR2'
ORDER BY 1
)
LOOP
stmt := 'UPDATE '||tr.OWNER||'.'||tr.TABLE_NAME
||' SET '||cr.COLUMN_NAME||' = REPLACE('||cr.COLUMN_NAME||', ''j'', ''b'')';
DBMS_OUTPUT.PUT_LINE(stmt||';'); -- useful for debugging
EXECUTE IMMEDIATE stmt;
END LOOP;
END LOOP;
END;
/
One UPDATE statement per table
You can try to be smarter and use only one UPDATE
statement for all the columns in a table. Be careful not to overflow the stmt
variable.
DECLARE
schemaName VARCHAR2(30) := 'HR';
stmt VARCHAR2(32767); -- on 11g, use CLOB
do_update BOOLEAN;
BEGIN
FOR tr IN (
SELECT t.OWNER, t.TABLE_NAME
FROM ALL_TABLES t
WHERE t.OWNER = schemaName
ORDER BY 1, 2
)
LOOP
do_update := FALSE;
stmt := 'UPDATE '||tr.OWNER||'.'||tr.TABLE_NAME||' SET ';
FOR cr IN (
SELECT c.COLUMN_NAME
FROM ALL_TAB_COLUMNS c
WHERE c.OWNER = tr.OWNER AND c.TABLE_NAME = tr.TABLE_NAME
AND c.DATA_TYPE = 'VARCHAR2'
ORDER BY 1
)
LOOP
do_update := TRUE;
stmt := stmt||cr.COLUMN_NAME||' = REPLACE('||cr.COLUMN_NAME||', ''j'', ''b''), ';
END LOOP;
IF do_update THEN
stmt := SUBSTR(stmt, 1, LENGTH(stmt) - 2); -- remove trailing ', '
DBMS_OUTPUT.PUT_LINE(stmt||';'); -- useful for debugging
EXECUTE IMMEDIATE stmt;
END IF;
END LOOP;
END;
/