I have a simple oracle statement in my procedure:
update org.security_training_question a
set a.actv_indr = 'N' where a.qstn_id in (v_qstns_to_delete);
v_qstns_to_delete is a parameter being passed. It is a varchar2 field and a.qstn_id is a numeric field.
When calling the Stored Procedure, for v_qstns_to_delete I am passing the following String: "24, 43, 23, 44, 21".
When I run the statement output the stored procedure thenn it runs fine but when I run it as a stored procedure I get an error on the above line saying Invalid Number.
Any clue?
You can't use a "in" clause with a variable like that. One way around it is
declare stmt varchar2(4000);
begin
stmt := 'update org.security_training_question a set a.actv_indr = ''N'' where a.qstn_id in ('||v_qstns_to_delete||')';
execute immediate stmt;
end;
if v_qstns_to_delete is a varchar, you would need to convert it somewhat to let Oracle understand that there may be several items in it. One method would be to convert the string to a table of items.
Supposing qstn_id is a NUMBER column, you would:
SQL> CREATE TYPE tab_number AS TABLE OF NUMBER;
2 /
Type created
SQL> CREATE OR REPLACE FUNCTION to_tab_number(p_in VARCHAR2,
2 p_separator VARCHAR2 DEFAULT ',')
3 RETURN tab_number AS
4 l_result tab_number := tab_number();
5 l_tail LONG := p_in;
6 BEGIN
7 WHILE l_tail IS NOT NULL LOOP
8 l_result.EXTEND;
9 IF instr(l_tail, p_separator) != 0 THEN
10 l_result(l_result.COUNT) := to_number(substr(l_tail,
11 1,
12 instr(l_tail, p_separator) - 1));
13 l_tail := substr(l_tail, instr(l_tail, p_separator) + 1);
14 ELSE
15 l_result(l_result.COUNT) := to_number(l_tail);
16 l_tail := NULL;
17 END IF;
18 END LOOP;
19 RETURN l_result;
20 END;
21 /
Function created
You could then convert a string to a table of number from SQL:
SQL> SELECT * FROM TABLE(to_tab_number('24, 43, 23, 44, 21'));
COLUMN_VALUE
------------
24
43
23
44
21
To do a variable in-list:
SQL> SELECT object_id, owner
2 FROM all_objects
3 WHERE object_id IN (SELECT column_value FROM TABLE(to_tab_number('18,19,20')));
OBJECT_ID OWNER
---------- ------------------------------
18 SYS
19 SYS
20 SYS
More on the same subject on askTom.
Related
This question already has answers here:
What is the string concatenation operator in Oracle?
(6 answers)
Closed 8 months ago.
Can you please tell me how can | pass variable in an oracle LIKE statement?
here is the plsql :
declare
variables VARCHAR2 (200):= 'exemple';
V1 integer ;
BEGIN
select COUNT(*)into V1 from user_source
where name LIKE '%'+variables+'%'
;
DBMS_OUTPUT.PUT_LINE(V1);
END;
PS : i need to use the % in the like sql statement. it's not just : where name LIKE variables the % operator is necessary !
thank you for the help !
Like this:
DECLARE
variables VARCHAR2 (200):= 'exemple';
V1 integer;
BEGIN
select COUNT(*)
into V1
from user_source
where name like '%'||variables||'%';
DBMS_OUTPUT.PUT_LINE(V1);
END;
Concatenate, or use INSTR function:
SQL> set serveroutput on
SQL> DECLARE
2 variables VARCHAR2 (200) := 'TEST';
3 v1 INTEGER;
4 BEGIN
5 SELECT COUNT (*)
6 INTO v1
7 FROM user_source
8 WHERE UPPER (name) LIKE '%' || UPPER (variables) || '%';
9
10 DBMS_OUTPUT.put_line (v1);
11
12 --
13
14 SELECT COUNT (*)
15 INTO v1
16 FROM user_source
17 WHERE INSTR (UPPER (name), UPPER (variables)) > 0;
18
19 DBMS_OUTPUT.put_line (v1);
20 END;
21 /
90
90
PL/SQL procedure successfully completed.
SQL>
enter image description here
declare
str varchar2(2000) := :inputstr;
v_len number;
currChar CHAR(1);
begin
v_len := length(str);
for i in 1..v_len
Loop
currChar := substr(str,i,1);
if currChar = 1 then
dbms_output.put_line('curr index' || i);
end if;
End loop;
end;
When I pass '000111000' as input to IN_STRING variable , it trims the string and behaves very unusually.Please suggest some good approaches to iterate over binary strings like this.I am expecting output as 4,5,6 from above operation.
EDIT1:
Please don't directly input the string as str varchar2(2000) := '000111000';
Instead input it from bind variable as I mentioned above.
Your code works so long as you pass in a VARCHAR2 data type (and not a NUMBER).
You can also tidy up the code passing in the bind variable only once and using CONSTANTs to hold the values that are constant:
VARIABLE in_string VARCHAR2;
DECLARE
c_string CONSTANT VARCHAR2(200) := :in_string;
c_length CONSTANT PLS_INTEGER := LENGTH(c_string);
v_out CHAR(1);
BEGIN
FOR i IN 1..c_length
LOOP
v_out := SUBSTR(c_string,i,1) ;
DBMS_OUTPUT.PUT_LINE(v_out);
END LOOP;
END;
/
Which outputs:
0
0
1
1
1
0
0
0
db<>fiddle here
Shouldn't behave unusual, unless datatype of in_string variable is NUMBER (then leading zeros don't have any meaning) - switch to VARCHAR2.
Illustration:
NUMBER variable datatype
value you enter
result - really, missing leading zeros
Otherwise, it works OK (this is SQL*Plus so I used substitution variable):
SQL> DECLARE
2 v_length NUMBER (10);
3 v_out VARCHAR2 (20);
4 BEGIN
5 v_length := LENGTH ( '&&in_string');
6
7 FOR i IN 1 .. v_length
8 LOOP
9 v_out := SUBSTR ( '&&in_string', i, 1);
10 DBMS_OUTPUT.PUT_LINE (v_out);
11 END LOOP;
12 END;
13 /
Enter value for in_string: 00111000
0
0
1
1
1
0
0
0
PL/SQL procedure successfully completed.
Another option (if you're interested in it) doesn't require PL/SQL:
SQL> SELECT SUBSTR ( '&&in_string', LEVEL, 1) val
2 FROM DUAL
3 CONNECT BY LEVEL <= LENGTH ( '&&in_string');
V
-
0
0
1
1
1
0
0
0
8 rows selected.
SQL>
How do i print number of rows in utl_file.
If i am using dbms_output.put_line('Total record'||'|'||SQL%ROWCOUNT);
and
If i am ​using dbms_output.put_line('Total record'||'|'||To_char(SQL%ROWCOUNT));
Compiler saying wrong argument is passed. nothing is reflecting
and
utl_file.put(file_handele,'total roecord' ||'|'|| SQL%ROWCOUNT);
only total record is reflecting.
please help new to psql
What do you call "number of rows in utl_file"?
You said that 1st and 2nd statements are invalid because compiler is complaining. It is not for me, so I kind of doubt what you claim:
SQL> begin
2 -- your 1st statement:
3 dbms_output.put_line('Total record'||'|'||SQL%ROWCOUNT);
4
5 -- your 2nd statement:
6 dbms_output.put_line('Total record'||'|'||To_char(SQL%ROWCOUNT));
7 end;
8 /
Total record|
Total record|
PL/SQL procedure successfully completed.
As of the 3rd statement:
SQL> declare
2 file_handele utl_file.file_type;
3 begin
4 file_handele := utl_file.fopen('EXT_DIR', 'test.txt', 'w');
5
6 -- your 3rd statement, literally, with typos:
7 utl_file.put(file_handele,'total roecord' ||'|'|| SQL%ROWCOUNT);
8
9 utl_file.fclose(file_handele);
10 end;
11 /
PL/SQL procedure successfully completed.
SQL> $type c:\temp\test.txt
total roecord|
SQL>
All 3 statements are OK (i.e. they don't fail, they don't raise an error), but the question is: which problem are you trying to solve? Display number of rows written into a file using UTL_FILE package? If so, you should have posted that piece of code as well.
Anyway: one option is to literally count them. For example:
SQL> declare
2 i number := 0; -- this is the counter
3 file_handele utl_file.file_type;
4 begin
5 file_handele := utl_file.fopen('EXT_DIR', 'test.txt', 'w');
6
7 for cur_r in (select dname from dept) loop
8 utl_file.put(file_handele, cur_r.dname || utl_tcp.crlf);
9 i := i + 1; -- increment the counter
10 end loop;
11
12 -- your 3rd statement, literally, with typos:
13 utl_file.put(file_handele,'total roecord' ||'|'|| i);
14
15 utl_file.fclose(file_handele);
16 end;
17 /
PL/SQL procedure successfully completed.
The result:
SQL> $type c:\temp\test.txt
ACCOUNTING
RESEARCH
SALES
OPERATIONS
total roecord|4 --> here's the total number of lines written into the file
SQL>
I need to write a block in PL/SQL which may results into the addition of the number inputted.
For example, if I'm entering 245 so it should gives the output like addition if these 3 number(I.e. 2+4+5).
I hope you got my query.
Thanks in advance.
You don't really need PL/SQL for that.
SQL> set ver off
SQL>
SQL> select sum(dig) result
2 from (select substr(&&enter_number, level, 1) dig
3 from dual
4 connect by level <= length(&&enter_number)
5 );
Enter value for enter_number: 245
RESULT
----------
11
It is a simple task to rewrite it to a function:
SQL> create or replace function f_dig (par_enter_number in number)
2 return number
3 is
4 retval number;
5 begin
6 select sum(dig)
7 into retval
8 from (select substr(par_enter_number, level, 1) dig
9 from dual
10 connect by level <= length(par_enter_number)
11 );
12 return retval;
13 end;
14 /
Function created.
SQL> select f_dig(245) result_1,
2 f_dig(189) result_2,
3 f_dig(9834188) result_3
4 from dual;
RESULT_1 RESULT_2 RESULT_3
---------- ---------- ----------
11 18 41
SQL>
You may try below function for the same -
CREATE OR REPLACE FUNCTION digit_sum(P_N NUMBER)
RETURN NUMBER
IS
n number := P_N;
s number := 0;
m number := 0;
BEGIN
WHILE (n > 0) LOOP
m := MOD(n, 10);
s := s + m;
n := TRUNC(n/10);
END LOOP;
RETURN (s);
END digit_sum;
/
Here is the fiddle.
For simple things is it better to use the translate function on the premise that it is less CPU intensive or is regexp_replace the way to go?
This question comes forth from How can I replace brackets to hyphens within Oracle REGEXP_REPLACE function?
I think you're running into simple optimization. The regexp expression is so expensive to compute that the result is cached in the hope that it will be used again in the future. If you actually use distinct strings to convert, you will see that the modest translate is naturally faster because it is its specialized function.
Here's my example, running on 11.1.0.7.0:
SQL> DECLARE
2 TYPE t IS TABLE OF VARCHAR2(4000);
3 l t;
4 l_level NUMBER := 1000;
5 l_time TIMESTAMP;
6 l_char VARCHAR2(4000);
7 BEGIN
8 -- init
9 EXECUTE IMMEDIATE 'ALTER SESSION SET PLSQL_OPTIMIZE_LEVEL=2';
10 SELECT dbms_random.STRING('p', 2000)
11 BULK COLLECT
12 INTO l FROM dual
13 CONNECT BY LEVEL <= l_level;
14 -- regex
15 l_time := systimestamp;
16 FOR i IN 1 .. l.count LOOP
17 l_char := regexp_replace(l(i), '[]()[]', '-', 1, 0);
18 END LOOP;
19 dbms_output.put_line('regex :' || (systimestamp - l_time));
20 -- tranlate
21 l_time := systimestamp;
22 FOR i IN 1 .. l.count LOOP
23 l_char := translate(l(i), '()[]', '----');
24 END LOOP;
25 dbms_output.put_line('translate :' || (systimestamp - l_time));
26 END;
27 /
regex :+000000000 00:00:00.979305000
translate :+000000000 00:00:00.238773000
PL/SQL procedure successfully completed
on 11.2.0.3.0 :
regex :+000000000 00:00:00.617290000
translate :+000000000 00:00:00.138205000
Conclusion: In general I suspect translate will win.
For SQL, I tested this with the following script:
set timing on
select sum(length(x)) from (
select translate('(<FIO>)', '()[]', '----') x
from (
select *
from dual
connect by level <= 2000000
)
);
select sum(length(x)) from (
select regexp_replace('[(<FIO>)]', '[\(\)\[]|\]', '-', 1, 0) x
from (
select *
from dual
connect by level <= 2000000
)
);
and found that the performance of translate and regexp_replace were almost always the same, but it could be that the cost of the other operations is overwhelming the cost of the functions I'm trying to test.
Next, I tried a PL/SQL version:
set timing on
declare
x varchar2(100);
begin
for i in 1..2500000 loop
x := translate('(<FIO>)', '()[]', '----');
end loop;
end;
/
declare
x varchar2(100);
begin
for i in 1..2500000 loop
x := regexp_replace('[(<FIO>)]', '[\(\)\[]|\]', '-', 1, 0);
end loop;
end;
/
Here the translate version takes just under 10 seconds, while the regexp_replace version around 0.2 seconds -- around 2 orders of magnitude faster(!)
Based on this result, I will be using regular expressions much more often in my performance critical code -- both SQL and PL/SQL.