这是一些测试数据:
SQL> select * from t23;
ID NAME JOB
---------- -------------------- --------------------
10 JACK JANITOR
20 JAN TUTOR
30 MOHAN JAZZ DANCER
40 JOAN MECHANIC
SQL>
我想用“MO”替换所有“JA”实例。这意味着我需要更新 NAME 和 JOB。显然我可以编写一个 UPDATE 语句,但我也可以使用数据字典的魔力生成一个:
SQL> select column_name, data_type
2 from user_tab_cols
3 where table_name = 'T23';
COLUMN_NAME DATA_TYPE
------------------------------ ----------
ID NUMBER
NAME VARCHAR2
JOB VARCHAR2
SQL>
这似乎是一个一次性的任务,为此我需要一个匿名的 PL/SQL 块而不是一个永久的过程。所以这是一个脚本,保存为gen_upd_stmt.sql。
declare
stmt varchar2(32767);
target_string varchar2(20) := 'JA';
replace_string varchar2(20) := 'MO';
begin
stmt := 'update t23 set ';
for lrec in ( select column_name
, row_number() over (order by column_id) as id
from user_tab_cols
where table_name = 'T23'
and data_type = 'VARCHAR2'
)
loop
if lrec.id > 1 then
stmt := stmt || ',';
end if;
stmt := stmt || lrec.column_name || '=replace('
|| lrec.column_name || ', ''' || target_string
|| ''',''' || replace_string
|| ''')';
end loop;
-- uncomment for debugging
-- dbms_output.put_line(stmt);
execute immediate stmt;
dbms_output.put_line('rows updated = '|| to_char(sql%rowcount));
end;
/
请注意,生成动态 SQL 是一个繁琐的过程,因为语法错误是在运行时而不是编译时引发的。转义引号可能特别有害。显示生成的语句以使调试更容易是一个好主意。
此外,我将目标列限制为具有正确数据类型的列。这不是绝对必要的,因为replace() 将为我们处理类型转换(在大多数情况下)。但是使用大表排除我们知道不匹配的列会更有效。
不管怎样,让我们滚吧!
SQL> set serveroutput on
SQL> @gen_upd_stmt
rows updated = 4
PL/SQL procedure successfully completed.
SQL>
正如预期的那样,所有四行都已更新,但并非全部更改:
SQL> select * from t23;
ID NAME JOB
---------- -------------------- --------------------
10 MOCK MONITOR
20 MON TUTOR
30 MOHAN MOZZ DANCER
40 JOAN MECHANIC
SQL>
为了完整起见,生成的语句是这样的:
update t23 set NAME=replace(NAME, 'JA','MO'),JOB=replace(JOB, 'JA','MO')
对于更大的表或更复杂的要求,我可能会使用chr(13)||chr(10) 引入换行符,以使生成的代码更具可读性(用于调试)。