Declare a Type as a table of number(38,0).
In a procedure of function.
code example:
ids_list := Type(....); // load data to variable
ids_list.trim(count);
I don't quite understand what will do to trim a list with a numeric list with numeric parameter. What will ids_list.trim(count) happens?
If I understood you correctly, you're talking about a collection and its trim method. If that's so, then documentation says:
Decreasing the Size of a Collection (TRIM Method)
This procedure has two forms:
TRIM removes one element from the end of a collection.
TRIM(n) removes n elements from the end of a collection.
If you want to remove all elements, use DELETE without parameters.
Code you posted doesn't do anything because of
SQL> declare
2 type numlist is table of number;
3 n numlist := numlist(1,2,3, 10);
4 begin
5 n.trim(count);
6 end;
7 /
n.trim(count);
*
ERROR at line 5:
ORA-06550: line 5, column 10:
PLS-00204: function or pseudo-column 'COUNT' may be used inside a SQL statement only
ORA-06550: line 5, column 3:
PL/SQL: Statement ignored
SQL>
Perhaps you meant to use the count method instead (n.count)?
SQL> declare
2 type numlist is table of number;
3 n numlist := numlist(1,2,3, 10);
4 begin
5 dbms_output.put_line('Number of elements in that collection = ' || n.count);
6
7 n.trim(n.count);
8
9 dbms_output.put_line('Number of elements in that collection AFTER trim = ' || n.count);
10 end;
11 /
Number of elements in that collection = 4
Number of elements in that collection AFTER trim = 0
PL/SQL procedure successfully completed.
SQL>
If so, then - as you can see - it removes all elements from the collection.
Related
ORA-06550: line 6, column 31: PLS-00103: Encountered the symbol ")"
when expecting one of the following: (
I don't know how to manage error
create or replace PROCEDURE Get_StarInfo(Stars in out starsin.starname%type, cnt out integer, avg out float)
is
begin
select starname, count(title), avg(length) into Stars, cnt, avg
from starsin, movie
where movietitle=title
and movieyear=year
and starname=Stars
group by starname;
exception
when others then
cnt := -1;
avg := -1;
end;
declare
Stars starsin.starname%type := 'harrison ford';
cnt integer:=3;
avg float:=3.1;
begin
get_starinfo(Stars,cnt,avg);
end;
As Alex commented, it was a bad idea calling variable or parameter the same as built-in functions. Though, you can make it work but - you'd rather not do it that way.
I don't have your tables so I'm using Scott's sample schema, calculating some values from its emp table based on department number (that's your "stars").
SQL> create or replace PROCEDURE Get_StarInfo(Stars in out number, cnt out integer, avg out float)
2 is
3 begin
4 select deptno, count(*), avg(sal)/20 into Stars, cnt, avg
5 from emp
6 where deptno = stars
7 group by deptno;
8 exception
9 when others then
10 cnt := -1;
11 avg := -1;
12 end;
13 /
Procedure created.
This is how you called it and got the error:
SQL> set serveroutput on;
SQL> declare
2 Stars number := 10;
3 cnt integer; -- :=3;
4 avg float; -- :=3.1;
5 begin
6 get_starinfo(Stars,cnt,avg);
7 dbms_output.put_line('count = ' || cnt ||', average = ' || "AVG");
8 end;
9 /
get_starinfo(Stars,cnt,avg);
* --> see where the asterisk points to? That's where the error is!
ERROR at line 6:
ORA-06550: line 6, column 31:
PLS-00103: Encountered the symbol ")" when expecting one of the following:
(
ORA-06550: line 8, column 4:
PLS-00103: Encountered the symbol "end-of-file" when expecting one of the
following:
end not pragma final instantiable persistable order
overriding static member constructor map
SQL>
Now, how to use that "avg" of yours:
a side note: cnt and avg local variables (lines #3 and #4) are supposed to accept values returned by the procedure, so there's no point in setting their initial values
Oracle, by default, uses uppercase for object names, column names etc., so - if you enclose avg into double quotes and switch to uppercase (line #6), there's no error any more
So:
SQL> set serveroutput on;
SQL> declare
2 Stars number := 10;
3 cnt integer; -- :=3;
4 avg float; -- :=3.1;
5 begin
6 get_starinfo(Stars,cnt,"AVG");
7 dbms_output.put_line('count = ' || cnt ||', average = ' || "AVG");
8 end;
9 /
count = 3, average = 145.835
PL/SQL procedure successfully completed.
SQL>
Once again: as suggested, just don't do that. What we usually do, is to use prefix (such as v_ for "variable" or l_ for "local variable" or p_ for "parameter", etc.)
DECLARE
a NUMBER;
b NUMBER;
BEGIN
a :=: a;
b :=: b;
DBMS_OUTPUT.PUT_LINE('sum = '|| a+b);
END;
I am getting error as numeric or value error
The problem is with operator precedence:
Oracle evaluates operators with equal precedence from left to right within an expression.
... and as the table shows + and || have equal precedence. So in this statement:
DBMS_OUTPUT.PUT_LINE('sum = '|| a+b);
this is interpreted as 'sum = '|| a which gives you a string, then <result string> + b, and string + number will cause the error you see if the string cannot be implicitly converted to a number - and 'sum = 1' can't be.
You can add parentheses to override the default precedence:
DBMS_OUTPUT.PUT_LINE('sum = '|| (a+b));
db<>fiddle
A simplified version to clarify the issue:
begin
dbms_output.put_line('Result ' || 2 + 2);
end;
/
ERROR at line 1:
ORA-06502: PL/SQL: numeric or value error: character to number conversion error
ORA-06512: at line 2
This fails because the expression is built left to right, and you can't add 2 to the string 'Result: 2'.
It would work with multiplication or division, because arithmetic precedence causes those operations to be evaluated first.
begin
dbms_output.put_line('Result ' || 2 / 2);
end;
/
Result 1
PL/SQL procedure successfully completed.
To allow this to work for any operation, you need to bracket the expression:
begin
dbms_output.put_line('Result ' || (2 + 2));
end;
/
Result 4
PL/SQL procedure successfully completed.
Just to add, it is clearer to write host/bind variables without any space after :, for example :myvar rather than : myvar.
You're not seeing a sqldeveloper error, you're getting an error running a sql statement in sqldeveloper, that is something very different.
For simplicity I'm declaring the variables in my example instead of using bind variables like you do:
DECLARE
a NUMBER;
b NUMBER;
BEGIN
a := 1;
b := 2;
DBMS_OUTPUT.PUT_LINE('sum = '||a+b);
END;
Returns
ORA-06502: PL/SQL: numeric or value error: character to number conversion error
ORA-06512: at line 7
The important information is the "line 7". Something is failing in that dbms_output statement and the cause is the operator precedence. Best is to tell the database engine that you want the number concatenated to the string, the error indicates that there is an implicit conversion happening.
The following works just fine:
DECLARE
a NUMBER;
b NUMBER;
BEGIN
a := 1;
b := 2;
DBMS_OUTPUT.PUT_LINE('sum = '|| TO_NUMBER(a+b));
END;
One additional remark. This syntax
a :=: a;
works but is very confusing. I would use a more descriptive name for your bind variable and separate the assignment operator := from the bind variable. Writing it like this is a lot more readable:
a := :variablea;
FOR this_loop
IN (SELECT field_A, field_B
FROM TABLE_NAME
WHERE num = i_num)
LOOP
IF this_loop.field_B BETWEEN 1 AND 3
THEN
v_A := v_A || ' ' || this_loop.field_A;
ELSIF this_loop.field_B BETWEEN 4 AND 8
THEN
v_field_A := v_field_A || ' ' || this_loop.field_A; -- Error is at this line
ELSIF this_loop.field_B BETWEEN 9 AND 15
THEN
v_B := v_B || ' ' || this_loop.field_A;
END IF;
END LOOP;
Variable decalared as
v_field_A VARCHAR2 (100);
What I know -
Variable v_field_A cannot hold value more than 100 characters
The output I get from the SELECT Query doesn't have more than 10 characters.
My question - How is even possible to face this issue of space buffer when the characters are whitin the limit of varchar2 ?
I have faced this issue few years back but last time the cause was output of select query. It had more than 100 characters and hence the size issue but this time it is not more than 10 characters. I'm confused. Any help is appreciated
Variable v_field_A cannot hold value more than 100 characters
Why not? It is very much possible since you are concatenating the variable for each row in the CURSOR FOR LOOP.
For example,
SQL> DECLARE
2 v_name VARCHAR2(50);
3 BEGIN
4 FOR i IN
5 (SELECT ename FROM emp
6 )
7 LOOP
8 v_name := v_name || i.ename;
9 END LOOP;
10 END;
11 /
DECLARE
*
ERROR at line 1:
ORA-06502: PL/SQL: numeric or value error: character string buffer too small
ORA-06512: at line 8
Use DBMS_OUTPUT to see the current size of the variable and the new value being appended.
Let's debug
SQL> DECLARE
2 v_name VARCHAR2(50);
3 BEGIN
4 FOR i IN
5 (SELECT ename FROM emp
6 )
7 LOOP
8 dbms_output.put_line('Length of new value = '||LENGTH(i.ename));
9 v_name := v_name || i.ename;
10 dbms_output.put_line('Length of variable = '||LENGTH(v_name));
11 END LOOP;
12 END;
13 /
Length of new value = 5
Length of variable = 5
Length of new value = 5
Length of variable = 10
Length of new value = 4
Length of variable = 14
Length of new value = 5
Length of variable = 19
Length of new value = 6
Length of variable = 25
Length of new value = 5
Length of variable = 30
Length of new value = 5
Length of variable = 35
Length of new value = 5
Length of variable = 40
Length of new value = 4
Length of variable = 44
Length of new value = 6
Length of variable = 50
Length of new value = 5
Error
DECLARE
*
ERROR at line 1:
ORA-06502: PL/SQL: numeric or value error: character string buffer too small
ORA-06512: at line 9
It is pretty clear, we wanted to concatenate a string with length 5 to the variable declared as max size 50, currently holding a value of size 50. hence, it throws the error ORA-06502: PL/SQL: numeric or value error: character string buffer too small.
11 rows * 10 character > 100 characters => error - your concatenating many rows of 10 characters together into a one of three variables. One of them will be growing each time through the loop.
What am I missing?
Also beware of characters versus bytes. In a varchar2, each CHARACTER will likely take 2 bytes.
Check how many times this condition is being executed and what values are being concatenated. Since this being concatenated multiple number of times there is a possibility that size of v_field_A is going beyond 50 characters
ELSIF this_loop.field_B BETWEEN 4 AND 8
THEN
v_field_A := v_field_A || ' ' || this_loop.field_A;
To resolve this you can increase the size of this variable. You can declare a varchar upto 32,767 bytes
You can do the string concatenation in a SQL query:
SELECT field_A,
LISTAGG(CASE WHEN field_B BETWEEN 1 AND 3 THEN field_A END, ' ') WITHIN GROUP (ORDER BY field_A) as val1,
LISTAGG(CASE WHEN field_B BETWEEN 4 AND 8 THEN field_A END, ' ') WITHIN GROUP (ORDER BY field_A) as val2,
LISTAGG(CASE WHEN field_B BETWEEN 9 AND 15 THEN field_A END, ' ') WITHIN GROUP (ORDER BY field_A) as val3
FROM TABLE_NAME
WHERE num = i_num;
This is intended to simplify your code. You are still limited by Oracle string lengths. You can get around the Oracle limit in PL/SQL using a CLOB, but that is unnecessary when the final string is only a few hundred characters.
The answer to your question lies in how many times the loop
executes and how many times it goes inside IF condition.
EXAMPLE :
Condition : BETWEEN 4 AND 8
this_loop.field_A:= 'test';
Number of times loop executes = 100
Due to CONCATINATION the size will definitely shoot up more than 100 CHAR.
Similar will the case with other LOOP conditions.
Solution : Try using CLOB instead of VARCHAR to eliminate this issue.
Oracle error are very descriptive. If it throws some error it pretty much explains the scenario :P.
How to pass String values through variable in VARRAY functions in PL/SQL.
Coding:
create or replace
PROCEDURE dynamic_query_build
(
vr_plan_sku_id IN VARCHAR2
)
IS
type plan_sku_id_array IS VARRAY(999) OF VARCHAR2(5000);
plan_sku_id plan_sku_id_array;
total integer;
vrx_plan_sku_id VARCHAR2(3000);
BEGIN
vrx_plan_sku_id:= replace(vr_plan_sku_id,',',chr(39)||','||chr(39));
vrx_plan_sku_id:=chr(39)||vrx_plan_sku_id||chr(39);
--plan_sku_id := plan_sku_id_array('Apple','Apple','Orange','Banana');
dbms_output.put_line(vrx_plan_sku_id);
plan_sku_id := plan_sku_id_array(vrx_plan_sku_id);
total := plan_sku_id.count;
dbms_output.put_line('Array count: 'total);
EXCEPTION
WHEN OTHERS THEN
raise_application_error(-20001,'An error was encountered - '||SQLCODE||' -ERROR- '||SQLERRM);
END dynamic_query_build;
Execution:
set serveroutput on;
declare
vr_plan_sku_id varchar2(200) := 'Apple,Apple,Orange,Banana';
BEGIN
dynamic_query_build(vr_plan_sku_id);
END;
/
My Output:
Array count: 1
Expected Output:
Array count: 4
Explanation:
When i pass the string values like "plan_sku_id_array('Apple','Apple','Orange','Banana')" then i am getting count values is 4.. But when i pass the same string through variables then it is considering whole value as single value in the varray.
You need to substring your input parameter (based on ,) and then do something like this post (e.g. call EXTEND(); then ary(count)= value). Keep extending and adding as long as you still have some value in your input parameter
Can pass your value string like in given below way. This would convert entire comma separated string in different rows and you can run a loop and store variables in VARRAY.
SELECT trim(x.column_value.extract('e/text()')) COLUMNS
from t t, table (xmlsequence(xmltype('<e><e>' || replace(valuestring,':','</e><e>')||
'</e></e>').extract('e/e'))) x );
I have to write an Oracle query in toad to find all the occurrences of a character in a string. For example if I'm searching for R in the string SSSRNNSRSSR, it should return positions 4, 8 and 11.
I am new to Oracle and tried this.
select instr(mtr_ctrl_flags, 'R', pos + 1, 1) as pos1
from mer_trans_reject
where pos in ( select instr(mtr_ctrl_flags, 'R', 1, 1) as pos
from mer_trans_reject
);
where mtr_ctrl_flags is the column name. I'm getting an error indicating that pos is an invalid identifier.
Extending GolezTrol's answer you can use regular expressions to significantly reduce the number of recursive queries you do:
select instr('SSSRNNSRSSR','R', 1, level)
from dual
connect by level <= regexp_count('SSSRNNSRSSR', 'R')
REGEXP_COUNT() returns the number of times the pattern matches, in this case the number of times R exists in SSSRNNSRSSR. This limits the level of recursion to the exact number you need to.
INSTR() simply searches for the index of R in your string. level is the depth of the recursion but in this case it's also the level th occurrence of the string as we restricted to the number of recurses required.
If the string you're wanting to pick out is more complicated you could go for regular expressions ans REGEXP_INSTR() as opposed to INSTR() but it will be slower (not by much) and it's unnecessary unless required.
Simple benchmark as requested:
The two CONNECT BY solutions would indicate that using REGEXP_COUNT is 20% quicker on a string of this size.
SQL> set timing on
SQL>
SQL> -- CONNECT BY with REGEX
SQL> declare
2 type t__num is table of number index by binary_integer;
3 t_num t__num;
4 begin
5 for i in 1 .. 100000 loop
6 select instr('SSSRNNSRSSR','R', 1, level)
7 bulk collect into t_num
8 from dual
9 connect by level <= regexp_count('SSSRNNSRSSR', 'R')
10 ;
11 end loop;
12 end;
13 /
PL/SQL procedure successfully completed.
Elapsed: 00:00:03.94
SQL>
SQL> -- CONNECT BY with filter
SQL> declare
2 type t__num is table of number index by binary_integer;
3 t_num t__num;
4 begin
5 for i in 1 .. 100000 loop
6 select pos
7 bulk collect into t_num
8 from ( select substr('SSSRNNSRSSR', level, 1) as character
9 , level as pos
10 from dual t
11 connect by level <= length('SSSRNNSRSSR') )
12 where character = 'R'
13 ;
14 end loop;
15 end;
16 /
PL/SQL procedure successfully completed.
Elapsed: 00:00:04.80
The pipelined table function is a fair bit slower, though it would be interesting to see how it performs over large strings with lots of matches.
SQL> -- PIPELINED TABLE FUNCTION
SQL> declare
2 type t__num is table of number index by binary_integer;
3 t_num t__num;
4 begin
5 for i in 1 .. 100000 loop
6 select *
7 bulk collect into t_num
8 from table(string_indexes('SSSRNNSRSSR','R'))
9 ;
10 end loop;
11 end;
12 /
PL/SQL procedure successfully completed.
Elapsed: 00:00:06.54
This is a solution:
select
pos
from
(select
substr('SSSRNNSRSSR', level, 1) as character,
level as pos
from
dual
connect by
level <= length(t.text))
where
character = 'R'
dual is a built in table that just returns a single row. Very convenient!
connect by lets you build recursive queries. This is often used to generate lists from tree-like data (parent/child relations). It allows you to more or less repeat the query in front of it. And you've got special fields, like level that allows you to check how deeply the recursion went.
In this case, I use it to split the string to characters and return a row for each character. Using level, I can repeat the query and get a character until the end of the string is reached.
Then it is just a matter of returning the pos for all rows containing the character 'R'
To take up a_horse_with_no_name's challenge here is another answer with a pipelined table function.
A pipelined function returns an array, which you can query normally. I would expect that over strings with large numbers of matches this will perform better than the recursive query but as with everything test yourself first.
create type num_array as table of number
/
create function string_indexes (
PSource_String in varchar2
, PSearch_String in varchar2
) return num_array pipelined is
begin
for i in 1 .. length(PSource_String) loop
if substr(PSource_String, i, 1) = PSearch_String then
pipe row(i);
end if;
end loop;
return;
end;
/
Then in order to access it:
select *
from table(string_indexes('SSSRNNSRSSR','R'))
SQL Fiddle