There's the simplified version of my code who keep raise me ORA-06502:
declare
p_filter varchar2(300) := '2012';
p_value varchar2(300) := '12345.000';
w_new_value number(13,3) := null ;
w_count number(4) := null ;
BEGIN
SELECT count(*)
INTO w_count
FROM dual
where p_filter = p_filter;
--- more filters
if w_count != 0 then
w_new_value := p_value / w_count;
else
w_new_value := p_value;
end if;
-- do something
end;
/
Someone can give me a help?
DataBase Details
nls_language = italian
nls_territory = italy
nls_currency = �
nls_iso_currency = italy
nls_numeric_characters = ,.
nls_calendar = gregorian
nls_date_format = dd-mon-rr
nls_date_language = italian
nls_characterset = we8iso8859p15
nls_sort = west_european
nls_time_format = hh24:mi:ssxff
nls_timestamp_format = dd-mon-rr hh24:mi:ssxff
nls_time_tz_format = hh24:mi:ssxff tzr
nls_timestamp_tz_format = dd-mon-rr hh24:mi:ssxff tzr
nls_dual_currency = �
nls_nchar_characterset = al16utf16
nls_comp = binary
nls_length_semantics = byte
nls_nchar_conv_excp = false
First, this is always going return a value of 1.
SELECT count(*)
INTO w_count
FROM dual
It doesn't matter what the qualifier is.
Lastly, I just ran your simplified code example in Oracle 11R2 and it didn't throw an exception.
I added the following statement in place of your "do something" comment:
dbms_output.put_line('w_new_value: ' || w_new_value || '. w_count: ' || w_count);
The result was:
w_new_value: 12345. w_count: 1
So, I think you've simplified your example into oblivion. You need to provide something that actually shows the error.
Good luck.
I found myself the ansewer and i think is useful for other know.
The real problem of the script for my DB is the language.
The italian "version" of Oracle accept , instead of the . for translate the VARCHAR2 into NUMBER unlike the most of other country.
For make the code running well the solution is
w_new_value := replace(p_value,'.',',') / w_count;
This trick finally allows the DB use my VARCHAR2 param like a NUMBER
Related
Using ROracle package in R Studio 2021.09.0
I have a stored procedure
PROCEDURE A_TEST_RORACLE_IN_BOOLEAN_OUT_CLOB (v_bool IN BOOLEAN, v_resp OUT CLOB) IS
BEGIN
IF (v_bool=TRUE) THEN
v_resp := 'The value passed is TRUE';
ELSIF (v_bool=FALSE) THEN
v_resp := 'The value passed is FALSE';
ELSE
v_resp := 'The value passed is NULL';
END IF;
END A_TEST_RORACLE_IN_BOOLEAN_OUT_CLOB;
I have a functioning driver/con etc - have called similar test procedures using INTEGER IN and CLOB OUT vars ... hung up on the BOOLEAN thing.
I call it using these lines in an R script.
v_answer = FALSE
df_test <- data.frame(v_bool=v_answer,v_resp=as.character(NA),stringsAsFactors=FALSE)
attr(df_test$v_bool, "ora.parameter_name") <- "oompalumpa"
attr(df_test$v_bool, "ora.parameter_mode") <- "IN"
attr(df_test$v_bool, "ora.type") <- "BOOLEAN"
attr(df_test$v_resp, "ora.parameter_name") <- "jazzy"
attr(df_test$v_resp, "ora.parameter_mode") <- "OUT"
attr(df_test$v_resp, "ora.type") <- "CLOB"
v_test_smpl_mv <- 'BEGIN TESTSCHEMA.TESTPACKAGE.A_TEST_RORACLE_IN_BOOLEAN_OUT_CLOB(:oompalumpa,:jazzy);END;'
v_result <- oracleProc(con,v_test_smpl_mv,df_test)
get response
Error in .oci.oracleProc(conn, statement, data = data, prefetch = prefetch, :
ORA-06550: line 1, column 7:
PLS-00306: wrong number or types of arguments in call to 'A_TEST_RORACLE_IN_BOOLEAN_OUT_CLOB'
ORA-06550: line 1, column 7:
PL/SQL: Statement ignored
have tried:
df_test <- data.frame(v_bool=as.logical(v_answer),v_resp=as.character(NA),stringsAsFactors=FALSE)
and
leaving out the BOOLEAN specific ora.type attr()
and setting the ora.type attr() to other types (CHAR,VARCHAR,RAW)
and changing the value of v_answer
v_answer = 'FALSE'
... all roads lead to silence or ORA- errors.
this ad-hoc pl/sql call returns 'The value passed is NULL' as expected.
DECLARE
v_clob CLOB;
v_char boolean;
BEGIN WRM_ADMIN.WRM_MAINT_DATA_LOAD_02.A_TEST_RORACLE_IN_BOOLEAN_OUT_CLOB(v_char,v_clob);
DBMS_OUTPUT.PUT_LINE(v_clob);
END;
Left to conclude that BOOLEAN parameters are a bad idea, or at the very least unsupported in OCI/RORACLE ... but I cannot figure out where that is documented.
I have looked here:
https://cran.r-project.org/web/packages/ROracle/ROracle.pdf
And googled the google ... nothing comes back that helps me.
Thanks in advance for any definitive guidance you can offer.
Contacted the maintainer of the package # Oracle and got a report back that they will add BOOLEAN support to a future release of ROracle; they were extremely responsive and helpful.
In the meantime ... presuming you already have other code that uses BOOLEAN values and you need a quick workaround.
-- change the in parameters to char
PROCEDURE A_TEST_RORACLE_IN_CHARBOOL_OUT_CLOB (v_bl_flg IN CHAR default 'F', v_resp OUT CLOB) IS
-- ADD THIS vvv
v_bool BOOLEAN;
BEGIN
-- ADD THIS vvv
IF (v_bl_flg = 'T') THEN
v_bool := TRUE;
ELSIF (v_bl_flg = 'F') THEN
v_bool := FALSE
END IF;
-- ADD THIS ^^^^
IF (v_bool=TRUE) THEN
v_resp := 'The value passed is TRUE';
ELSIF (v_bool=FALSE) THEN
v_resp := 'The value passed is FALSE';
ELSE
v_resp := 'The value passed is NULL';
END IF;
END A_TEST_RORACLE_IN_CHARBOOL_OUT_CLOB;
and adjust the R-part accordingly
v_answer = 'F'
df_test <- data.frame(v_bl_flg=as.character(v_answer),v_resp=as.character(NA),stringsAsFactors=FALSE)
## attr(df_test$v_bl_flg, "ora.parameter_name") <- "oompalumpa"
attr(df_test$v_bl_flg, "ora.parameter_mode") <- "IN"
attr(df_test$v_bl_flg, "ora.type") <- "CHAR"
## attr(df_test$v_resp, "ora.parameter_name") <- "jazzy"
attr(df_test$v_resp, "ora.parameter_mode") <- "OUT"
attr(df_test$v_resp, "ora.type") <- "CLOB"
v_test_smpl_mv <- 'BEGIN TESTSCHEMA.TESTPACKAGE.A_TEST_RORACLE_IN_CHARBOOL_OUT_CLOB(:oompalumpa,:jazzy);END;'
v_result <- oracleProc(con,v_test_smpl_mv,df_test)
Above is close but has not been tested. May require adjustment. I've had more trouble suggesting the parameter name in an ATTR() call than just leaving it out - hence the commented out ATTR() lines for ora.parameter_name.
Hope this helps someone who runs across this in the future.
I'm trying to understand more about how arguments/parameters are used when calling and executing procedures. I understand that procedures that contain an parameter with a leading colon (:) pass back values to the calling DO command, however I think what it a little confusing is it appears that the variables names from what the calling DO command issues, and what the procedure (called by DO) returns don't necessarily have to be the same name. If someone could help shed some light on the following examples and explain what values are being either passed to/from or how their referenced by the issuing DO command that would be helpful.
It looks like the call to run the Get-Recursive-Reports-To (do Get-Recursive-Reports-To($Recursive_Line, $_POSITION_NBR, #_Global_Counter) is issuing these 3 variables as parameters to the procedure Get-Recursive-Reports-To, however as I look through the Get-Recursive-Reports-To procedure, I do not see any references of the variable $Recursive_Line within this procedure, so is the procedure actually using it or what is the purpose of including it? Similar question with $_val_Position_NBR, where is this variable getting it's value assigned from?
And then within the Get-Recursive-Reports-To procedure I see a parameter - :$var_Next_EMPLID that I believe is being passed back to the calling DO in the Run-Recursion procedure, but I can not figure out how/where it uses the value passed back to it...
begin-procedure Run-Recursion
let #_Global_Counter = 0
let $Recursive_Line = ''
let $Data_Line_EMPL = ''
let $Data_Line_EMPL = $_BUSINESS_UNIT || '|' || $_BUSINESS_UNIT_DESCR || '|' || '2019' || '|' ||
$_EMPLID || '|' || $_NAME || '|' || $_DEPTID || '|' || $_DEPT_DECSR || '|' || $_JOBCODE || '|'
do Get-Recursive-Reports-To($Recursive_Line, $_POSITION_NBR, #_Global_Counter)
let $Data_Line_EMPL = $Data_Line_EMPL || $Recursive_Line
do Write-Data-Line($Data_Line_EMPL)
end-procedure
begin-procedure Get-Recursive-Reports-To(:$val_Data_Line, $val_Current_Position_Nbr, #Recursion_Counter)
let #Recursion_Counter = #Recursion_Counter + 1
do Get-the-ReportsTo-for-the-Current-Position($val_Current_Position_Nbr, $Next_Position_Nbr, $Next_EMPLID)
do Check-For-Stop($Stop_Recursion, $val_Current_Position_Nbr, $Next_Position_Nbr, #Recursion_Counter)
if $Stop_Recursion = 'N'
let $val_Data_Line = $val_Data_Line || $Next_EMPLID || '|'
do Get-Recursive-Reports-To($val_Data_Line, $Next_Position_Nbr, #Recursion_Counter)
end-if
end-procedure
begin-procedure Get-the-ReportsTo-for-the-Current-Position($_val_Position_NBR, :$var_ReportsTo, :$var_Next_EMPLID)
let #local_counter = 0
begin-select
G.REPORTS_TO &G.Reports_to
let $var_ReportsTo= &G.Reports_To
from PS_POSITION_DATA G
WHERE G.POSITION_NBR = $_val_Position_NBR
and G.EFF_STATUS = 'A'
AND (G.EFFDT =
(SELECT MAX(G_ED.EFFDT) FROM PS_POSITION_DATA G_ED
WHERE G.POSITION_NBR = G_ED.POSITION_NBR
AND G_ED.EFFDT <= $_As_OF_Date))
end-select
begin-select
H.EMPLID &H.EMPLID
Z.NAME &Z.NAME
let $var_Next_EMPLID= &H.EMPLID
let #local_counter = #local_counter + 1
from PS_JOB H !, PS_EMPLOYEES Z
WHERE H.POSITION_NBR = $var_ReportsTo
and H.EMPL_STATUS not in ('D', 'R', 'T')
and (H.EFFDT =
(SELECT MAX(H_ED.EFFDT) FROM PS_JOB H_ED
WHERE H.EMPLID = H_ED.EMPLID
AND H.EMPL_RCD = H_ED.EMPL_RCD
AND H_ED.EFFDT <= $_As_Of_Date))
end-select
if #local_counter > 1
let $var_Next_EMPLID = $local_counter || ' ' || 'Employees in this Position'
end-if
if #local_counter = 0
let $var_Next_EMPLID = 'Position Vacant'
end-if
end-procedure
The FIRST call to Get-Recursive-Reports-To from the main procedure uses the $Recursive_Line variable. It doesn't have to be referenced again because it's internal to the procedure.
Once IN procedure Get-Recursive-Reports-To, the name of this variable becomes $val_Data_Line and is used to call back to the same procedure Get-Recursive-Reports-To which can change it. I guess ergo the name, Get-Recursive-Reports-To.
Recursion is a pain in the neck but it looks like it's being used to go up an organizational chain, finding the reports to employee id, then getting the name of that person.
Variable $var_Next_EMPLID is passed from Get-the-ReportsTo-for-the-Current-Position back to the caller as variable $Next_Emplid which is then used again to call Get-Recursive-Reports-To.
I can't tell when or why the Recursion stops since you didn't include the procedure "Check-For-Stop". Looks like there is some check - perhaps if the $Val_Current_Position_Nbr or $Next_Position_Nbr is blank or equal or something.
In my Pl/Sql code , I have three variables v_var1 , v_operand , v_var2 whose values are populated based on some logic (v_var1 & v_var2 can be date , number , varchar. Associated Operand will be according to data type only). A sample would be
v_var1 = 10 , v_operand = '=' , v_var2 = 20.
Based on these value , I have to evaluate whether the condition "v_var1 -v_operand- v_var2"is true or false.
Ex :- with above values, I have to evaluate whether 10 equals 20 or not.
How can I achieve this ? Can I pass the whole string as '10 = 20' to some function and get the result as false?
One way I can think of is to write CASE statements for evaluating but can there be a better way ?
You could use dynamic SQL to do the evaluation as a filter on the dual table:
declare
v_var1 varchar2(10) := '10';
v_operand varchar2(10) := '=';
v_var2 varchar2(10) := '20';
l_result number;
begin
execute immediate 'select count(*) from dual where :var1 ' || v_operand || ' :var2'
into l_result using v_var1, v_var2;
if l_result = 1 then
dbms_output.put_line('True');
else
dbms_output.put_line('False');
end if;
end;
/
PL/SQL procedure successfully completed.
False
If the condition is true the count will get 1, otherwise it will get 0, and you can then test that via the local variable you select the count into.
Holding dates and numbers as strings isn't ideal, even temporarily, but might be OK as long as you convert to/from the real data types consistently, e.g. always explicitly converting dates with to_date and to_char and specifying the format masks.
How can I change this nested IF statement to a compound IF statements, I know it is pretty much the same but I do not know how to implement that with my code.
SET VERIFY OFF
DECLARE
v_idno donornew.idno%TYPE :=&input_idno;
v_yrgoal donornew.yrgoal%TYPE;
v_newgoal donornew.yrgoal%TYPE;
v_state donornew.state%TYPE;
v_city donornew.city%TYPE;
BEGIN
SELECT yrgoal, state, city INTO v_yrgoal, v_state, v_city
FROM donornew
WHERE idno = v_idno;
IF v_state = 'MA' AND ( v_yrgoal < 400 or v_city = 'Fall River') THEN
v_newgoal := v_yrgoal * 2.5;
ELSE
v_newgoal := v_yrgoal * 1.3;
END IF;
UPDATE donornew
SET yrgoal = v_newgoal
WHERE idno = v_idno;
COMMIT;
END;
/
SET VERIFY ON
Hello bascically for thsi purpose i will suggest you to go with purely SQL logic rather than PLSQL approach. Merge is the perfect example to resolve your issues.
-- Approach is to use SQL approach rather then using PLSQL approach.
MERGE INTO donornew D USING
(SELECT yrgoal,
state,
city,
idno,
CASE
WHEN state = 'MA'
AND (yrgoal < 400
OR city = 'Fall River')
THEN yrgoal * 2.5
ELSE yrgoal * 1.3
END newgoal
FROM donornew
WHERE idno = v_idno -- Input IDNO
)A ON (d.idno = a.idno )
WHEN MATCHED THEN
UPDATE SET yrgoal = a.newgoal;
Let me know if this helps.
I have a log table and the table has a varchar2 field which holds xml string like below:
In this example ClientName attribute did not change but Clientsurname changed.
I want to capture changed columns and their previous and new values.
The log table contains millions of records.
Which method can you suggest for parsing this data in an efficient way?
<r>
<columntag nameattribute="ClientName">
<new_value>Jeffrey</new_value>
<previous_value>Jeffrey</previous_value>
</columntag>
<columntag nameattribute="ClientSurname">
<new_value>Dijk</new_value>
<previous_value>Disk</previous_value>
</columntag>
</r>
Thank you
not 100% sure the below is what you are after but it should give you some ideas about how to go about it. Hope it is helpfull
CREATE TABLE "RM4SERV"."LOG_TEST" ( "TESTLOG" VARCHAR2(4000 BYTE))
Insert into RM4SERV.LOG_TEST (TESTLOG) values ('<r><columntag nameattribute="ClientName"><new_value>Jeffery</new_value><previous_value>Jeffery</previous_value> </columntag><columntag nameattribute="ClientSurname"><new_value>Dijk</new_value><previous_value>Disk</previous_value></columntag></r>');
Insert into RM4SERV.LOG_TEST (TESTLOG) values ('<r><columntag nameattribute="ClientName"><new_value>Jeffery</new_value><previous_value>Jeffery</previous_value> </columntag><columntag nameattribute="ClientSurname"><new_value>Disk</new_value><previous_value>Disk</previous_value></columntag></r>');
Insert into RM4SERV.LOG_TEST (TESTLOG) values ('<r><columntag nameattribute="ClientName"><new_value>Jeffery</new_value><previous_value>Jim</previous_value> </columntag><columntag nameattribute="ClientSurname"><new_value>Dijks</new_value><previous_value>Diskett</previous_value></columntag></r>');
declare
v_logrec varchar2(4000) := null;
v_recnum number := 0;
cursor c_logs is
select testlog from log_test;
cursor c_records is
select extractValue(x.column_value, '/columntag/#nameattribute') as column_name,
extractValue(x.column_value, '/columntag/new_value') as new_value,
extractValue(x.column_value, '/columntag/previous_value') as previous_value
from TABLE(XMLSequence(extract(xmltype.createxml(v_logrec), '//columntag'))) x
where extractValue(x.column_value, '/columntag/new_value') != extractValue(x.column_value, '/columntag/previous_value');
begin
for v_log in c_logs loop
v_logrec := v_log.testlog;
v_recnum := v_recnum + 1;
dbms_output.put_line(v_recnum);
for v_rec in c_records loop
SYS.dbms_output.put_line(v_rec.column_name || ' : *' || v_rec.new_value || '* : *' || v_rec.previous_value || '*');
end loop;
end loop;
end;
This would give you the below output (so Surname different in first record, nothing different in the second and both different in the third)...
1
ClientSurname : Dijk : Disk
2
3
ClientName : Jeffery : Jim
ClientSurname : Dijks : Diskett