pl/sql - can collection loop through column names? - sql

The output from the below code is:
|LAT|MISC|SID|NO
MIN_LENGTH|1|2|1|1
MAX_LENGTH|6|6|4|2
The output is as I expect, but is there anyway to loop through the columns using an index (ie. j) instead of doing RESULTS(I).MAX_LENGTH , RESULTS(I).MAX_LENGTH etc ? The concern is that when adding extra columns to the 'R_RESULT_REC' record, another loop is required.
set serveroutput on
DECLARE
TYPE R_RESULT_REC IS RECORD
(COL_NAME VARCHAR2(100),
MIN_LENGTH NUMBER,
MAX_LENGTH NUMBER
);
TYPE tr_RESULT IS TABLE OF R_RESULT_REC;
RESULTS TR_RESULT := TR_RESULT();
v_counter NUMBER := 1;
BEGIN
FOR J IN (SELECT DISTINCT COLUMN_NAME FROM ALL_TAB_COLUMNS
WHERE OWNER = 'SYSTEM'
and TABLE_NAME = 'SPECCHAR')
LOOP
RESULTS.EXTEND;
RESULTS(V_COUNTER).COL_NAME := J.COLUMN_NAME;
EXECUTE IMMEDIATE 'SELECT MIN(LENGTH('||J.COLUMN_NAME||')),
MAX(LENGTH('||J.COLUMN_NAME||'))
FROM '||'SYSTEM'||'.'||'SPECCHAR' INTO
RESULTS(V_COUNTER).MIN_LENGTH,
RESULTS(V_COUNTER).MAX_LENGTH;
V_COUNTER := V_COUNTER + 1;
END LOOP;
FOR I IN RESULTS.FIRST .. RESULTS.LAST LOOP
IF I = RESULTS.LAST THEN
DBMS_OUTPUT.PUT_LINE(RESULTS(I).COL_NAME);
ELSIF I = RESULTS.FIRST THEN
DBMS_OUTPUT.PUT(' |'||RESULTS(I).COL_NAME||'|');
ELSE
DBMS_OUTPUT.PUT(RESULTS(I).COL_NAME||'|');
END IF ;
END LOOP;
FOR I IN RESULTS.FIRST .. RESULTS.LAST LOOP
IF I = RESULTS.LAST THEN
DBMS_OUTPUT.PUT_LINE(RESULTS(I).MIN_LENGTH);
ELSIF I = RESULTS.FIRST THEN
DBMS_OUTPUT.PUT('MIN_LENGTH|'||RESULTS(I).MIN_LENGTH||'|');
ELSE
DBMS_OUTPUT.PUT(RESULTS(I).MIN_LENGTH||'|');
END IF ;
END LOOP;
FOR I IN RESULTS.FIRST .. RESULTS.LAST LOOP
IF I = RESULTS.LAST THEN
DBMS_OUTPUT.PUT_LINE(RESULTS(I).MAX_LENGTH);
ELSIF I = RESULTS.FIRST THEN
DBMS_OUTPUT.PUT('MAX_LENGTH|'||RESULTS(I).MAX_LENGTH||'|');
ELSE
DBMS_OUTPUT.PUT(RESULTS(I).MAX_LENGTH||'|');
END IF ;
END LOOP;
end;

This uses DBMS_SQL, so it's pretty snarly to read. The main reason I saw to use it was that I could get columnar descriptions of a SQL statement and to a buffer-based, not object-based fetch.
Rather than making calls to DBMS_OUTPUT during the processing, it builds a table of records for output, using associative arrays for simplicity.
It could further be refined to have an array or parsable list of functions to apply to each function, but that seems excess to current requirements. The nature of the code would require editing if new aggregation functions are being added.
Call overview (2c + a + s):
3 loops;
2 loops over column list (c),
1 loop over number of analytic functions (a).
1 SQL statement against table data (s).
OP's call overview (c*s + a + 1):
1 loop, executing a sql statement against table data per column (c*s)
a+1 loops, where a is the number of analytic functions
Test data:
1 select min(length(GP_ID)), max(length(GP_ID)),
2 min(length(GGP_ID)), max(length(GGP_ID)),
3 min(length(OBJECT_NAME)), max(length(OBJECT_NAME))
4* from AMUSCH.GP
SQL> /
MIN(LENGTH(GP_ID)) MAX(LENGTH(GP_ID)) MIN(LENGTH(GGP_ID))
MAX(LENGTH(GGP_ID)) MIN(LENGTH(OBJECT_NAME)) MAX(LENGTH(OBJECT_NAME))
1 7 1
4 9 41
Code:
declare
p_owner varchar2(30);
p_table_name varchar2(30);
TYPE OUTPUT_TAB_TYPE IS TABLE OF VARCHAR2(32767) index by binary_integer;
OUTPUT_TAB OUTPUT_TAB_TYPE;
l_columns_tab dbms_sql.desc_tab;
l_columns_cur integer;
l_columns_sql varchar2(32767);
l_columns_cnt number;
l_minmax_sql varchar2(32767);
l_minmax_cur integer;
l_minmax_tab dbms_sql.desc_tab;
l_minmax_cnt number;
l_fetch_ok number;
l_fetch_value number;
begin
p_owner := 'AMUSCH';
p_table_name := 'GP';
output_tab(1) := lpad(' ', 20, ' ');
output_tab(2) := lpad('MIN_LENGTH', 20, ' ');
output_tab(3) := lpad('MAX_LENGTH', 20, ' ');
l_columns_sql := 'select * from ' || p_owner || '.' || p_table_name ||
' where 1 = 0';
l_columns_cur := dbms_sql.open_cursor;
dbms_sql.parse (l_columns_cur, l_columns_sql, dbms_sql.native);
dbms_sql.describe_columns (l_columns_cur, l_columns_cnt, l_columns_tab);
-- build the min/max sql statement
l_minmax_sql := 'select ' ;
for i in 1..l_columns_cnt
loop
l_minmax_sql := l_minmax_sql ||
' min(length(' || l_columns_tab(i).col_name || ')), ';
l_minmax_sql := l_minmax_sql ||
' max(length(' || l_columns_tab(i).col_name || ')), ';
end loop;
l_minmax_sql := substr(l_minmax_sql, 1,
length(l_minmax_sql) - 2); -- trim trailing comma
l_minmax_sql := l_minmax_sql || ' from ' || p_owner || '.' || p_table_name;
l_minmax_cur := dbms_sql.open_cursor;
dbms_sql.parse (l_minmax_cur, l_minmax_sql, dbms_sql.native);
dbms_sql.describe_columns (l_minmax_cur, l_minmax_cnt, l_minmax_tab);
for i in 1..l_minmax_cnt
loop
dbms_sql.define_column(l_minmax_cur, i, l_fetch_value);
end loop;
l_fetch_ok := dbms_sql.execute(l_minmax_cur);
loop
l_fetch_ok := dbms_sql.fetch_rows(l_minmax_cur);
exit when l_fetch_ok = 0;
-- loop over the columns selected over
for i in 1..l_columns_cnt
loop
output_tab(1) := output_tab(1) || '|' || l_columns_tab(i).col_name;
dbms_sql.column_value(l_minmax_cur, (2*i-1), l_fetch_value);
output_tab(2) := output_tab(2) || '|' ||
lpad(l_fetch_value, length(l_columns_tab(i).col_name), ' ');
dbms_sql.column_value(l_minmax_cur, (2*i), l_fetch_value);
output_tab(3) := output_tab(3) || '|' ||
lpad(l_fetch_value, length(l_columns_tab(i).col_name), ' ');
end loop;
end loop;
if dbms_sql.is_open(l_minmax_cur) then
dbms_sql.close_cursor (l_minmax_cur);
end if;
if dbms_sql.is_open (l_columns_cur) then
dbms_sql.close_cursor (l_columns_cur);
end if;
for i in output_tab.first..output_tab.last
loop
dbms_output.put_line(output_tab(i));
end loop;
end;
/
Results:
|GP_ID|GGP_ID|OBJECT_NAME
MIN_LENGTH| 1| 1| 9
MAX_LENGTH| 7| 4| 41

If you want to use the DBMS_SQL package (which is sometimes very complex), then there is a DBMS_SQL.COLUMN_VALUE function that may work for you.
update:
Or even better: DBMS_SQL.DESC_REC
you can refer to:
http://docs.oracle.com/cd/B19306_01/appdev.102/b14258/d_sql.htm#i996963
notice example 8
I haven't tested it
update:
Perhaps what you really want is to loop on an Object type attributes and not a table column, so maybe you should try a different approach:
Make your type R_RESULT_REC an Object type in the DB and then you can loop on the query results:
SELECT attr_name
FROM user_type_attrs
WHERE type_name = 'R_RESULT_REC'
It's not like working with indexes but you still don't need to hard code the column names / type attributes
here is the code (based on yours):
CREATE OR REPLACE TYPE R_RESULT_REC AS OBJECT
(
COL_NAME VARCHAR2(100),
MIN_LENGTH NUMBER,
MAX_LENGTH NUMBER
);
/
and then:
DECLARE
TYPE tr_RESULT IS TABLE OF R_RESULT_REC;
RESULTS TR_RESULT := TR_RESULT();
v_counter NUMBER := 1;
v_max number;
v_min number;
BEGIN
FOR J IN (SELECT DISTINCT COLUMN_NAME
FROM ALL_TAB_COLUMNS
WHERE OWNER = 'SYSTEM'
and TABLE_NAME = 'SPECCHAR') LOOP
EXECUTE IMMEDIATE 'SELECT MIN(LENGTH(' || J.COLUMN_NAME || ')),
MAX(LENGTH(' || J.COLUMN_NAME || ')) FROM ' ||
'SPECCHAR'
INTO v_min, v_max;
RESULTS.EXTEND;
RESULTS(V_COUNTER) := new R_RESULT_REC(J.COLUMN_NAME, v_min, v_max);
V_COUNTER := V_COUNTER + 1;
END LOOP;
for r in (select attr_name
from all_type_attrs t
where t.owner = 'SYSTEM'
and t.type_name = 'R_RESULT_REC') loop
FOR I IN RESULTS.FIRST .. RESULTS.LAST LOOP
IF I = RESULTS.LAST THEN
execute immediate 'declare rec R_RESULT_REC := :0; begin' ||
' DBMS_OUTPUT.PUT_LINE(rec.' || r.attr_name || ');' ||
'end;'
using RESULTS(I);
ELSIF I = RESULTS.FIRST THEN
execute immediate 'declare rec R_RESULT_REC := :0; begin' ||
' DBMS_OUTPUT.PUT(''' || r.attr_name ||
'|'' || rec.' || r.attr_name || ' || ''|'');' ||
'end;'
using RESULTS(I);
ELSE
execute immediate 'declare rec R_RESULT_REC := :0; begin' ||
' DBMS_OUTPUT.PUT(rec.' || r.attr_name ||
' || ''|''); ' || 'end;'
using RESULTS(I);
END IF;
END LOOP;
end loop;
end;
If you'll add another attribute to the Record (and initiate it with values) , it will automatic display it.

Take advantage of Oracle's stats for this.
First, fully build stats on table using dbms_stats.gather_table_stats
Then, create the following function to help translate the raw low/high values that Oracle stores in all_tab_columns
create or replace function show_raw(i_raw raw, i_type varchar2)
return varchar2 is
l_varchar2 varchar2(32);
l_number number;
l_date date;
l_nvarchar2 nvarchar2(32);
l_rowid rowid;
l_char char;
begin
if (i_type = 'VARCHAR2') then
DBMS_STATS.CONVERT_RAW_VALUE(i_raw, l_varchar2);
return to_char(l_varchar2);
elsif(i_type = 'NUMBER') then
DBMS_STATS.CONVERT_RAW_VALUE(i_raw, l_number);
return to_char(l_number);
elsif(i_type = 'DATE') then
DBMS_STATS.CONVERT_RAW_VALUE(i_raw, l_date);
return to_char(l_date);
elsif(i_type = 'NVARCHAR2') then
DBMS_STATS.CONVERT_RAW_VALUE(i_raw, l_nvarchar2);
return to_char(l_nvarchar2);
elsif(i_type = 'ROWID') then
DBMS_STATS.CONVERT_RAW_VALUE(i_raw, l_rowid);
return to_char(l_rowid);
elsif(i_type = 'CHAR') then
DBMS_STATS.CONVERT_RAW_VALUE(i_raw, l_char);
return l_char;
else return 'Unknown type value';
end if;
end;
Then, just select the low/high values for each column:
select column_id,
column_name,
data_type,
show_raw(low_value, data_type) as min_val,
show_raw(high_value, data_type) as max_val
from all_tab_columns
where table_name = 'SOME_TABLE'
and owner = 'SOME_OWNER'
;

Related

Print a json structure from a dynamic query

I want to create function that return a CLOB with a json content.
But first i want to print the result (see query code) to see how it looks like.
The result must be a json, that will contain the information for 1 record just, this record select query, must be created dynamically.
So I try the next query, to resolve this problem. but i get an error.
Query
declare
v_stmt VARCHAR2(1000);
l_sep varchar2(1) := chr(10);
l_tab varchar2(1) := chr(9);
up_column VARCHAR2(50);
low_column VARCHAR2(50);
cur_asc SYS_REFCURSOR;
Vcdogcur SYS_REFCURSOR;
id_ccd number;
first boolean := true;
begin
id_ccd := 174;
v_stmt := 'select ';
open cur_asc for
SELECT column_name as up_column
,lower(column_name) as low_column
FROM USER_TAB_COLUMNS
WHERE table_name = upper('my_table');
loop
FETCH cur_asc
INTO up_column, low_column;
EXIT WHEN cur_asc%NOTFOUND;
if not first then
v_stmt := v_stmt || ',';
end if;
first := false;
v_stmt := v_stmt || l_sep || l_tab ||up_column || ' as ' || low_column;
END LOOP;
--v_stmt := v_stmt || l_sep || 'from my_table' || l_sep ||'where id = ' || id_ccd || ';';
v_stmt := v_stmt || l_sep || 'from my_table' || l_sep ||'where id = ' || id_ccd;
DBMS_OUTPUT.PUT_LINE('salida: ' || v_stmt);
CLOSE cur_asc;
--v_stmt := v_stmt || l_sep || 'from my_table' || l_sep ||'where id = ' || id_ccd;
--DBMS_OUTPUT.PUT_LINE('salida: ' || v_stmt);
open Vcdogcur for v_stmt;
APEX_JSON.initialize_clob_output;
APEX_JSON.open_object;
APEX_JSON.write('my_table', Vcdogcur);
APEX_JSON.close_object;
DBMS_OUTPUT.put_line(APEX_JSON.get_clob_output);
CLOSE Vcdogcur;
end;
/
DBMS output
salida: select
CREATED as created,
CREATED_BY as created_by,
UPDATED as updated,
UPDATED_BY as updated_by,
ID as id,
TEXT as text,
PART as part,
CODE as code,
DATE as date,
PERCENTAGE as percentage
from my_table
where id = 174
{
"my_table":[
{
"CREATED":"2020-06-04T08:10:09.000000000Z"
,"CREATED_BY":"APEX_APP"
,"UPDATED":"2020-06-04T10:10:09.295632000Z"
,"UPDATED_BY":"APEX_APP"
,"ID":174
,"TEXT":"ALL"
,"PART":2770
,"CODE":2212
,"DATE":"2017-01-01T00:00:00.000000000Z"
,"PERCENTAGE":-4.98
}
]
}
Error
Informe de error -
ORA-01001: cursor no válido
ORA-06512: en línea 44
01001. 00000 - "invalid cursor"
*Cause:
*Action:
Can somebody help me to resolve this?
Best Regards

PL/SQL CRUD matrix from a source code

I am trying to create a CRUD matrix for my function from a source code of that function, so I created a procedure that will read a source code.
create or replace procedure test_
IS
CURSOR c_text is
SELECT USER_SOURCE.TEXT
FROM USER_SOURCE
WHERE USER_SOURCE.name='TEST_FUNCTION'
AND USER_SOURCE.type='FUNCTION';
order by line;
v_single_text varchar2(4000);
v_tmp_text varchar2(10000) := ' ';
begin
open c_text;
loop
fetch c_text into v_single_text;
exit when c_text%notfound;
v_tmp_text := v_tmp_text|| chr(10) || rtrim(v_single_text);
dbms_output.put_line(v_single_text);
end loop;
close c_text;
end test_;
And that works very good for me, I get the source code of my desired function. It's a very simple function and I use this to learn PL/SQL. Output of that procedure look's like this.
function test_funkction Return varchar2
IS
kpp_value varchar2(20);
begin
select KPP
into kpp_value
from CUSTOMER
where CUSTOMER_ID = 200713;
dbms_output.put_line (kpp_value);
Return kpp_value;
end test_function;
Now, how to parse the string I've got in the output to get a desired result, my result should be like this
==TABLE_NAME==========OPERATIONS==
CUSTOMER - R - -
==================================
Now I have managed to do it.
But it will only work with my simple function, now I want to make a procedure that will work with any function.
Source code below.
create or replace procedure test_
IS
v_string_fnc varchar2(10000) := UPPER('function test_function
Return varchar2
IS
kpp_value varchar2(20);
begin
select KPP into kpp_value from CUSTOMER where CUSTOMER_ID = 200713;
dbms_output.put_line (kpp_value);
Return kpp_value;
end test_function;');
v_check PLS_INTEGER;
CURSOR c_text is
SELECT USER_SOURCE.TEXT
FROM USER_SOURCE
WHERE USER_SOURCE.name = 'TEST_FUNCTION'
AND USER_SOURCE.type = 'FUNCTION'
order by line;
v_single_text varchar2(4000);
v_tmp_text varchar2(10000) := ' ';
/*v_string varchar2(10000);*/
insert_flag char := '-';
read_flag char := '-';
update_flag char := '-';
delete_flag char := '-';
empty_space char(34) := ' ';
underline char(42) := '==========================================';
/*v_txt varchar2(10000) := ' ';*/
result_table varchar2(1000) := '/';
begin
open c_text;
loop
fetch c_text
into v_single_text;
exit when c_text%notfound;
v_tmp_text := v_tmp_text || chr(10) || rtrim(v_single_text);
/* print source code*/
/*dbms_output.put_line(v_single_text);*/
end loop;
close c_text;
/*DELETE SEARCH*/
v_check := instr(v_string_fnc, 'DELETE ');
if v_check < 1 then
dbms_output.put_line('THERE IS NO DELETE COMMAND');
else
dbms_output.put_line('THERE IS A DELETE COMMAND');
delete_flag := 'D';
v_check := instr(v_string_fnc, 'FROM ');
v_check := v_check + 5;
result_table := substr(v_string_fnc, v_check);
result_table := substr(result_table, 0, instr(result_table, ' '));
dbms_output.put_line('TABLE AFFECTED BY DELETE: ' || result_table);
end if;
/*SELECT SEARCH*/
v_check := instr(v_string_fnc, 'SELECT ');
if v_check < 1 then
dbms_output.put_line('THERE IS NO READ COMMAND');
else
dbms_output.put_line('THERE IS A READ COMMAND');
read_flag := 'R';
v_check := instr(v_string_fnc, 'FROM ');
v_check := v_check + 5;
result_table := substr(v_string_fnc, v_check);
result_table := substr(result_table, 0, instr(result_table, ' '));
dbms_output.put_line('TABLE AFFECTED BY READ: ' || result_table);
end if;
/*UPDATE SEARCH*/
v_check := instr(v_string_fnc, 'UPDATE ');
if v_check < 1 then
dbms_output.put_line('THERE IS NO UPDATE COMMAND');
else
dbms_output.put_line('THERE IS A UPDATE COMMAND');
update_flag := 'U';
v_check := instr(v_string_fnc, 'FROM ');
v_check := v_check + 5;
result_table := substr(v_string_fnc, v_check);
result_table := substr(result_table, 0, instr(result_table, ' '));
dbms_output.put_line('TABLE AFFECTED BY UPDATE: ' || result_table);
end if;
/*INSERT SEARCH*/
v_check := instr(v_string_fnc, 'INSERT ');
if v_check < 1 then
dbms_output.put_line('THERE IS NO CREATE COMMAND');
else
dbms_output.put_line('THERE IS A CREATE COMMAND');
insert_flag := 'C';
v_check := instr(v_string_fnc, 'FROM ');
v_check := v_check + 5;
result_table := substr(v_string_fnc, v_check);
result_table := substr(result_table, 0, instr(result_table, ' '));
dbms_output.put_line('TABLE AFFECTED BY CREATE: ' || result_table);
end if;
dbms_output.put_line(' ');
dbms_output.put_line('==========' || 'TABLE_NAME' || '==========' ||
'OPERATIONS' || '==');
dbms_output.put_line(empty_space || insert_flag || read_flag ||
update_flag || delete_flag);
dbms_output.put_line(underline);
end test_;
With that procedure I can extract and output my code, dbms needs a bit clean up but it will give the result I need.
Now a few questions, how to put a source code of my function to a variable that is not predefined, here is v_string_fnc but it needs to be predefined to work.
And how to link a certain operation with the table, here in my example is easy, one SELECT and keyword FROM that gives me a name of table.
Struggling continues
The bigger part it's done, just a tuning after this.
v_check := instr2(v_string_fnc, 'DROP ');
if v_check > 0 then
delete_flag := 'D';
v_check := instr2(v_string_fnc, 'TABLE ', v_check);
v_check := v_check + 6;
result_table := substr(v_string_fnc, v_check);
rest_string := result_table;
result_table := substr(result_table, 0, instr(result_table, ' '));
result_table := rtrim(result_table);
result_table := rtrim(result_table, ';');
merge into result_set
using dual
on (tables_used = result_table)
when matched then
update set drop_operation = delete_flag
when not matched then
insert
(tables_used, drop_operation)
values
(result_table, delete_flag);
while v_check > 0 loop
v_check := instr2(rest_string, 'DROP ');
if v_check > 0 then
delete_flag := 'D';
v_check := instr2(rest_string, 'TABLE ', v_check);
v_check := v_check + 6;
result_table := substr(rest_string, v_check);
rest_string := result_table;
result_table := substr(result_table, 0, instr(result_table, ' '));
result_table := rtrim(result_table);
result_table := rtrim(result_table, ';');
merge into result_set
using dual
on (tables_used = result_table)
when matched then
update set drop_operation = delete_flag
when not matched then
insert
(tables_used, drop_operation)
values
(result_table, delete_flag);
end if;
end loop;
end if;

Selection of columns in stored procedure based on parameters

I need to check the parameters in the stored procedure if it is entered then I need to select that
i=j=k=l=m=1;
IF (p_plant_cd IS NULL) THEN
i=0;
END IF;
IF(p_global_duns_nbr IS NULL) THEN
j=0
END IF;
IF(p_global_duns_nbr IS NULL) THEN
k=0
END IF;
IF(p_matrl_grp IS NULL) THEN
l=0
END IF;
IF (p_mrp IS NULL) THEN
m=0
END IF ;
Which ever value is 1 I need to add corresponding parameters in the variable v_select
For eg ;
if k and l are 1 then
v_select='p_global_duns_nbr,p_matrl_grp'
Pls suggest me how to do this.
You can declare a variable , holding column names , then concat to select statement and you have SQL select statement in v_select , use it as you want
declare
v_columns varchar2(255);
v_select varchar2(2000);
v_result sys_refcursor;
begin
v_columns := 'rownum';
IF p_plant_cd = 1 THEN
v_columns := v_columns || ',p_plant_cd';
END IF;
IF p_global_duns_nbr = 1 THEN
v_columns := v_columns || ',p_global_duns_nbr';
END IF;
IF p_global_duns_nbr = 1 THEN
v_columns := v_columns || ',p_global_duns_nbr';
END IF;
IF p_matrl_grp = 1 THEN
v_columns := v_columns || ',p_matrl_grp';
END IF;
IF p_mrp = 1 THEN
v_columns := v_columns || ',p_mrp';
END IF;
v_select := 'SELECT ' || v_columns || ' FROM table';
open v_result for v_select;
end;

Finding specific data in Oracle Tables

I needed to find a value for a column in my oracle database but i don't know which
table or column it's stored in
How can I search for a specific or like %% data as I do in
select * from SYS.dba_source
is there a table like that
Column Name ID Data Type Null? Comments
OWNER 1 VARCHAR2 (30 Byte) Y
NAME 2 VARCHAR2 (30 Byte) Y Name of the object
TYPE 3 VARCHAR2 (12 Byte) Y
Type of the object:
"TYPE", "TYPE BODY", "PROCEDURE", "FUNCTION",
"PACKAGE", "PACKAGE BODY" or "JAVA SOURCE"
LINE 4 NUMBER Y Line number of this line of source
TEXT 5 VARCHAR2 (4000 Byte) Y Source text
LINK: pl/sq to find any data in a schema
Imagine, there are a few tables in your schema and you want to find a specific value in all columns within these tables. Ideally, there would be an sql function like
select * from * where any(column) = 'value';
Unfortunately, there is no such function.
However, a PL/SQL function can be written that does that. The following function iterates over all character columns in all tables of the current schema and tries to find val in them.
create or replace function find_in_schema(val varchar2)
return varchar2 is
v_old_table user_tab_columns.table_name%type;
v_where Varchar2(4000);
v_first_col boolean := true;
type rc is ref cursor;
c rc;
v_rowid varchar2(20);
begin
for r in (
select
t.*
from
user_tab_cols t, user_all_tables a
where t.table_name = a.table_name
and t.data_type like '%CHAR%'
order by t.table_name) loop
if v_old_table is null then
v_old_table := r.table_name;
end if;
if v_old_table <> r.table_name then
v_first_col := true;
-- dbms_output.put_line('searching ' || v_old_table);
open c for 'select rowid from "' || v_old_table || '" ' || v_where;
fetch c into v_rowid;
loop
exit when c%notfound;
dbms_output.put_line(' rowid: ' || v_rowid || ' in ' || v_old_table);
fetch c into v_rowid;
end loop;
v_old_table := r.table_name;
end if;
if v_first_col then
v_where := ' where ' || r.column_name || ' like ''%' || val || '%''';
v_first_col := false;
else
v_where := v_where || ' or ' || r.column_name || ' like ''%' || val || '%''';
end if;
end loop;
return 'Success';
end;

pl sql and dynamic sql

I am trying to create some dynamic sql using the following code block
firstSqlStatement := true;
updateText := 'UPDATE T_EMPLOYEES SET ';
if FIRSTNAME IS NOT NULL and FIRSTNAME > 0 THEN
updateText:=updateText || ' firstName=' || FIRSTNAME || ' ';
firstSqlStatement := false;
end if;
if MIDDLENAME IS NOT NULL and length(MIDDLENAME) > 0 THEN
if firstSqlStatement = false THEN
updateText:=updateText || ',';
end if;
updateText:=updateText || ' middleName=' || MIDDLENAME || ' ';
firstSqlStatement := false;
end if;
updateText:=updateText
|| ' where upper(id)=upper(' || ID ||');';
DBMS_OUTPUT.put_line(updateText);
EXECUTE IMMEDIATE updateText;
The statement never executes properly as there are missing single quotes around values.
Any ideas what i can do to make this small example work or is there any better way of doing this?
firstSqlStatement := true;
updateText := 'UPDATE T_EMPLOYEES SET ';
if FIRSTNAME IS NOT NULL and FIRSTNAME > 0 THEN
updateText:=updateText || ' firstName=''' || FIRSTNAME || ''' ';
firstSqlStatement := false;
end if;
if MIDDLENAME IS NOT NULL and length(MIDDLENAME) > 0 THEN
if firstSqlStatement = false THEN
updateText:=updateText || ',';
end if;
updateText:=updateText || ' middleName=''' || MIDDLENAME || ''' ';
firstSqlStatement := false;
end if;
updateText:=updateText || ' where upper(id)=upper(' || ID || ');';
DBMS_OUTPUT.put_line(updateText);
EXECUTE IMMEDIATE updateText;
use '''
Maybe you can do it this way.
declare
ll_employee_id number := 10;
lv_firstname varchar2(30) := 'Thomas';
lv_middlename varchar2(30) := null;
begin
update t_employees
set firstname = decode(lv_firstname, null, firstname, lv_firstname),
middlename = decode(lv_middlename, null, middlename, lv_middlename)
where employee_id = ll_employee_id;
end;
DECLARE
my_error exception;
sql_stmt VARCHAR2 (500);
v_char_field VARCHAR2 (500);
v_number_field NUMBER;
v_stmt_number NUMBER;
BEGIN
sql_stmt := 'UPDATE TABLE';
IF 1 = 1 -- REPLACE WITH CONDITION FOR SELECTION FIELD
THEN
sql_stmt := sql_stmt || 'field_1 = :1';
v_stmt_number := 1;
ELSIF 1 = 1 -- REPLACE WITH CONDITION FOR SELECTION FIELD
THEN
sql_stmt := sql_stmt || 'field_2 = :1';
v_stmt_number := 2;
ELSE
DBMS_OUTPUT.put_line ('Field unmanaged');
RAISE my_error;
END IF;
IF 1 = 1 -- REPLACE WITH CONDITION FOR SELECTION TYPE FIELD
THEN
EXECUTE IMMEDIATE sql_stmt USING v_char_field;
ELSIF 1 = 1 -- REPLACE WITH CONDITION FOR SELECTION TYPE FIELD
THEN
EXECUTE IMMEDIATE sql_stmt USING v_number_field;
ELSE
DBMS_OUTPUT.put_line ('Type Field unmanaged');
RAISE my_error;
END IF;
DBMS_OUTPUT.PUT_LINE ('STATEMENT NUMBER : ' || v_stmt_number);
DBMS_OUTPUT.PUT_LINE ('TOTAL RECORD UPDATE : ' || SQL%ROWCOUNT);
EXCEPTION
WHEN my_error
THEN
NULL;
WHEN OTHERS
THEN
DBMS_OUTPUT.PUT_LINE ('ERROR :' || SQLERRM);
END;
You can use multiple selections to compose your statement for selection fields to be updated and for the type.