If no data then don't create empty file - oracle

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;

Related

how to sort by different attribute each time we click a button pl/sql?

When I click the button for the first time, my selected column is sorted. When I click for the second time, it will sort in reverse.How can I do this?
Note: My program consists of dummy block. No data blocks
declare
cursor c is
select *
from muhasebe.doviz_takip
where UPPER(fatura_no) LIKE
NVL('%' || UPPER(:giris.sorgulama) || '%', UPPER(fatura_no))
order by fatura_no asc;
begin
go_block('XDOVIZ_TAKIP');
clear_block;
first_record;
for r in c loop
:FATURA_NO := r.fatura_no;
:ACIKLAMA := r.aciklama;
:YUKLEME_TAR := r.yukleme_tar;
:VARIS_TAR := r.varis_tar;
:TUTAR := r.tutar;
next_record;
end loop;
first_record;
end;
This is the code I can sort once
One option is to create a global variable (or a parameter), two cursors (one for each sorting) and IF-THEN-ELSE which decides which cursor to use, depending on global variable's value.
Something like this:
DECLARE
CURSOR c_asc IS
SELECT *
FROM muhasebe.doviz_takip
WHERE UPPER (fatura_no) LIKE
NVL ('%' || UPPER ( :giris.sorgulama) || '%',
UPPER (fatura_no))
ORDER BY fatura_no ASC;
CURSOR c_desc IS
SELECT *
FROM muhasebe.doviz_takip
WHERE UPPER (fatura_no) LIKE
NVL ('%' || UPPER ( :giris.sorgulama) || '%',
UPPER (fatura_no))
ORDER BY fatura_no DESC;
BEGIN
GO_BLOCK ('XDOVIZ_TAKIP');
CLEAR_BLOCK;
FIRST_RECORD;
:global.sort := NVL ( :global.sort, 'ASC');
IF :global.sort = 'DESC'
THEN
FOR r IN c_asc
LOOP
:FATURA_NO := r.fatura_no;
:ACIKLAMA := r.aciklama;
:YUKLEME_TAR := r.yukleme_tar;
:VARIS_TAR := r.varis_tar;
:TUTAR := r.tutar;
NEXT_RECORD;
END LOOP;
:global.sort := 'ASC';
ELSIF :global.sort = 'ASC'
THEN
FOR r IN c_desc
LOOP
:FATURA_NO := r.fatura_no;
:ACIKLAMA := r.aciklama;
:YUKLEME_TAR := r.yukleme_tar;
:VARIS_TAR := r.varis_tar;
:TUTAR := r.tutar;
NEXT_RECORD;
END LOOP;
:global.sort := 'DESC';
END IF;
FIRST_RECORD;
END;
Perhaps you could try to make it "smarter" (as this is pretty much dummy - repeating more or less the same code twice), but - this is simple and easy to maintain.
One another approach would be converting it to a database block by creating a view named v_doviz_takip such as
CREATE OR REPLACE VIEW v_doviz_takip AS
SELECT *
FROM muhasebe.doviz_takip
and setting the Query Data Source Name of the block xdoviz_takip to this
and adding a WHERE clause
UPPER(fatura_no) LIKE NVL('%' || UPPER(:giris.sorgulama) || '%', UPPER(fatura_no))`
to the block. Then add a line
SET_BLOCK_PROPERTY('xdoviz_takip',order_by, 'fatura_no');
to the WHEN-NEW-FORM-INSTANCE trigger.
And convert your current code block to this one :
DECLARE
v_ord VARCHAR2(25) := GET_BLOCK_PROPERTY('XDOVIZ_TAKIP',order_by);
BEGIN
GO_BLOCK ('XDOVIZ_TAKIP');
CLEAR_BLOCK;
IF v_ord = 'fatura_no' THEN v_ord := v_ord||' DESC'; ELSE v_ord := 'fatura_no' END IF;
SET_BLOCK_PROPERTY('XDOVIZ_TAKIP',order_by, v_ord);
EXECUTE_QUERY;
FIRST_RECORD;
END;
this way, it will sort descendingly by fatura_no for the first, and ascendingly for the second attempts
, or just converting the current IF clause to IF v_ord != 'fatura_no' ... would reversely change the behaviour.

ORA-01007 "variable not in select list" from dbms_sql.column_value call

I am trying to use dynamic SQL to sample all the data in a schema with a pattern:
DECLARE
xsql varchar2(5000);
c NUMBER;
d NUMBER;
col_cnt INTEGER;
f BOOLEAN;
rec_tab DBMS_SQL.DESC_TAB;
col_num NUMBER;
varvar varchar2(500);
PROCEDURE print_rec(rec in DBMS_SQL.DESC_REC) IS
BEGIN
DBMS_OUTPUT.ENABLE(1000000);
DBMS_OUTPUT.NEW_LINE;
DBMS_OUTPUT.PUT_LINE('col_type = '
|| rec.col_type);
DBMS_OUTPUT.PUT_LINE('col_maxlen = '
|| rec.col_max_len);
DBMS_OUTPUT.PUT_LINE('col_name = '
|| rec.col_name);
DBMS_OUTPUT.PUT_LINE('col_name_len = '
|| rec.col_name_len);
DBMS_OUTPUT.PUT_LINE('col_schema_name = '
|| rec.col_schema_name);
DBMS_OUTPUT.PUT_LINE('col_schema_name_len = '
|| rec.col_schema_name_len);
DBMS_OUTPUT.PUT_LINE('col_precision = '
|| rec.col_precision);
DBMS_OUTPUT.PUT_LINE('col_scale = '
|| rec.col_scale);
DBMS_OUTPUT.PUT('col_null_ok = ');
IF (rec.col_null_ok) THEN
DBMS_OUTPUT.PUT_LINE('true');
ELSE
DBMS_OUTPUT.PUT_LINE('false');
END IF;
END;
BEGIN
c := DBMS_SQL.OPEN_CURSOR;
xsql:='
WITH got_r_num AS
(
SELECT e.* -- or whatever columns you want
, ROW_NUMBER () OVER (ORDER BY dbms_random.value) AS r_num
FROM dba_tab_columns e
)
SELECT * -- or list all columns except r_num
FROM got_r_num
WHERE r_num <= 10';
DBMS_SQL.PARSE(c, xsql, DBMS_SQL.NATIVE);
d := DBMS_SQL.EXECUTE(c);
DBMS_SQL.DESCRIBE_COLUMNS(c, col_cnt, rec_tab);
LOOP
IF DBMS_SQL.FETCH_ROWS(c)>0 THEN
NULL;
-- get column values of the row
DBMS_SQL.COLUMN_VALUE(c, 2, varvar);
--dbms_output.put_line('varvar=');
--DBMS_SQL.COLUMN_VALUE(source_cursor, 2, name_var);
--DBMS_SQL.COLUMN_VALUE(source_cursor, 3, birthdate_var);
-- Bind the row into the cursor that inserts into the destination table. You
-- could alter this example to require the use of dynamic SQL by inserting an
-- if condition before the bind.
--DBMS_SQL.BIND_VARIABLE(destination_cursor, ':id_bind', id_var);
--DBMS_SQL.BIND_VARIABLE(destination_cursor, ':name_bind', name_var);
--DBMS_SQL.BIND_VARIABLE(destination_cursor, ':birthdate_bind',
--birthdate_var);
--ignore := DBMS_SQL.EXECUTE(destination_cursor);
--ELSE
-- No more rows to copy:
--EXIT;
END IF;
END LOOP;
--EXIT WHEN d != 10;
--END LOOP;
col_num := rec_tab.first;
IF (col_num IS NOT NULL) THEN
LOOP
print_rec(rec_tab(col_num));
col_num := rec_tab.next(col_num);
EXIT WHEN (col_num IS NULL);
END LOOP;
END IF;
DBMS_SQL.CLOSE_CURSOR(c);
END;
/
When I run that it gives me this error from the line with the dbms_sql.column_value call:
ORA-01007: variable not in select list
If I comment out that dbms_sql.column_value call it still errors but now with:
ORA-01002: fetch out of sequence
What am I doing wrong?
You have two problems in the code you posted. Firstly you have skipped part of the execution flow because you haven't called the DEFINE_COLUMN procedure. That is what is causing the ORA-01007 error, as the dynamic SQL processing hasn't been told about the select list columns via that call. For your current code you only need to define column 2, but assuming you will actually want to refer to the others you can define them in a loop. To treat them all as string for display you could do:
...
DBMS_SQL.PARSE(c, xsql, DBMS_SQL.NATIVE);
d := DBMS_SQL.EXECUTE(c);
DBMS_SQL.DESCRIBE_COLUMNS(c, col_cnt, rec_tab);
FOR i IN 1..col_cnt
LOOP
-- dbms_output.put_line('col_name is ' || rec_tab(i).col_name);
DBMS_SQL.DEFINE_COLUMN(c, i, varvar, 500);
END LOOP;
LOOP
IF DBMS_SQL.FETCH_ROWS(c)>0 THEN
...
If you want to do anything that needs to treat the variables as the right types you could have a local variable of each type and use the data type from the rec_tab information you already have from describe_columns to use the appropriately typed variable for each column.
The second problem, which you were hitting when you commented the column_value call, is still there once that definbe issue has been fixed. Your loop doesn't ever exit, so after you fetch the last row from the cursor you do a further invalid fetch, which throws ORA-01002. You have the code to avoid that already but it's commented out:
...
LOOP
IF DBMS_SQL.FETCH_ROWS(c)>0 THEN
-- get column values of the row
DBMS_SQL.COLUMN_VALUE(c, 2, varvar);
...
ELSE
-- No more rows to copy:
EXIT;
END IF;
END LOOP;
...
With those two changes your code runs, and dumps the view structure:
PL/SQL procedure successfully completed.
col_type = 1
col_maxlen = 30
col_name = OWNER
col_name_len = 5
col_schema_name =
col_schema_name_len = 0
col_precision = 0
col_scale = 0
col_null_ok = false
col_type = 1
col_maxlen = 30
col_name = TABLE_NAME
...
To those who find this question when accessing Oracle through ODP.NET, as I did:
We started getting this error whenever we would add column to an existing table in our application. I'm not sure what all the conditions were to make it fail, but ours were:
Run a SELECT * FROM "table".
Include a ROWNUM restriction in the WHERE clause (WHERE ROWNUM < 10).
Run that through the ODP.NET dataReader.GetSchemaTable() call.
Running unrestricted queries or running queries directly on Oracle SQL Developer did not seem to cause the error.
I've hit some pretty weird stuff in the past with Oracle connection pooling, so I eventually thought that could be the problem. The solution was to restart the web service to force all the connections to be fully dropped and recreated.
The theory is that the ODP.NET connection from the connection pool still had no idea the column existed on the table, but the column was returned by the database.

Calling an internal function inside a procedure PL/SQL

A bit green with PL/SQL functions but trying to add a function where if the current person does not have an alt_id to get the relative alt_id via the application. Many thanks in advance.
CREATE OR REPLACE PROCEDURE CLIENT.p_Verification(pin_Member_ID IN dbo.member.member_id%Type,
pin_Person_ID IN dbo.person.person_id%type,
pin_user_id IN NUMBER default null,
pioc_ref_cursor IN OUT dbo.pkg_benefit_q.ref_cursor) IS
lv_member_id dbo.person.alt_identifier%Type;
-- other variables
...
Procedure p_get_other_deductions(...
Begin
...
end;
new function here
FUNCTION f_get_alt_id
RETURN Varchar2
IS-- dbo.person.alt_identifier%Type IS
alt_id Varchar2(10); --dbo.person.alt_identifier%Type;
BEGIN
SELECT p.ALT_IDENTIFIER
into alt_id
FROM DBO.PERSON p , DBO.MEMBER m, DBO.APPLICATION a
where p.PERSON_ID = m.PERSON_ID
AND m.MEMBER_ID = a.MEMBER_ID
AND a.APPLICATION_ID= cn_EntAppId
;
RETURN alt_id;
END;
calling it here...
Select dbo.pkg_benefit_s.f_get_value_description('PREFIX',
p.prefix,
'Y',
'N') || case
when p.prefix is not null then
' '
else
Null
end || nvl2(p.first_name, initcap(p.first_name) || ' ', '') ||
nvl2(p.middle_name, initcap(p.middle_name) || ' ', '') ||
nvl2(p.last_name, initcap(p.last_name), '') || ' ' ||
dbo.pkg_benefit_s.f_get_value_description('SUFFIX',
p.suffix,
'Y',
'N'),
nvl2(p.alt_identifier,
f_get_alt_id, -- errors here
''),
nvl2(p.first_name, initcap(p.first_name), ''),
nvl2(p.last_name, initcap(p.last_name), '')
into lv_member_name,
lv_member_id,
lv_member_first_name,
lv_member_last_name
From dbo.person p
where p.person_id = ln_person_id;
... more code
END p_Verification;
/
It would be better practice in a number of ways to create a package that has your procedure and your function listed in the header and the body.
CREATE OR REPLACE PACKAGE CLIENT IS
PROCEDURE p_Verification(pin_Member_ID IN dbo.member.member_id%Type,
pin_Person_ID IN dbo.person.person_id%type,
pin_user_id IN NUMBER default null,
pioc_ref_cursor IN OUT dbo.pkg_benefit_q.ref_cursor);
Procedure p_get_other_deductions(...
FUNCTION f_get_alt_id(alt_id_in IN NUMBER:= NULL) RETURN Varchar2;
END CLIENT;
CREATE OR REPLACE PACKAGE BODY CLIENT IS
END CLIENT;
Notice how I have added a null parameter so if you need to redesign the function you can do so without invalidating any dependent objects.
I added the
alt_id varchar(10) in the declaration section
restored the select query above to simply return
dbo.pkg_benefit_s.f_get_value_description('SUFFIX',
p.suffix,
'Y',
'N'),
p.alt_identifier,
nvl2(p.first_name, initcap(p.first_name), ''),
nvl2(p.last_name, initcap(p.last_name), '')
into lv_member_name,
lv_member_id,
lv_member_first_name,
lv_member_last_name
... everything else is the same...until
I added another section to set the lv_member_id
BEGIN
Select p.alt_identifier
into alt_id
from dbo.person p
where P.PERSON_ID = pin_Person_id;
if alt_id is not null
then
lv_member_id := alt_id;
else
lv_member_id := f_get_alt_id;
end if;
END;
... rest of procedure and end; -- it now compiles

If condition in PL/SQL script with cursor and loop

I would like to ask for some help or advice in this particular case.
I have table called "Teams". The table contains 3 columns - Issue, Responsible_team and More_Info (all varchar2).
I have a PL/SQL script with cursor and loop for selecting as many teams as issue description you type (some random word you think it might help you find the responsible team). This part works great for me.
But I do not know how to compile the IF condition in there. If no team is found according to typed word description, I would like to get some basic output dbms_output.put_line('Responsible team is not found').
There are 2 ways how I wrote the script. Classic loop and while loop.
I would be happy for any advice.
1.script
set verify off
DECLARE
v_issue teams.issue%type; --variable for issue column from teams table
v_respteam teams.responsible_team%type; --variable for responsible_team column from teams table
v_info teams.more_info%type; --variable for more_info column from teams table
--cursor declaration
CURSOR c_respteam
RETURN teams%ROWTYPE
IS
SELECT issue, responsible_team, more_info
FROM teams
WHERE lower(issue) like '%&Describe_Issue%';
BEGIN
OPEN c_respteam;
LOOP
FETCH c_respteam into v_issue, v_respteam, v_info;
EXIT when c_respteam%NOTFOUND;
dbms_output.put_line('Responsible team is '|| v_respteam || ' --> ' || v_info);
END LOOP;
CLOSE c_respteam;
end;
/
2.script
-- cursor with while loop
set verify off
DECLARE
v_issue teams.issue%type; --variable for issue column from teams table
v_respteam teams.responsible_team%type; --variable for responsible_team column from teams table
v_info teams.more_info%type; --variable for more_info column from teams table
CURSOR c_respteam
RETURN teams%ROWTYPE IS
SELECT issue, responsible_team, more_info
FROM teams
WHERE lower(issue) like '%&Describe_Issue%';
BEGIN
OPEN c_respteam;
FETCH c_respteam INTO v_issue, v_respteam, v_info;
WHILE c_respteam%FOUND
LOOP
dbms_output.put_line('Responsible team is '|| v_respteam || ' --> ' || v_info);
FETCH c_respteam INTO v_issue, v_respteam, v_info;
END LOOP;
CLOSE c_respteam;
END;
/
You could rewrite to:
declare
l_found boolean :=false;
cursor c_respteam is
select issue
,responsible_team
,more_info
from teams
where lower(issue) like '%&Describe_Issue%';
begin
for r in c_respteam
loop
l_found := true;
dbms_output.put_line('Responsible team is ' || r.responsible_team || ' --> ' || r.more_info);
end loop;
if not l_found
then
dbms_output.put_line('No records found');
end if;
end;
/
You need to have a counter variable [ETA: ooh, I like Rene's boolean variable idea instead; either way, you need an extra variable!] to work out if any rows were returned or not. I'm not sure why you're using an explicit cursor fetch, rather than using the cursor-for-loop? Cursor-for-loops are not only easier to write, read and maintain, but Oracle have put some behind-the-scenes optimisation in, to aid performance.
Of course, depending on what you're actually doing with the data returned by your cursor (dbms_output.put_line being something that you should never have in your production code), it's debatable that you would need to loop through a cursor at all.
Anyway, with that said, here's an example demonstrating how I would handle your requirement to check for no rows returned by the cursor:
declare
cursor cur (p_val varchar2)
is
select dummy
from dual
where dummy like '%'||p_val||'%';
v_counter integer := 0;
begin
for rec in cur('Y')
loop
dbms_output.put_line('value of dummy = '||rec.dummy);
v_counter := v_counter + 1;
end loop;
if v_counter = 0 then
dbms_output.put_line('no rows returned');
end if;
end;
/
no rows returned
declare
cursor cur (p_val varchar2)
is
select dummy
from dual
where dummy like '%'||p_val||'%';
v_counter integer := 0;
begin
for rec in cur('X')
loop
dbms_output.put_line('value of dummy = '||rec.dummy);
v_counter := v_counter + 1;
end loop;
if v_counter = 0 then
dbms_output.put_line('no rows returned');
end if;
end;
/
value of dummy = X
To expand on what I said in my comment below, it sounds like you just need a single sql statement, rather than using PL/SQL and relying on dbms_output.
Eg., say you have the following statement:
select lvl
from (select 'X'||level lvl from dual connect by level <= 10)
where lvl like '%&val%';
with &val blank, you get:
LVL
-----------------------------------------
X1
X2
X3
X4
X5
X6
X7
X8
X9
X10
With &val = 2 you get:
LVL
-----------------------------------------
X2
With &val = 100 you get:
no rows selected.

Oracle PLSQL: Help Required: Parsing String Collection or Concatenated String

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

Resources