How to iterate over positional script parameters in sqlplus? - oracle

In an Oracle sqlplus script, I can use single script parameters as
set serveroutput on
define arg = '&1'
begin
dbms_output.putline('&arg.');
end;
/
And in a Unix shell, I can iterate over given script arguments and pass that to an sqlplus script with
for x in "$#"; do
sqlplus user/passwd#sid #script.sql "$x"
done
Is there a similar way in Oracle sqlplus? Something like
for arg in &* loop
dbms_output.putline('&arg.');
end loop;
where arg would take values of &1, &2, &3, ...
This means, when I call
$ sqlplus user/passwd#sid #script.sql a b c
I would see as output
a
b
c

Let's see if this is what you are looking for.
We have an undetermined list of parameters
All of them are split by whitespace ( as it were a list on bash input parameters )
Do something with all of them, independently of the number of parameters
In order for sqlplus to deal with this, all of the parameters must be enclosed with double quotes, so that the code will interpret them as only one parameter.
Script
set serveroutput on size unlimited
declare
vstring varchar2(4000) := '&1';
vcounter pls_integer;
vrecord varchar2(20);
BEGIN
vcounter := regexp_count( vstring , ' ') + 1;
for var in 1..vcounter
loop
if var = 1
then
vrecord := regexp_substr( vstring, '[^ ]+', 1 , 1 );
dbms_output.put_line(vrecord);
elsif var > 1 and var <= vcounter
then
vrecord := regexp_substr( vstring, '[^ ]+', 1 , var );
dbms_output.put_line(vrecord);
end if;
end loop;
end;
/
I have this script stored as iteration.sql. So, let's run it with a set of parameters
SQL> #iteration.sql "2"
old 2: vstring varchar2(4000) := '&1';
new 2: vstring varchar2(4000) := '2';
2
PL/SQL procedure successfully completed.
SQL> #iteration.sql "2 m3 88 99 ajkd8 0 88 aa"
old 2: vstring varchar2(4000) := '&1';
new 2: vstring varchar2(4000) := '2 m3 88 99 ajkd8 0 88 aa';
2
m3
88
99
ajkd8
0
88
aa
PL/SQL procedure successfully completed.

Related

SELECT in an IN clause

I try to convert a comma separated string to number values. And then I try to chceck if a specified number occures there. I'm doing it inside a function. I'm getting error PLS-00103. The simplified snipped is here:
fieldType NUMBER :=1;
myString := '2,3,4,5';
IF fieldType NOT IN (SELECT TO_NUMBER(xt.column_value) FROM XMLTABLE(myString) xt) THEN
--do soemthing
-- Problem occures with the (
--
END IF;
You cannot use select statement in if condition
Link : https://stackoverflow.com/a/10203109/8843451
You need to assign the value to variable and use it
strop number :=0;
select count(*) into strop from (SELECT to_number(trim(COLUMN_VALUE)) as str FROM
xmltable(('"'|| REPLACE(myString, ',', '","')|| '"'))) where str in (fieldType);
if strop >0 then
dbms_output.put_line(fieldType || ' Is a Valid Number');
else
dbms_output.put_line(fieldType || ' Is a InValid Number');
end if;
There's a simpler option (SQL*Plus example; you'd use that 1 in line #2, or whatever you'd want to use):
SQL> DECLARE
2 fieldtype NUMBER := &par_fieldtype;
3 mystring VARCHAR2 (200) := '2,3,4,5';
4 BEGIN
5 IF ',' || mystring || ',' LIKE '%,' || fieldtype || ',%'
6 THEN
7 DBMS_OUTPUT.put_line ('Member');
8 ELSE
9 DBMS_OUTPUT.put_line ('Not a member');
10 END IF;
11 END;
12 /
Enter value for par_fieldtype: 1
Not a member
PL/SQL procedure successfully completed.
SQL> /
Enter value for par_fieldtype: 3
Member
PL/SQL procedure successfully completed.
SQL>

How can i display a string adding a hyphen each word on PL/SQL

How can I display a string, separating each letter by a dash with a for loop?
For example i want to display:
h-e-l-l-o-w-o-r-l-d
I tried with the substr function but I can't get it out
If it must be PL/SQL and FOR loop, then you could
SQL> set serveroutput on
SQL> declare
2 l_str varchar2(20) := 'helloworld';
3 retval varchar2(50);
4 begin
5 for i in 1 .. length(l_str) loop
6 retval := retval || substr(l_str, i, 1) ||'-';
7 end loop;
8 retval := rtrim(retval, '-');
9 dbms_output.put_line(retval);
10 end;
11 /
h-e-l-l-o-w-o-r-l-d
PL/SQL procedure successfully completed.
SQL>
Otherwise, consider e.g.
SQL> select rtrim(regexp_replace('helloworld', '(.)', '\1-'), '-') result from dual;
RESULT
-------------------
h-e-l-l-o-w-o-r-l-d
SQL>
or
SQL> select listagg(substr('helloworld', level, 1), '-') within group (order by level) result
2 from dual
3 connect by level <= length('helloworld');
RESULT
--------------------------------------------------------------------------------
h-e-l-l-o-w-o-r-l-d
SQL>
You can just output each successive character and, after the first, output a hyphen between them:
DECLARE
string VARCHAR2(20) := 'helloworld';
BEGIN
DBMS_OUTPUT.PUT(SUBSTR(string, 1, 1));
FOR i IN 2 .. LENGTH(string)
LOOP
DBMS_OUTPUT.PUT('-');
DBMS_OUTPUT.PUT(SUBSTR(string, i, 1));
END LOOP;
DBMS_OUTPUT.NEW_LINE();
END;
/
Which outputs:
h-e-l-l-o-w-o-r-l-d
If you want to do it without loops then you can use:
DECLARE
string VARCHAR2(20) := 'helloworld';
BEGIN
DBMS_OUTPUT.PUT_LINE( SUBSTR(REGEXP_REPLACE(string, '(.)', '-\1'), 2) );
END;
/
You should not use LTRIM (or RTRIM) to remove the hyphens as, if the input string has leading (or trailing) hyphens then these would be removed from the output and that would be erroneous.
For example:
DECLARE
string VARCHAR2(20) := '--helloworld--';
BEGIN
DBMS_OUTPUT.PUT_LINE('Correct:');
DBMS_OUTPUT.PUT_LINE(SUBSTR(REGEXP_REPLACE(string, '(.)', '-\1'), 2));
DBMS_OUTPUT.PUT_LINE('Incorrect:');
DBMS_OUTPUT.PUT_LINE(LTRIM(REGEXP_REPLACE(string, '(.)', '-\1'), '-'));
END;
/
Outputs:
Correct:
----h-e-l-l-o-w-o-r-l-d----
Incorrect:
h-e-l-l-o-w-o-r-l-d----
db<>fiddle here

While loops with Strings not Integers

I'm having trouble finding any information on oracle plSQL while loops with strings online. All seem to be integer. When doing my research i feel i understand the integer aspect of while loops in plSQL but no sites that i have visited touched on or had examples of While Loops using strings.
For example: I can use a For loop to print individual letters from the word 'text' but what would stop me from using a While loop to get the same output?
DECLARE
c1 Number:= 1;
c2 Varchar2(4);
BEGIN
FOR c1 in 1..4
LOOP
SELECT substr('text' , c1 , 1 ) into c2 from dual;
dbms_output.put_line(c2);
END LOOP;
END;
If someone could explain how one would print a individual character or even the whole string with a while loop; or possibly point me in the right direction in terms of where to research example while loops with strings online.
Thank you.
You can write a WHILE loop entirely based on characters - no need for a counter of any kind. Something like this:
declare
txt varchar2(100);
begin
txt := 'my text';
while txt is not null loop
dbms_output.put_line(substr(txt, 1, 1)); -- or whatever else you need to do
txt := substr(txt, 2);
end loop;
end;
/
m
y
t
e
x
t
PL/SQL procedure successfully completed.
Yes, it can be written using a WHILE loop. The crucial thing is the length function and the counter. Also, you don't need a select query.
SET SERVEROUTPUT ON
DECLARE
c1 NUMBER := 1;
txt VARCHAR2(20) := 'text';
BEGIN
WHILE c1 <= length(txt) LOOP
dbms_output.put_line(substr(txt,c1,1));
c1 := c1 + 1; --increment the counter
END LOOP;
END;
/
Result
t
e
x
t
PL/SQL procedure successfully completed.
You can use SUBSTR in WHILE loop directly as follows:
SQL>
SQL> set serverout on
SQL> DECLARE
2 C1 NUMBER := 1;
3 C2 VARCHAR2(10) := 'TEXT';
4 BEGIN
5 WHILE SUBSTR(C2, C1, 1) IS NOT NULL LOOP
6 DBMS_OUTPUT.PUT_LINE(SUBSTR(C2, C1, 1));
7 C1 := C1 + 1;
8 END LOOP;
9 END;
10 /
T
E
X
T
PL/SQL procedure successfully completed.
SQL>
Cheers!!

Use Oracle PL/SQL For Loop to iterate through comma delimited string

I am writing a piece of code that would need to iterate on the content of a string, each values being separated with a ,.
e.g. I have my elements
v_list_pak_like varchar2(4000) := 'PEBO,PTGC,PTTL,PTOP,PTA';
How can I get it into an Array / Cursor to iterate on it in my loop?
for x in (elements)
loop
-- do my stuff
end loop;
I am looking for the very simple way, if possible avoiding to declare associative arrays.
Would it be possible to create a function that would return something usable as an input for a for loop (opposite to the while that could be used like in https://stackoverflow.com/a/19184203/6019417)?
Many thanks in advance.
You could do it easily in pure SQL. there are multiple ways of doing it, see Split comma delimited string into rows in Oracle
However, if you really want to do it in PL/SQL, then you could do it as:
SQL> set serveroutput on
SQL> DECLARE
2 str VARCHAR2(100) := 'PEBO,PTGC,PTTL,PTOP,PTA';
3 BEGIN
4 FOR i IN
5 (SELECT trim(regexp_substr(str, '[^,]+', 1, LEVEL)) l
6 FROM dual
7 CONNECT BY LEVEL <= regexp_count(str, ',')+1
8 )
9 LOOP
10 dbms_output.put_line(i.l);
11 END LOOP;
12 END;
13 /
PEBO
PTGC
PTTL
PTOP
PTA
PL/SQL procedure successfully completed.
SQL>
Thanks to Lalit great instructions, I am able to create a function that I can call from my for loop:
Create a type and function
CREATE OR REPLACE TYPE t_my_list AS TABLE OF VARCHAR2(100);
CREATE OR REPLACE
FUNCTION comma_to_table(p_list IN VARCHAR2)
RETURN t_my_list
AS
l_string VARCHAR2(32767) := p_list || ',';
l_comma_index PLS_INTEGER;
l_index PLS_INTEGER := 1;
l_tab t_my_list := t_my_list();
BEGIN
LOOP
l_comma_index := INSTR(l_string, ',', l_index);
EXIT
WHEN l_comma_index = 0;
l_tab.EXTEND;
l_tab(l_tab.COUNT) := TRIM(SUBSTR(l_string,l_index,l_comma_index - l_index));
l_index := l_comma_index + 1;
END LOOP;
RETURN l_tab;
END comma_to_table;
/
Then how to call it in my for loop:
declare
v_list_pak_like varchar2(4000) := 'PEBO,PTGC,PTTL,PTOP,PTA';
begin
FOR x IN (select * from (table(comma_to_table(v_list_pak_like)) ) )
loop
dbms_output.put_line(x.COLUMN_VALUE);
end loop;
end;
/
Notice the default name COLUMN_VALUE given by Oracle that is necessary for the use I want to make of the result.
Result as expected:
PEBO
PTGC
PTTL
PTOP
PTA
declare
type array_type is table of VARCHAR2(255) NOT NULL;
my_array array_type := array_type('aaa','bbb','ccc');
begin
for i in my_array.first..my_array.last loop
dbms_output.put_line( my_array(i) );
end loop;
end;
In the first line you define a table of any type you want.
then create variable of that type and give it values with a kind of constructor.
I initialized it in the declaration but it can be done also in the body of the Pl Sql.
Then just loop over your array from first index to the last.

Print Record fields in PL/SQL

How can I print all the fields of a record variable in PL/SQL.
The record variable has got lots of fields so is there a better way than printing each field?
Also tried dynamic sql but didn't help.
Building on Ollies use of dbms_output, but for to dynamically go through the cursor
set up for test
/*create table temp (aa varchar2(50) , bb number , cc date ) ;
insert into temp (aa,bb,cc)
select chr(level+100) , level, sysdate+level
from dual
connect by level < 15 ;
/
*/
Block to show the test (this assumes 11g)
set serveroutput on
declare
l_cur SYS_REFCURSOR ;
PROCEDURE CursorOutput(
p_refcursor IN OUT SYS_REFCURSOR
)
AS
l_desc DBMS_SQL.DESC_TAB ;
l_cols BINARY_INTEGER ;
l_cursor BINARY_INTEGER ;
v_varchar2 VARCHAR2( 4000 ) ;
v_number NUMBER ;
v_date DATE ;
l_data varchar2( 32767 ) ;
l_columnValue VARCHAR2( 32767 ) ;
l_processedRows Number := 0;
BEGIN
/* Convert refcursor "parameter" to DBMS_SQL cursor... */
l_cursor := DBMS_SQL.TO_CURSOR_NUMBER( p_refcursor );
/* Describe the cursor... */
DBMS_SQL.DESCRIBE_COLUMNS( l_cursor, l_cols, l_desc );
/* Define columns to be fetched. We're only using V2, NUM, DATE for example...
for a complete list of the col_types this link is accessible.
http://download.oracle.com/docs/cd/B10501_01/server.920/a96540/sql_elements2a.htm#45504
http://forums.oracle.com/forums/thread.jspa?threadID=912475
if not a usable type, will throw new exception
*/
FOR i IN 1 .. l_cols LOOP
IF l_desc(i).col_type = 2 THEN
DBMS_SQL.DEFINE_COLUMN(l_cursor, i, v_number);
ELSIF l_desc(i).col_type = 12 THEN
DBMS_SQL.DEFINE_COLUMN(l_cursor, i, v_date);
ELSif l_desc(i).col_type = 01 or l_desc(i).col_type = 96 then
DBMS_SQL.DEFINE_COLUMN(l_cursor, i, v_varchar2, 4000);
else
--raise an exception if the user's query contains a datatype not (yet) supported by this procedure
RAISE_APPLICATION_ERROR(-20000, 'Invalid Data Type for conversion to delimited file. {' || l_desc(i).col_name || '}');
END IF;
END LOOP;
/* -- print out the column names if desired
FOR i IN 1 .. l_cols LOOP
dbms_output.put_line('** ' || l_desc(i).col_name) ;
END LOOP;
*/
/* Fetch all data... */
WHILE DBMS_SQL.FETCH_ROWS(l_cursor) > 0 LOOP
dbms_output.put_line('LINE: ' || l_processedRows || '');
FOR i IN 1 .. l_cols LOOP
if l_desc(i).col_type = 12 THEN --we are in a date
DBMS_SQL.COLUMN_VALUE(l_cursor, i, v_date);
v_varchar2 := to_char(v_date , 'dd-MON-yyyy' ) ;
elsif l_desc(i).col_type = 2 THEN --we are in a number
DBMS_SQL.COLUMN_VALUE(l_cursor, i, v_number);
v_varchar2 := to_char(v_number) ;
else --treat it as a string (should be varchar2,char,etc)
DBMS_SQL.COLUMN_VALUE(l_cursor, i, v_varchar2);
IF v_varchar2 IS NOT NULL THEN
v_varchar2 := '"' || v_varchar2 || '"' ;
ELSE
v_varchar2 := '';
END IF ;
end if ;
dbms_output.put_line(l_desc(i).col_name || '=>' || v_varchar2) ;
END LOOP;
l_processedRows := l_processedRows + 1 ;
END LOOP;
dbms_sql.close_cursor(l_cursor);
dbms_output.put_line('I found and processed ' || l_processedRows || ' rows .');
END;
begin
open l_cur for select * from temp;
CursorOutput(p_refcursor => l_cur) ;
end ;
/
will give you this result
LINE: 0
AA=>"e"
BB=>1
CC=>04-JAN-2012
LINE: 1
AA=>"f"
BB=>2
CC=>05-JAN-2012
LINE: 2
AA=>"g"
BB=>3
CC=>06-JAN-2012
LINE: 3
AA=>"h"
BB=>4
CC=>07-JAN-2012
LINE: 4
AA=>"i"
BB=>5
CC=>08-JAN-2012
LINE: 5
AA=>"j"
BB=>6
CC=>09-JAN-2012
LINE: 6
AA=>"k"
BB=>7
CC=>10-JAN-2012
LINE: 7
AA=>"l"
BB=>8
CC=>11-JAN-2012
LINE: 8
AA=>"m"
BB=>9
CC=>12-JAN-2012
LINE: 9
AA=>"n"
BB=>10
CC=>13-JAN-2012
LINE: 10
AA=>"o"
BB=>11
CC=>14-JAN-2012
LINE: 11
AA=>"p"
BB=>12
CC=>15-JAN-2012
LINE: 12
AA=>"q"
BB=>13
CC=>16-JAN-2012
LINE: 13
AA=>"r"
BB=>14
CC=>17-JAN-2012
I found and processed 14 rows .
I had done something similar to this to dynamically build a csv file utilizing these two links as sources
http://www.oracle-developer.net/display.php?id=505
http://asktom.oracle.com/pls/apex/f?p=100:11:0::::P11_QUESTION_ID:88212348059
Depending on what you are going for, however, you may just want to run it in SQL Developer (or Toad) and export the results!
If it's a PL/SQL block that you are running in an IDE then you could at a pinch use DBMS_OUTPUT to output the values.
http://docs.oracle.com/cd/B19306_01/appdev.102/b14258/d_output.htm
For example:
SET SERVEROUTPUT ON
DECLARE
-- Define the record
TYPE test_rectype IS RECORD (
field1 NUMBER,
field2 VARCHAR2
);
-- Define a variable for the record
test_rec TEST_RECTYPE;
BEGIN
-- Populate the record
test_rec.field1 := 1;
test_rec.field2 := 'my value';
-- Enable the DBMS_OUTPUT
DBMS_OUTPUT.enable(1000000);
-- Send the output to the buffer
DBMS_OUTPUT.put_line('Field1: '||test_rec.field1||', Field2: '||test_rec.field2);
END;
There is more to DBMS_OUTPUT so take a look at the docs from the link above.
Alternatively, you could write the values to a file using UTL_FILE.
http://docs.oracle.com/cd/B19306_01/appdev.102/b14258/u_file.htm
Hope it helps...

Resources