Oracle PL/SQL SUBSTR error - plsql

i need to convert a varchar to an array using pl/sql, but when i use the SUBSTR function, i get this error:
Error report -
ORA-06550: line 12, column 3:
PLS-00330: invalid use of type name or subtype name
ORA-06550: line 12, column 3:
PL/SQL: Statement ignored
06550. 00000 - "line %s, column %s:\n%s"
*Cause: Usually a PL/SQL compilation error.
this is my code:
SET SERVEROUTPUT ON;
DECLARE
v_string varchar2(20) := 'hello';
type array_string is varray(5) of varchar2(10);
v_length number;
cnt number;
v_char char(1);
BEGIN
v_length := length(v_string);
while (cnt < v_length)
loop
v_char := SUBSTR(v_string, cnt, 1);
array_string(cnt) := v_char;
cnt := cnt + 1;
end loop;
END;
when i (partially) type 'SUBSTR' it autocompletes to SUBSTR(SQLERRM, 1, 64) so it should know the command, right?
what am i doing wrong? im pretty new at pl/sql
regards,

You must initialize variable cnt before using it in substr.
You must use variable name instead of type name in array_string(cnt).
You must extend your varray before writing new value to it.

In oracle environment, the starting index for varrays is always 1.
Your cnt is starting from 0. So the during the first time execution of the loop, the
a_string(cnt) := v_char;
cnt is 0.
Initialize cnt as 1, and run the loop for
while (cnt < 5)
This will remove the error you are getting

Related

PL/SQL Calling a function to a procedure

Previously,I asked about creating a function that returns total amount of salaries from a parameter city.
I chose Dallas for instance.Tables are EMP and DEPT:
You can find them below
https://livesql.oracle.com/apex/livesql/file/content_O5AEB2HE08PYEPTGCFLZU9YCV.html
Mistakenly I wrote in my language instead of english at the end of it but I corrected it.
Now I need to create a procedure which lists ascending the employees and their salaries from another parameter city.After listing them I need to call the function for the second city.Problem is I get an error for the 'loop' from the procedure and i got no idea why.
set serveroutput on;
create or replace function show_sal (local dept.loc%type) return number as
vval number;
begin
select sum(emp.sal)
into vval
from emp inner join dept on dept.deptno = emp.deptno
where upper(dept.loc) = upper(local);
return vval;
end;
/
begin
dbms_output.put_line('Total sum of salaries from DALLAS = ' || show_sal('DALLAS'));
end;
/
-------------------------------------------------------------------------------------------------
CREATE OR REPLACE PROCEDURE show_employees(v_loc dept.loc%TYPE) AS
CURSOR c IS
SELECT ename,sal
FROM emp INNER JOIN dept ON emp.deptno = dept.deptno
WHERE UPPER(loc)=UPPER(v_loc)
ORDER BY ename ASC;
v_name VARCHAR2;
v_salaries emp.sal%TYPE;
BEGIN
OPEN c
LOOP
FETCH c INTO v_name,v_salaries;
EXIT WHEN c%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(v_name||' '|| v_salaries);
END LOOP;
CLOSE c;
END;
/
EXECUTE show_employees('CHICAGO');
BEGIN
show_sal('CHICAGO');
END;
/
You simply forgot the semicolon after OPEN c.
And here is how to do the same with an implicit cursor, which is much easier to deal with in my opinion:
CREATE OR REPLACE PROCEDURE show_employees(v_loc dept.loc%TYPE) AS
BEGIN
FOR rec IN
(
SELECT ename, sal
FROM emp
WHERE deptno IN (SELECT deptno FROM dept WHERE UPPER(loc) = UPPER(v_loc))
ORDER BY ename
) LOOP
DBMS_OUTPUT.PUT_LINE(rec.ename || ' ' || rec.sal);
END LOOP;
END show_employees;
Errors i get:
Function SHOW_SAL compiled
Total sum of salaries from DALLAS = 10875
PL/SQL procedure successfully completed.
Procedure SHOW_EMPLOYEES compiled
LINE/COL ERROR
9/9 PLS-00215: String length constraints must be in range (1 .. 32767)
Errors: check compiler log
Error starting at line : 40 in command -
BEGIN show_employees('CHICAGO'); END;
Error report -
ORA-06550: line 1, column 61:
PLS-00905: object HR.SHOW_EMPLOYEES is invalid
ORA-06550: line 1, column 61:
PL/SQL: Statement ignored
06550. 00000 - "line %s, column %s:\n%s"
*Cause: Usually a PL/SQL compilation error.
*Action:
Error starting at line : 42 in command -
BEGIN
show_sal('CHICAGO');
END;
Error report -
ORA-06550: line 2, column 1:
PLS-00221: 'SHOW_SAL' is not a procedure or is undefined
ORA-06550: line 2, column 1:
PL/SQL: Statement ignored
06550. 00000 - "line %s, column %s:\n%s"
*Cause: Usually a PL/SQL compilation error.
*Action:
set serveroutput on;
create or replace function show_sal (local dept.loc%type) return number as
vval number;
begin
select sum(emp.sal)
into vval
from emp inner join dept on dept.deptno = emp.deptno
where upper(dept.loc) = upper(local);
return vval;
end;
/
begin
dbms_output.put_line('Total sum of salaries from DALLAS = ' || show_sal('DALLAS'));
end;
/
-------------------------------------------------------------------------------------------------
CREATE OR REPLACE PROCEDURE show_employees(v_loc dept.loc%TYPE) AS
CURSOR C is SELECT ename,sal
FROM emp,dept
WHERE emp.deptno=dept.deptno AND UPPER(loc)=UPPER(v_loc)
ORDER BY ename ASC;
v_nume emp.ename%TYPE;
v_salariu emp.sal%TYPE;
v_tot emp.sal%TYPE;
BEGIN
v_tot:=show_sal('CHICAGO');
dbms_output.put_line('nume salar total: '||v_tot);
OPEN C;
LOOP
FETCH C INTO v_nume,v_salariu;
EXIT WHEN C%NOTFOUND;
dbms_output.put_line(v_nume||' '||v_salariu);
END LOOP;
CLOSE C;
END;
/
EXECUTE show_employees('CHICAGO');

using nested table as in Parameter to a Procedure

I have a table T1 and I want to insert multiple rows at a time through a procedure using collection. I have written the code but when I'm trying to execute it throws an error. Please advise.
create table t1 ( id number , name varchar2(10));
/
create or replace PACKAGE PKG1 AS
TYPE TAB_LIST IS TABLE OF T1%ROWTYPE;
PROCEDURE PROC1 (p_val IN TAB_LIST);
END PKG1;
/
create or replace PACKAGE BODY PKG1 AS
PROCEDURE PROC1 (P_VAL IN TAB_LIST
)
IS
BEGIN
FOR i IN p_val.FIRST..p_val.LAST
LOOP
insert INTO T1
(
id, name
)
VALUES
(
p_val(i).id,
p_val(i).name
);
END LOOP;
END;
END;
error after executing
DECLARE
p_val PKG1.TAB_LIST;
BEGIN
p_val := PKG1.TAB_LIST(123,'XYZ');
END;
Error report -
ORA-06550: line 5, column 11:
PLS-00306: wrong number or types of arguments in call to 'TAB_LIST'
ORA-06550: line 5, column 11:
PLS-00306: wrong number or types of arguments in call to 'TAB_LIST'
ORA-06550: line 5, column 2:
PL/SQL: Statement ignored
06550. 00000 - "line %s, column %s:\n%s"
*Cause: Usually a PL/SQL compilation error.
*Action:
DECLARE
P_VAL PKG1.TAB_LIST := PKG1.TAB_LIST();
BEGIN
P_VAL.extend;
P_VAL(1).id := 123;
P_VAL(1).name := 'XYZ';
PKG1.PROC1( P_VAL );
END;
example for multiple records:
DECLARE
P_VAL PKG1.TAB_LIST := PKG1.TAB_LIST();
BEGIN
for i in 1 .. 10
loop
P_VAL.extend;
P_VAL(P_VAL.LAST).id := i;
P_VAL(P_VAL.LAST).name := 'XYZ' || i;
end loop;
PKG1.PROC1( P_VAL );
END;
#hekko": its not a string .sorry about formatting . The multiple values (n) can be passed from application like this and all should be inserted into table at once.
123 'XYZ'
456 'DFK'
866 'HKK'
#Kaushik :it is not a string but formatting issue.. the question remains the same "I have a table T1 and I want to insert multiple rows at a time through a procedure using collection.

ORA-06502: PL/SQL: numeric or value error: NULL index table key value ORA-06512: at "OJC.JC_MASTER", line 129

I'm trying to run an interface in ODI 11g. When I call the procedure I get this error :
ODI-1228: Task START_JC (Procedure) fails on the target ORACLE connection OJC.
Caused By: java.sql.SQLException: ORA-06502: PL/SQL: numeric or value error: NULL index table key value
ORA-06512: at "OJC.JC_MASTER", line 129
ORA-06512: at "OJC.JC_MASTER", line 689
ORA-06512: at line 9
the sql code
PROCEDURE string_to_aa_parameter_type (
p_string VARCHAR2,
p_out_aa_parameter_values IN OUT aa_parameter_type
)
AS
v_start INTEGER := 1;
v_pos INTEGER := 0;
v_counter INTEGER := 0;
v_temp_parameter_name VARCHAR2 (4000);
v_temp_parameter_value VARCHAR2 (4000);
BEGIN
IF p_string IS NULL
THEN
RETURN;
END IF;
-- determine first chuck of string
v_pos := INSTR (p_string, '=', v_start);
-- while there are chunks left, loop
WHILE (v_pos != 0)
LOOP
v_counter := v_counter + 1;
-- create array
IF MOD (v_counter, 2) = 1
THEN
v_temp_parameter_name :=
SUBSTR (p_string, v_start, v_pos - v_start);
v_start := v_pos + 1;
v_pos := INSTR (p_string, ';', v_start);
ELSE
v_temp_parameter_value :=
SUBSTR (p_string, v_start, v_pos - v_start);
p_out_aa_parameter_values (trim(v_temp_parameter_name)) :=
trim(v_temp_parameter_value);
v_start := v_pos + 1;
v_pos := INSTR (p_string, '=', v_start);
END IF;
END LOOP;
-- IN THE FOLLOWING LINE I GET THE ERROR
v_temp_parameter_value := SUBSTR (p_string, v_start);
p_out_aa_parameter_values (trim(v_temp_parameter_name)) :=
trim(v_temp_parameter_value);
END;
Can someone help me in figuring out that the problem is ?
You'll get that error if p_string is a not-null value which doesn't contain an equals sign at all, or with any semicolon-delimited part that starts with an equals sign. It's thrown by the line after the one you indicated (or the equivalent line inside the loop, if p_string has a final semicolon).
If there is no equals sign at all then
v_pos := INSTR (p_string, '=', v_start);
gives zero, which means you don't go through the loop at all; which means when you get to that final assignment v_temp_parameter_name has never been set.
If there is a key/value pair with no key, say p_string is 'x=y;=z' you do go into the loop, and with that example the first key/value pair is added to the array; but then v_start and v_pos end up as the same value (5 in this case, both pointing to the second =). The the next time round the loop:
v_temp_parameter_name :=
SUBSTR (p_string, v_start, v_pos - v_start);
evaluates to SUBSTR(p_string, 5, 0) (where the third argument is zero because those two variables are the same), which is always going to be an empty string, or null.
There is no actual error yet, so it evaluates v_pos again, and either gets zero or non-zero, depending on whether there is a terminating semicolon.
If it's non-zero then it goes round the loop again; if it's zero it drops out. Either way it has a last stab at getting the matching value - it doesn't matter if that is set to anything or not. When it tries to add the element to the array, though, the name is still null, and you get that error, from whichever of the two array assignments it hits.
You could do additional testing and handling inside the procedure to spot and discard null keys, but
i didn't write the procedure,i have to run it . It is supposed to be syntax correct
So you need to figure out why the Java code is passing a value which the procedure can't handle - i.e. why it is sending incomplete key/value pairs.

How to optimize the substring & instring or substitute the functions for debugging

I wrote the stored procedure below for the purposes of reading a comma delimited file that is moved onto the server ("DIR" folder), and when script is executed, it essentially parses the file (.csv), and assigns the data to its respective variables (xJOB_ID, xCTRL_ID, xACCT_SEC, xCREATEDON_DATE) such that I can insert the data into a table. I am using Oracle SQL Developer version 4.0.0.13 in a Windows 7 environment. Fortunately, after banging my head on the table a couple of times the code works and I have not had any issues running the script.
Example format of the file:
1111, 2, T, 10/10/2000
2222, 12345, U, 10/10/2001
5555, 123, S, 10/10/1999
MY QUESTION:
I found a little difficulty using the SUBSTRING & INSTRING functions to parse the data and wanted to know how can I improve the script so that in the event some debugging was needed, it can be easily resolved for someone that did not write the stored procedure. Please let me know if that makes sense. I gave you the entire script so that you can understand what I was trying to accomplish and so that I can improve the code for debugging purposes.
create or replace PROCEDURE SP_INSERT_INTO_TABLE(xFILE_NAME IN VARCHAR2)
IS
--UTL_FILE is an oracle package that allows you to read and write operating system files.
TEXT_DATA UTL_FILE.FILE_TYPE;
v_ROW_LENGTH NUMBER := 1024;
v_TEXTSTRING VARCHAR2(4000);
cLINE VARCHAR2(100);
xJOB_ID NUMBER;
xCTRL_ID NUMBER;
xACCT_SEC VARCHAR2(1);
xCREATEDON_DATE DATE;
xCOUNT NUMBER := 0;
BEGIN
BEGIN
--Streams in the file data and assigns it to TEXT_DATA variable.
TEXT_DATA := UTL_FILE.FOPEN('DIR', xFILE_NAME, 'R', v_ROW_LENGTH);
END;
--Begin LOOP to get each line and assign to cLINE to extract, assign to each variable, and insert into the table
LOOP
BEGIN
--Gets each string/line up to the line terminator
UTL_FILE.GET_LINE(TEXT_DATA, v_TEXTSTRING);
EXCEPTION
WHEN NO_DATA_FOUND THEN
EXIT;
END;
--Each line is assigned to the variable cLINE.
cLINE := v_TEXTSTRING;
--Begin to parse data using SUBSTRING and INSTRING functions
BEGIN
--Extracts string from cLINE position 1 up to the first occurrence, converts it to a number, and assigns it to the variable.
xJOB_ID := TO_NUMBER(SUBSTR(cLINE, 1,INSTR(cLINE, ',', 1, 1)-1));
--Extracts string from cLINE between the 1st and 2nd occurrence, converts it to a number, and assigns it to the variable.
xCTRL_ID := TO_NUMBER(SUBSTR(cLINE, INSTR(cLINE, ',', 1, 1)+1, INSTR(cLINE, ',', 1,2)-INSTR(cLINE, ',', 1,1)-1));
--Extracts string from cLINE between the 2nd and 3rd occurrence and assigns it to the variable.
xACCT_SEC := SUBSTR(cLINE, INSTR(cLINE, ',', 1, 2) +1, INSTR(cLINE, ',', 1,3)-INSTR(cLINE, ',', 1,2) -1);
--Extracts string from cLINE after the last occurrence, converts it to a date, and assigns it the variable.
xCREATEDON_DATE := TO_DATE(SUBSTR(cLINE, INSTR(cLINE, ',', 1, 3)+1), 'MM/DD/YYYY');
INSERT INTO TABLE(JOB_ID, CTRL_ID, ACCT_SEC, CREATEDON_DATE)
VALUES(xJOB_ID, xCTRL_ID, xACCT_SEC, xCREATEDON_DATE);
COMMIT;
--Counter to count the amount of inserts
xCOUNT := xCOUNT + 1;
EXCEPTION
--Exception to handle the conversion of a string to a NUMBER or value is longer than the declared length of the variable.
WHEN VALUE_ERROR THEN
NULL;
END;
END LOOP;
DBMS_OUTPUT.PUT_LINE('RECORDS INSERTED: ' || xCOUNT);
UTL_FILE.FCLOSE(TEXT_DATA);
END;
You could use REGEXP_SUBSTR instead, since that just requires one function call, rather than a series of SUBSTR and INSTR calls, eg:
(courtesy of Gary_W):
declare
v_str varchar2(20) := 'a,,bcd';
v_substr1 varchar2(10);
v_substr2 varchar2(10);
v_substr3 varchar2(10);
begin
v_substr1 := regexp_substr(v_str, '([^,]*)(,|$)', 1, 1, NULL, 1);
v_substr2 := regexp_substr(v_str, '([^,]*)(,|$)', 1, 2, NULL, 1);
v_substr3 := regexp_substr(v_str, '([^,]*)(,|$)', 1, 3, NULL, 1);
dbms_output.put_line(v_substr1||':'||v_substr2||':'||v_substr3);
end;
/
a::bcd
The above will work with strings that have null portions, as demonstrated. The search pattern has been split into two groups (aka subexpressions): [^,]* and ,|$.
The first group says: any character that isn't a comma ([^,]) that appears 0 or more times (*).
The second group says: comma or the end of the line.
So the whole pattern is looking for a set of any characters excluding the comma that may or may not exist which is followed by either a comma or the end of the line.
The last parameter in the regexp_substr is indicating that we want to pick the 1st subexpression from the search pattern to display - if we didn't include this, then you'd end up with the commas being displayed as part of the string being returned.
If you're absolutely sure that none of the elements of the string will ever be null, then the following will work:
declare
v_str varchar2(20) := 'a,123,bcd';
v_substr1 varchar2(10);
v_substr2 varchar2(10);
v_substr3 varchar2(10);
begin
v_substr1 := regexp_substr(v_str, '[^,]+', 1, 1);
v_substr2 := regexp_substr(v_str, '[^,]+', 1, 2);
v_substr3 := regexp_substr(v_str, '[^,]+', 1, 3);
dbms_output.put_line(v_substr1||':'||v_substr2||':'||v_substr3);
end;
/
a:123:bcd
This is just looking for the specified occurrence of a string of characters that aren't a comma, which is slighly easier to grok (imho!) than the search pattern used in the previous example, but is a lot less robust.

PL/SQL update based on flag

I have a table "flags" containing flags with "name" and "value" where "name" is the name of the flag and "value" is either 'T' or 'F'
I would like to update a table based on a flag with name 'enable_feature' in the following manner:
BEGIN;
IF ((SELECT flags.value FROM flags WHERE flags.name = 'enable_feature') = 'T')
UPDATE... SET...;
ELSE
UPDATE... SET...;
END IF;
END;
My trouble seems to be in the IF statement. Specifically, i get the following error:
PLS-00103: Encountered the symbol "SELECT" when expecting one of the
following:
( - + case mod new not null continue avg count current
exists max min prior sql stddev sum variance execute forall merge
time timestamp interval date pipe
How do I modify this statement such that I can perform an UPDATE/SET statement based on the value of 'enable_feature' (The UPDATE/SET statement is on a different table)
DECLARE
v flags.value%type;
BEGIN
SELECT flags.value into v FROM flags WHERE flags.name = 'enable_feature';
IF v = 'T' THEN
UPDATE... SET...;
ELSE
UPDATE... SET...;
END IF;
END;

Resources