I have created following query. However, I got error below. I think that error is related to TO_CHAR(' AND MARITAL_STATUS in ('Married-civ-spouse') ') line. How can I define this other ways?
Thanks in advance.
Error:
PLS-00103: Encountered the symbol ") " when expecting one of the following:
. ( ) , * # % & = - + < / > at in is mod remainder not rem =>
<an exponent (**)> <> or != or ~= >= <= <> and or like like2
like4 likec between || member
06550. 00000 - "line %s, column %s:\n%s"
This is my code:
SET SERVEROUTPUT ON;
DECLARE
KATSAYI1 VARCHAR2(1000):=TO_CHAR('CREATE TABLE DQ_DNM2 AS SELECT * FROM DQ_TEST_DATA WHERE YEARMONTH=');
KATSAYI2 varchar2(100) := TO_CHAR(sysdate,'YYYY');
KATSAYI3 VARCHAR2(100) := TO_CHAR(KATSAYI2-2);
KATSAYI4 varchar2(100) := to_char(sysdate, 'MM');
KATSAYI7 varchar2(100) := to_char(KATSAYI4 + 11);
KATSAYI5 varchar2(100) := to_char(CONCAT(KATSAYI3,KATSAYI7));
KATSAYI6 VARCHAR2(1000):=TO_CHAR(CONCAT(KATSAYI1,KATSAYI5));
KATSAYI8 VARCHAR2(100) :=TO_CHAR(' AND MARITAL_STATUS in ('Married-civ- spouse') ');
KATSAYI9 varchar2(100) := to_char(CONCAT(KATSAYI6,KATSAYI8));
BEGIN
dbms_output.put_line( KATSAYI5);
DBMS_OUTPUT.PUT_LINE('İlgili tablo yaratıldı.');
EXECUTE IMMEDIATE KATSAYI9;
END;
/code here
Use chr(39) instead of '
select 'That'||chr(39)||'s the right way' from dual
will give you
That's the right way
You can use q'[ ]' to escape all characters and new lines
Example:
select q'[That's the right way also "" '' !##$%^&*()_=+
anything you want... ]' from dual
That's the right way also "" '' !##$%^&*()_=+
anything you want...
This approach is good for dynamic code execution, where you don't need to worry about escaping variables and concatenations; you just write your code in the same format of what will be executed. better in readability of your code.
You appear to be taking 2 off the years and adding 11 to the months (so in effect taking off 13 months):
SET SERVEROUTPUT ON;
DECLARE
p_yyyymm char(6) := TO_CHAR( ADD_MONTHS( SYSDATE, -13 ), 'YYYYMM');
BEGIN
DBMS_OUTPUT.PUT_LINE( p_yyyymm );
DBMS_OUTPUT.PUT_LINE('İlgili tablo yaratıldı.');
EXECUTE IMMEDIATE 'CREATE TABLE DQ_DNM2 AS SELECT * FROM DQ_TEST_DATA WHERE YEARMONTH=''' || p_yyyymm || ''' AND MARITAL_STATUS in (''Married-civ- spouse'')';
END;
Thanks for your answers. Using double quotes worked in this case. Before opening this thread, I tried to use (") . Dont use that. Use double quotes.
Related
For the below procedure I am getting the ora error as mentioned in the title while running in Oracle SQL developer
DECLARE
sqlstr VARCHAR2(1000);
CURSOR TabSubPartitions IS
SELECT TABLE_NAME, PARTITION_NAME
FROM USER_TAB_PARTITIONS
WHERE TABLE_NAME = 'PART_TABLE'
ORDER BY PARTITION_NAME;
BEGIN
FOR aSubPart IN TabSubPartitions LOOP
IF TRUNC(LAST_DAY(SYSDATE)) = '31-07-2020' THEN
sqlstr := 'ALTER TABLE '||aSubPart.TABLE_NAME||' MODIFY PARTITION '||aSubPart.PARTITION_NAME|| ' ADD SUBPARTITION ' ||aSubPart.PARTITION_NAME||'_'||TO_CHAR(TRUNC(LAST_DAY(SYSDATE))+1, 'MON_YYYY')||' VALUES LESS THAN ( '||TO_DATE(TRUNC(LAST_DAY(SYSDATE))+2, 'SYYYY-MM-DD HH24:MI:SS' , 'NLS_CALENDER=GREGORIAN')||')' ;
EXECUTE IMMEDIATE sqlstr;
END IF;
END LOOP;
END;
Could anyone please help me. Many thanks in advance.
The main issue in the block itself is is here:
'NLS_CALENDER=GREGORIAN'
where it must be
'NLS_CALENDAR=GREGORIAN'
Moreover, you should not rely on implicite date conversions.
But there is also an issue with your resulting ALTER TABLE statement, which looks something like
ALTER TABLE MODIFY PARTITION ... VALUES LESS THAN ( 02.08.2020 00:00:00)
depending on session date settings (again because of implict date conversion). I doubt that Oracle accepts a timestamp like this. Make sure you produce a proper date literal (like DATE '2020-08-02') instead.
The whole corrected code:
DECLARE
sqlstr VARCHAR2(1000);
CURSOR TabSubPartitions IS
SELECT TABLE_NAME, PARTITION_NAME
FROM USER_TAB_PARTITIONS
WHERE TABLE_NAME = 'PART_TABLE'
ORDER BY PARTITION_NAME;
BEGIN
FOR aSubPart IN TabSubPartitions LOOP
IF TRUNC(LAST_DAY(SYSDATE)) = DATE '2020-07-31' THEN
sqlstr :=
'ALTER TABLE ' || aSubPart.TABLE_NAME || ' MODIFY PARTITION ' || aSubPart.PARTITION_NAME ||
' ADD SUBPARTITION ' || aSubPart.PARTITION_NAME || '_' || TO_CHAR(TRUNC(LAST_DAY(SYSDATE))+1, 'MON_YYYY') ||
' VALUES LESS THAN (DATE ''' || TRIM(TO_CHAR(TRUNC(LAST_DAY(SYSDATE))+2, 'SYYYY-MM-DD', 'NLS_CALENDAR=GREGORIAN')) || ''')' ;
EXECUTE IMMEDIATE sqlstr;
END IF;
END LOOP;
END;
I have several pieces of advice.
First, modify your code to output the value of sqlstr, so you can see exactly what the SQL statement is that you are trying to execute. This should make it easier to understand where the syntax error is.
Second, change TRUNC(LAST_DAY(SYSDATE)) = '31-07-2020' to use an explicit date format when converting from date to string. Something like TO_CHAR(TRUNC(LAST_DAY(SYSDATE)), 'DD-MM-YYYY') .... I don't think this is related to your error, but it is better practice than relying on the implicit conversion format.
Third, look carefully at the TO_DATE call. As it is you appear to be calling TO_DATE with date parameter, then implicitly converting that back to a string. At best that's not necessary, and at worst it will cause unexpected behavior. I suspect you may simply mean to use TO_CHAR where you currently have TO_DATE.
Whenever the length of string l_long_string is above 4000 characters, the following code is throwing an error:
ORA-01460: unimplemented or unreasonable conversion requested
Instead of the nested regexp_substr query, when I try to use
SELECT column_value
FROM TABLE(l_string_coll)
it throws:
ORA-22905: cannot access rows from a non-nested table item
How can I modify the dynamic query?
Notes:
- l_string_coll is of type DBMS_SQL.VARCHAR2S, and comes as input to my procedure (here, i have just shown as an anonymous block)
- I'll have to manage without creating a User-defined Type in DB schema, so I am using the in-built DBMS_SQL.VARCHAR2S.
- This is not the actual business procedure, but is close to this. (Can't post the original)
- Dynamic query has to be there since I am using it for building the actual query with session, current application schema name etc.
/*
CREATE TABLE some_other_table
(word_id NUMBER(10), word_code VARCHAR2(30), word VARCHAR2(255));
INSERT INTO some_other_table VALUES (1, 'A', 'AB');
INSERT INTO some_other_table VALUES (2, 'B', 'BC');
INSERT INTO some_other_table VALUES (3, 'C', 'CD');
INSERT INTO some_other_table VALUES (4, 'D', 'DE');
COMMIT;
*/
DECLARE
l_word_count NUMBER(10) := 0;
l_counter NUMBER(10) := 0;
l_long_string VARCHAR2(30000) := NULL;
l_dyn_query VARCHAR2(30000) := NULL;
l_string_coll DBMS_SQL.VARCHAR2S;
BEGIN
-- l_string_coll of type DBMS_SQL.VARCHAR2S comes as Input to the procedure
FOR i IN 1 .. 4100
LOOP
l_counter := l_counter + 1;
l_string_coll(l_counter) := 'AB';
END LOOP;
-- Above input collection is concatenated into CSV string
FOR i IN l_string_coll.FIRST .. l_string_coll.LAST
LOOP
l_long_string := l_long_string || l_string_coll(i) || ', ';
END LOOP;
l_long_string := TRIM(',' FROM TRIM(l_long_string));
dbms_output.put_line('Length of l_long_string = ' || LENGTH(l_long_string));
/*
Some other tasks in PLSQL done successfully using the concatenated string l_long_string
*/
l_dyn_query := ' SELECT COUNT(*)
FROM some_other_table
WHERE word IN ( SELECT TRIM(REGEXP_SUBSTR(str, ''[^,]+'', 1, LEVEL)) word
FROM ( SELECT :string str FROM SYS.DUAL )
CONNECT BY TRIM(REGEXP_SUBSTR(str, ''[^,]+'', 1, LEVEL)) IS NOT NULL )';
--WHERE word IN ( SELECT column_value FROM TABLE(l_string_coll) )';
EXECUTE IMMEDIATE l_dyn_query INTO l_word_count USING l_long_string;
dbms_output.put_line('Word Count = ' || l_word_count);
EXCEPTION
WHEN OTHERS
THEN
dbms_output.put_line('SQLERRM = ' || SQLERRM);
dbms_output.put_line('FORMAT_ERROR_BAKCTRACE = ' || dbms_utility.format_error_backtrace);
END;
/
How can I modify the dynamic query?
First of all. Based on the code you've provided, there is absolutely no need to use dynamic, native or DBMS_SQL dynamic SQL at all.
Secondly, SQL cannot operate on "strings" that are greater than 4K bytes in length(Oracle versions prior to 12c), or 32K bytes(Oracle version 12cR1 and up, if MAX_STRING_SIZE initialization parameter is set to EXTENDED).
PL/SQL, on the other hand, allows you to work with varchar2() character strings that are greater than 4K bytes (up to 32Kb) in length. If you just need to count words in a comma separated sting, you can simply use regexp_count() regular expression function(Oracle 11gr1 and up) as follows:
set serveroutput on;
set feedback off;
clear screen;
declare
l_str varchar2(100) := 'aaa,bb,ccc,yyy';
l_numOfWords number;
begin
l_numOfWords := regexp_count(l_str, '[^,]+');
dbms_output.put('Number of words: ');
dbms_output.put_line(to_char(l_numOfWords));
end;
Result:
Number of words: 4
I would like to check if there's no data coming from SQL query then file should not be created.
Here is my code:
CREATE OR REPLACE PROCEDURE VR_AD_INTEGRATION_EXPORT AS
l_v_file UTL_FILE.file_type
BEGIN
l_v_file := UTL_FILE.fopen('integration', 'HRMtoAD1_'||to_char(sysdate,'YYYYMMDD')||'_'||to_char(sysdate,'HH24MISS'), 'w', 32767);
FOR x IN (SELECT * FROM (SELECT
decode(pid, NULL, RPAD(' ',7,' '), RPAD(user_id, 7, ' '))|| '' ||
decode(name_last, NULL, RPAD(' ',50,' '), RPAD(name_last, 50, ' '))
str FROM vr_ad_integration WHERE integrated = 'N') str WHERE rownum <= 1000 ORDER BY rownum)
LOOP
BEGIN
UTL_FILE.put_line(l_v_file, x.str);
END;
END LOOP;
UTL_FILE.fflush(l_v_file);
UTL_FILE.fclose(l_v_file);
END VR_AD_INTEGRATION_EXPORT;
Now I can create file successfully in a remote location. However, what if there's no data in select query? no file should be created. If I am correct, I need to include exception code but I have no idea how to do it in this case. Any suggestion?
Cheers! :-)
There are several ways to achieve this. One is to adopt a more procedural approach with a explicit cursor and only open the file once a record is fetched. A second is to modify your code to include a count inside the loop and delete the file if the count is zero.
Here is a third choice, which is a variant on the previous option. It tests the size of the file and if it is zero deleted it using the UTL_FILE.FREMOVE() command. Note the need to store the generated file name in a variable for reference later.
CREATE OR REPLACE PROCEDURE VR_AD_INTEGRATION_EXPORT AS
l_v_file UTL_FILE.file_type;
l_filename varchar2(128);
f_exists boolean;
f_size pls_integer;
f_blk_size pls_integer;
BEGIN
l_filename := 'HRMtoAD1_'||to_char(sysdate,'YYYYMMDD')||'_'||to_char(sysdate,'HH24MISS');
l_v_file := UTL_FILE.fopen('integration', l_filename , 'w', 32767);
FOR x IN (SELECT * FROM (SELECT
decode(pid, NULL, RPAD(' ',7,' '), RPAD(user_id, 7, ' '))|| '' || decode(name_last, NULL, RPAD(' ',50,' '), RPAD(name_last, 50, ' '))
str
FROM vr_ad_integration
WHERE integrated = 'N') str
WHERE rownum <= 1000 ORDER BY rownum) LOOP
BEGIN
UTL_FILE.put_line(l_v_file, x.str);
END;
END LOOP;
utl_file.fgetattr('integration', l_filename , f_exists, f_size, f_blk_size);
if f_size > 0 then
UTL_FILE.fflush(l_v_file);
UTL_FILE.fclose(l_v_file);
else
UTL_FILE.fclose(l_v_file);
utl_file.fremove('integration', l_filename);
end if;
END VR_AD_INTEGRATION_EXPORT;
There is some useful functionality in the UTL_FILE package beyond reading and writing lines. I suggest you read the documentation to find out more.
Use a flag to check if the file is created, and only create it once on the first run through your loop. Pseudocode:
bool fileCreatedFlag = false;
for x in (SELECT ...):
if(!fileCreatedFlag):
l_v_file = fopen(...);
fileCreatedFlag = true;
put_line(...);
if(fileCreatedFlag):
fflush;
fclose;
I am using the REPLACE function in oracle to replace values in my string like;
SELECT REPLACE('THE NEW VALUE IS #VAL1#','#VAL1#','55') from dual
So this is OK to replace one value, but what about 20+, should I use 20+ REPLACE function or is there a more practical solution.
All ideas are welcome.
Even if this thread is old is the first on Google, so I'll post an Oracle equivalent to the function implemented here, using regular expressions.
Is fairly faster than nested replace(), and much cleaner.
To replace strings 'a','b','c' with 'd' in a string column from a given table
select regexp_replace(string_col,'a|b|c','d') from given_table
It is nothing else than a regular expression for several static patterns with 'or' operator.
Beware of regexp special characters!
The accepted answer to how to replace multiple strings together in Oracle suggests using nested REPLACE statements, and I don't think there is a better way.
If you are going to make heavy use of this, you could consider writing your own function:
CREATE TYPE t_text IS TABLE OF VARCHAR2(256);
CREATE FUNCTION multiple_replace(
in_text IN VARCHAR2, in_old IN t_text, in_new IN t_text
)
RETURN VARCHAR2
AS
v_result VARCHAR2(32767);
BEGIN
IF( in_old.COUNT <> in_new.COUNT ) THEN
RETURN in_text;
END IF;
v_result := in_text;
FOR i IN 1 .. in_old.COUNT LOOP
v_result := REPLACE( v_result, in_old(i), in_new(i) );
END LOOP;
RETURN v_result;
END;
and then use it like this:
SELECT multiple_replace( 'This is #VAL1# with some #VAL2# to #VAL3#',
NEW t_text( '#VAL1#', '#VAL2#', '#VAL3#' ),
NEW t_text( 'text', 'tokens', 'replace' )
)
FROM dual
This is text with some tokens to replace
If all of your tokens have the same format ('#VAL' || i || '#'), you could omit parameter in_old and use your loop-counter instead.
Bear in mind the consequences
SELECT REPLACE(REPLACE('TEST123','123','456'),'45','89') FROM DUAL;
will replace the 123 with 456, then find that it can replace the 45 with 89.
For a function that had an equivalent result, it would have to duplicate the precedence (ie replacing the strings in the same order).
Similarly, taking a string 'ABCDEF', and instructing it to replace 'ABC' with '123' and 'CDE' with 'xyz' would still have to account for a precedence to determine whether it went to '123EF' or ABxyzF'.
In short, it would be difficult to come up with anything generic that would be simpler than a nested REPLACE (though something that was more of a sprintf style function would be a useful addition).
In case all your source and replacement strings are just one character long, you can simply use the TRANSLATE function:
SELECT translate('THIS IS UPPERCASE', 'THISUP', 'thisup')
FROM DUAL
See the Oracle documentation for details.
This is an old post, but I ended up using Peter Lang's thoughts, and did a similar, but yet different approach. Here is what I did:
CREATE OR REPLACE FUNCTION multi_replace(
pString IN VARCHAR2
,pReplacePattern IN VARCHAR2
) RETURN VARCHAR2 IS
iCount INTEGER;
vResult VARCHAR2(1000);
vRule VARCHAR2(100);
vOldStr VARCHAR2(50);
vNewStr VARCHAR2(50);
BEGIN
iCount := 0;
vResult := pString;
LOOP
iCount := iCount + 1;
-- Step # 1: Pick out the replacement rules
vRule := REGEXP_SUBSTR(pReplacePattern, '[^/]+', 1, iCount);
-- Step # 2: Pick out the old and new string from the rule
vOldStr := REGEXP_SUBSTR(vRule, '[^=]+', 1, 1);
vNewStr := REGEXP_SUBSTR(vRule, '[^=]+', 1, 2);
-- Step # 3: Do the replacement
vResult := REPLACE(vResult, vOldStr, vNewStr);
EXIT WHEN vRule IS NULL;
END LOOP;
RETURN vResult;
END multi_replace;
Then I can use it like this:
SELECT multi_replace(
'This is a test string with a #, a $ character, and finally a & character'
,'#=%23/$=%24/&=%25'
)
FROM dual
This makes it so that I can can any character/string with any character/string.
I wrote a post about this on my blog.
I have created a general multi replace string Oracle function by a table of varchar2 as parameter.
The varchar will be replaced for the position rownum value of table.
For example:
Text: Hello {0}, this is a {2} for {1}
Parameters: TABLE('world','all','message')
Returns:
Hello world, this is a message for all.
You must create a type:
CREATE OR REPLACE TYPE "TBL_VARCHAR2" IS TABLE OF VARCHAR2(250);
The funcion is:
CREATE OR REPLACE FUNCTION FN_REPLACETEXT(
pText IN VARCHAR2,
pPar IN TBL_VARCHAR2
) RETURN VARCHAR2
IS
vText VARCHAR2(32767);
vPos INT;
vValue VARCHAR2(250);
CURSOR cuParameter(POS INT) IS
SELECT VAL
FROM
(
SELECT VAL, ROWNUM AS RN
FROM (
SELECT COLUMN_VALUE VAL
FROM TABLE(pPar)
)
)
WHERE RN=POS+1;
BEGIN
vText := pText;
FOR i IN 1..REGEXP_COUNT(pText, '[{][0-9]+[}]') LOOP
vPos := TO_NUMBER(SUBSTR(REGEXP_SUBSTR(pText, '[{][0-9]+[}]',1,i),2, LENGTH(REGEXP_SUBSTR(pText, '[{][0-9]+[}]',1,i)) - 2));
OPEN cuParameter(vPos);
FETCH cuParameter INTO vValue;
IF cuParameter%FOUND THEN
vText := REPLACE(vText, REGEXP_SUBSTR(pText, '[{][0-9]+[}]',1,i), vValue);
END IF;
CLOSE cuParameter;
END LOOP;
RETURN vText;
EXCEPTION
WHEN OTHERS
THEN
RETURN pText;
END FN_REPLACETEXT;
/
Usage:
TEXT_RETURNED := FN_REPLACETEXT('Hello {0}, this is a {2} for {1}', TBL_VARCHAR2('world','all','message'));
Thanks for the answer. Rather than specifying the translation in the call, you can also do this using a cursor as shown below.
create or replace function character_substitutions (input_str varchar2)
return varchar2
as
v_result VARCHAR2(4000);
cursor c_translate_table is
select '&' as symbol_to_replace, 'amp' as symbol_in_return_string from dual
union all
select '/' as symbol_to_replace, '_' as symbol_in_return_string from dual
union all
select '"' as symbol_to_replace, 'in' as symbol_in_return_string from dual
union all
select '%' as symbol_to_replace, 'per' as symbol_in_return_string from dual
union all
select '.' as symbol_to_replace, '_' as symbol_in_return_string from dual;
begin
v_result := input_str;
for r_translate in c_translate_table loop
v_result := REPLACE( v_result, r_translate.symbol_to_replace, r_translate.symbol_in_return_string);
end loop;
return v_result;
end;
/
Another example of how to apply an ordered list of character replacements. In this case:
convert a backslash to a slash
convert a double slash occurrence to a single slash
convert a single quote to an escaped single quote
For example, convert from:
\\server\Folder\611891\Joe's Doc.pdf
to
/server/Folder/611891/Joe\'s Doc.pdf
Can use a recursive CTE to iterate through the ordered list in sequence. First part demonstrates how the string evolves:
WITH subs AS (
SELECT '\' convert_from, '/' convert_to, 1 seq FROM DUAL
UNION SELECT '//', '/', 2 FROM DUAL
UNION SELECT '''', '\''', 3 FROM DUAL
),
rcte(convert_from, convert_to, seq, src) AS (
SELECT '', '', 0, '\\server\Folder\611891\Joe''s Doc.pdf' src FROM DUAL
UNION ALL
SELECT s.convert_from, s.convert_to, s.seq, REPLACE(r.src, s.convert_from, s.convert_to)
FROM rcte r
JOIN subs s ON (s.seq = r.seq + 1)
)
SELECT *
FROM rcte
ORDER BY seq
;
| CONVERT_FROM | CONVERT_TO | SEQ | SRC |
|--------------|------------|-----|--------------------------------------|
| (null) | (null) | 0 | \\server\Folder\611891\Joe's Doc.pdf |
| \ | / | 1 | //server/Folder/611891/Joe's Doc.pdf |
| // | / | 2 | /server/Folder/611891/Joe's Doc.pdf |
| ' | \' | 3 | /server/Folder/611891/Joe\'s Doc.pdf |
But actually only interested in the final result:
SELECT src
FROM rcte
WHERE seq = (SELECT MAX(seq) FROM subs)
;
| SRC |
|--------------------------------------|
| /server/Folder/611891/Joe\'s Doc.pdf |
I am currently developing a function that is meant to execute dynamically created SQL statements. This is done by concatenating the columns and fetching them via cursors. The problem is that when there is a function with a comma between its arguments, the concat concatenates the contents of the functions inclusive.
Is it possible to skip contents of every bracket found in a string using REGEXP_SUBTR or REGEXP_REPLACE?
Many thanks in advance for your prompt and kind suggestions.
-- strips out the select list
src_str := REGEXP_SUBSTR(v_sql, 'SELECT ([[:graph:]]+\ ?){1,1000000}/?');
-- Replace the commas in the select list with the concat symbol for concatenation
rep_str := REGEXP_REPLACE(src_str, ', ', p_dot);
-- Replace the select list with the replace string
v_query := REPLACE(v_sql, src_str, rep_str);
v_sql := select a, b, to_char(sysdate, 'dd/mm/yyyy') from demo;
p_dot := '||'',''||';
currently, it returns:
select a || ',' || b || ',' || to_char(sysdate || ',' || 'dd/mm/yyyy') from demo
but should return something like:
select a || ',' || b || ',' || to_char(sysdate, 'dd/mm/yyyy') from demo
Many thanks Rene, your query worked but I have one more question and here it is
for i in 1 .. p_arglist.count
loop
-- Search for : in the query
src_sym := REGEXP_SUBSTR(v_query, ':[[:graph:]]+\ ?', i,i);
-- Replace the : with each value of p_arglist passed
v_querymult := REGEXP_REPLACE(v_query, src_sym , p_arglist(i),i,i);
end loop;
return v_query;
where p_arglist is a varchar2 varray
p_arglist := ('demo#demo.com','2001')
v_query := 'SELECT A, B, C FROM DEMO WHERE USERID = :USERID AND YEAR = :YEAR';
Currently, it returns
v_query := SELECT A, B, C FROM DEMO WHERE USERID = :USERID AND YEAR = 2001
and skips the first in the list which is the userid.
many thanks for your anticipated help
Have you thought about using DBMS_SQL, that should parse the SQL and allow you to bind variables.
See these links for further reading
Oracle Docs
Ask Tom Example
something like this should do if I understood your requirement correctly:
-- multiple replacements to accomodate for functions with more
-- than two parameters (and accordingly more than one comma)
src_str := regexp_replace(src_str, '(\([^)]+),', '\1##comma-in-function##');
src_str := regexp_replace(src_str, '(\([^)]+),', '\1##comma-in-function##');
src_str := regexp_replace(src_str, '(\([^)]+),', '\1##comma-in-function##');
-- replace the left-over commas
src_str := replace(src_str, ', ', p_dot);
-- turn commas within function call back to commas:
src_str := replace(src_str, '##comma-in-function##', ',');