Is there anyway to test the function in SQL? - sql

I have following problem. I've made an function which checks dates, warehouse if it's full, etc..
Here is the code and I've added comments so it is more clear and understanble:
SET SERVEROUTPUT ON
CREATE OR REPLACE
PROCEDURE Anlieferung (P_Artikelbezeichnung VARCHAR, P_Datum date, P_Stueck INT) AS
_inputQuantity int;
_currentQuantity int;
_inputItem int;
_articleNummber int;
_quanitityToInput int:=P_Stueck;
_maximumLnr int;
BEGIN
FOR v_rec IN (SELECT * FROM lager) LOOP -- implicit cursor
_currentQuantity:=0;
SELECT artikel.anr -- Adding our article nummber in our variable called "_articleNummber"
INTO _articleNummber
FROM artikel
WHERE artikel.bezeichnung = P_Artikelbezeichnung;
FOR buchung IN(SELECT * FROM lagerbuchung lag --Checking the lnr and the date
WHERE lag.lnr = v_rec.lnr AND P_Datum >= lag.datum)
LOOP
--Menge in _currentQuantity einfügen
_currentQuantity:=_currentQuantity + buchung.stueck; --Adding our current quantity in our variable called "_currentQuantity"
END LOOP;
IF(v_rec.stueckkap > _currentQuantity OR _quanitityToInput >0) --Checking our capacity
THEN
_inputItem:=v_rec.stueckkap - _currentQuantity;
IF(_inputItem <= _quanitityToInput)
THEN
_inputQuantity:= _inputItem;
_quanitityToInput:=_quanitityToInput - _inputItem;
ELSE
_inputQuantity:= _quanitityToInput;
_quanitityToInput:=0;
END IF;
SELECT COALESCE(MAX(lfndnr),0) -- If we have null in our table, we will also get null in return
INTO _maximumLnr
FROM lagerbuchung;
_maximumLnr:=_maximumLnr +1;
--Inserting our new values in our table
INSERT INTO lagerbuchung (lfndnr, datum, stueck, anr, lnr) VALUES (_maximumLnr, P_Datum, _inputQuantity, _articleNummber, v_rec.lnr);
END IF;
END LOOP;
END Anlieferung;
/
SELECT l.LNR, SUM(lb.STUECK) FROM LAGER l JOIN LAGERBUCHUNG lb on l.lnr = lb.lnr group by l.lnr order by l.lnr;
EXEC Anlieferung('Apfel', '27.11.2015', 160);
SELECT l.LNR, SUM(lb.STUECK) FROM LAGER l JOIN LAGERBUCHUNG lb on l.lnr = lb.lnr group by l.lnr order by l.lnr;
So basically if I run only the function I don't get any error. But when I run it with the test date I get following error:
Fehler beim Start in Zeile : 166 in Befehl -
EXEC Anlieferung('Apfel', '27.11.2015', 160)
Fehlerbericht -
ORA-06550: line 1, column 7:
PLS-00905: object IF4EBIHORACM.ANLIEFERUNG is invalid
ORA-06550: line 1, column 7:
PL/SQL: Statement ignored
06550. 00000 - "line %s, column %s:\n%s"
*Cause: Usually a PL/SQL compilation error.
*Action:
I've tried to debbug it and also input different test dates but still, same error.

After you compile the procedure either run:
SHOW ERRORS
or (assuming you are compiling it as the owning user and not as a DBA user; if you are not the owning user then use ALL_ERRORS):
SELECT * FROM USER_ERRORS;
Either will give you a list of the errors in the procedure and you can then debug it and fix the errors so the procedure works.
Update
From the comment:
2/1 PLS-00103: Encountered the symbol "_" when expecting one of the following: begin function pragma procedure subtype type <an identifier> <a double-quoted delimited-identifier> current cursor delete exists prior external language
Could it be because of my variables? They start with underscore
From the Database Object Names and Qualifiers documentation:
Database Object Naming Rules
Every database object has a name. In a SQL statement, you represent the name of an object with a quoted identifier or a nonquoted identifier.
A quoted identifier begins and ends with double quotation marks ("). If you name a schema object using a quoted identifier, then you must use the double quotation marks whenever you refer to that object.
A nonquoted identifier is not surrounded by any punctuation.
...
Nonquoted identifiers must begin with an alphabetic character from your database character set. Quoted identifiers can begin with any character.
You have many identifiers for variables that begins with an underscore. This tells you that the solution is to change the variable names to start with an alphabetic character (or to use a quoted identifier and surround the identifier with double quotes; but don't use quoted identifier, just use a letter as a prefix before the underscore):
CREATE OR REPLACE PROCEDURE Anlieferung (
P_Artikelbezeichnung VARCHAR,
P_Datum date,
P_Stueck INT
)
AS
v_inputQuantity int;
v_currentQuantity int;
v_inputItem int;
v_articleNummber int;
v_quanitityToInput int:=P_Stueck;
v_maximumLnr int;
BEGIN
...
Also, don't use:
SELECT COALESCE(MAX(lfndnr),0) -- If we have null in our table, we will also get null in return
INTO _maximumLnr
FROM lagerbuchung;
_maximumLnr:=_maximumLnr +1;
As it may generate duplicate values if the procedure is called twice at the same time.
Instead, create a sequence for the column and get the next value from the sequence.
CREATE SEQUENCE lagerbuchung__lfndnr__seq START WITH 213 INCREMENT BY 1;
(Start with whatever is 1 more than your current maximum value.)
Then in your procedure use:
INSERT INTO lagerbuchung (
lfndnr,
datum,
stueck,
anr,
ln
) VALUES (
lagerbuchung__lfndnr__seq.NEXTVAL,
P_Datum,
v_inputQuantity,
v_articleNummber,
v_rec.lnr
);

The message
object IF4EBIHORACM.ANLIEFERUNG is invalid
means there are errors in the ANLIEFERUNG routine. Check the source code and fix errors in there

Related

Syntax errors while trying to use a select statement as the condition for a while loop in SQL

I have a simple loop I wrote to check if a random id is already in the table, else regenerate it:
random_id := dbms_random.value(1,100);
WHILE (SELECT COUNT(*) FROM ANIMAL WHERE ANIMAL_ID = random_id) <> 0
LOOP
random_id := dbms_random.value(1,100);
END LOOP;
Running the code above gives the error:
Error(40,12): PLS-00103: Encountered the symbol "SELECT" when expecting one of the following: ( - + case mod new not null <an identifier> <a double-quoted delimited-identifier> <a bind variable> continue avg count current exists max min prior sql stddev sum variance execute forall merge time timestamp interval date <a string literal with character set specification> <a number> <a single-quoted SQL string> pipe <an alternatively-quoted string literal with character set specification> <an alternat
and
Error(40,67): PLS-00103: Encountered the symbol ")" when expecting one of the following: . ( * # % & - + ; / at for mod remainder rem <an exponent (**)> and or group having intersect minus order start union where connect || multiset
As far as I can tell, no error in the editor.
The better approach is to collect all the id's into a PL/SQL collection, so that you only make one context switch from PL/SQL to SQL (and back). Something like this; note that I use empno from table emp in schema scott for testing, since I don't have your table. The employee numbers are all between 7350 and 7950; I will pretend that I am looking for a number in that range that is not already assigned to an existing employee. (Similar to your attempt, if I got it right.)
The code begins by defining a local collection type, as a table of numbers (to hold the id's), and an instance of this local collection type. Then a single select using the bulk collect into clause collects the existing id's (employee numbers) into this collection instance, so the rest of the code is pure PL/SQL.
Also, please pay attention to what I told you in a comment: without rounding or truncating, dbms_random.value() produces non-integers in general, and that is very unlikely to be what you intended.
declare
type id_list_t is table of number;
id_list id_list_t;
random_id number;
begin
select empno bulk collect into id_list from scott.emp;
random_id := round(dbms_random.value(7350, 7950));
while random_id member of id_list loop
random_id := round(dbms_random.value(7350, 7950));
end loop;
dbms_output.put_line(random_id);
end;
/
The Boolean expression referred to in the documentation for WHILE loops cannot use an implicit cursor to retrieve and compare an anonymous value. This is true throughout PL/SQL syntax, not just for WHILE loops. For example, you cannot declare
tablesExist boolean := (select count(*) from user_tables) > 0;
A PL/SQL Boolean expression has to use PL/SQL variables, constants, literals, functions etc.
tablesExist boolean := tableCount > 0;
Implicit cursors can only be used in cursor FOR loops and select into constructions.

Function - oracle (PLS-00103: Encountered the symbol "")

I'm trying to create a simple oracle function which loops over some records and then inserts records for each of those ..
CREATE OR REPLACE FUNCTION addNewRolesToAllGDP
return NUMBER
is dummy number;
BEGIN
FOR applicationId IN (SELECT APPID
FROM GRPAPPLICATIONINSTANCES
where GRPAPPID = (select GRPAPPID
from GRPAPPLICATIONS
where GRPNAME = 'DIGITAL_OFFICE')
AND APPID in (select APPID from APPLICATIONS where REGEXP_LIKE(APPNAME, '[[:digit:]]')))
LOOP
INSERT INTO ROLES (ROLID, ROLNAME, APPID)
VALUES (SEQROLES.nextval,
'INVENTORY_REQUESTER',
applicationId);
INSERT INTO ROLES (ROLID, ROLNAME, APPID)
VALUES (SEQROLES.nextval,
'INVENTORY_OWNER',
applicationId);
INSERT INTO ROLES (ROLID, ROLNAME, APPID)
VALUES (SEQROLES.nextval,
'INVENTORY_ADMIN',
applicationId);
END LOOP;
RETURN 1;
END;
alter function addNewRolesToAllGDP compile;
This statements gives me the following in USER_ERRORS:
PLS-00103: Encountered the symbol "" when expecting one of the following: ( return compress compiled wrapped
I'm not sure if this is the problem, but I notice that you do not have a slash following the END statement of the function. In Oracle tools, without that slash, the statement does not actually get terminated, and the ALTER command is included as part of the same statement. This can cause weird syntax errors.
If this is the problem, fundamentally this question is a duplicate of oracle SQL plus how to end command in SQL file?.
Edited to add As others have said in comments, the same code seems to compile fine for me when I cut-and-paste it from your post. Based on the error and the position where it is reported, my best guess is that at the end of the first line your original source has some invisible character that is causing the parser error.
Used to run this in Intellij which gave me the stated behaviour. After running it from sqlDeveloper it compiled fine.
I got a similar error message for a missing comma in the procedure definition and got compiled after adding the comma.
Procedure definition before fix
PROCEDURE CREATE_WO(p_wo_type IN NUMBER,
p_id IN NUMBER,
p_do_commit IN VARCHAR2 DEFAULT 'Y'
p_return_value OUT VARCHAR2)
IS
BEGIN
...
END;
Error Message
Cause: java.sql.SQLException: ORA-06550: line 8, column 9:
PLS-00103: Encountered the symbol "" when expecting one of the following:
) , * & = - + < / > at in is mod remainder not rem =>
<an exponent (**)> <> or != or ~= >= <= <> and or like like2
like4 likec between || multiset member submultiset
Procedure definition after fix, note the comma at the end of p_do_commit
PROCEDURE CREATE_WO(p_wo_type IN NUMBER,
p_id IN NUMBER,
p_do_commit IN VARCHAR2 DEFAULT 'Y',
p_return_value OUT VARCHAR2)
IS
BEGIN
...
END;

Using type "is table of" in a select statement

I am trying to create a select statement to select the fields that were select in a previous select statement, as per below code (using oracle 11g):
Declare
type NumberArray IS TABLE OF Number;
v_medium NumberArray;
v_medium_final NumberArray := NumberArray();
v_count number;
type VarcharArray IS TABLE OF VARCHAR2(60);
v_Type VarcharArray
--Select all Regions and Locality affected by the fault
cursor Locality_cur is
Select LocalityCode from AvaFaultLocality where FAULTID=36841 and SITEID=1;
Locality_rec Locality_cur%ROWTYPE;
cursor Region_cur is
Select RegionCode from avafaultregion where FAULTID=36841 and SITEID=1;
Region_rec Region_cur%ROWTYPE;
Begin
v_count := 1;
FOR Region_rec IN Region_cur
LOOP
dbms_output.put_line(Region_rec.RegionCode);
Select RECIPIENTMEDIUMID BULK COLLECT into v_medium from AvaRecipientAreaRegion where RegionCode = Region_rec.RegionCode;
FOR idx IN 1..v_medium.COUNT LOOP
v_medium_final.extend();
v_medium_final(v_count) := v_medium(idx);
dbms_output.put_line(TO_CHAR(v_medium(idx)));
v_count:= v_count + 1;
END LOOP;
dbms_output.put_line('End Loop for Region '||Region_rec.RegionCode);
END LOOP;
dbms_output.put_line(TO_CHAR(v_medium_final.count));
Select MEDIUMTYPECODE BULK COLLECT into v_Type from AvaRecipientMedium where RECIPIENTMEDIUMID in (SELECT * FROM TABLE(v_medium_final)); <--- Line with problem
End;
Mainly what I am trying to do is to select the content of all regions, loop this result and get the content of all locality in those regions, and then get all the RecipientMediumID for each locality and add the result in a variable called v_medium_final. This part is working perfectly.
The problem is when I try to use the result of this selection to select the values in a RecipientMedium table where all the values is in the variable v_medium_final.
I try use:
Select MEDIUMTYPECODE BULK COLLECT into v_Type from AvaRecipientMedium where RECIPIENTMEDIUMID in (SELECT * FROM TABLE(v_medium_final));
or
Select MEDIUMTYPECODE BULK COLLECT into v_Type from AvaRecipientMedium where RECIPIENTMEDIUMID in v_medium_final;
But I always get the errors:
[Error] Execution (32: 126): ORA-06550: line 32, column 126:
PLS-00642: local collection types not allowed in SQL statements
ORA-06550: line 32, column 120:
PL/SQL: ORA-22905: cannot access rows from a non-nested table item
ORA-06550: line 32, column 7:
PL/SQL: SQL Statement ignored
and for the second case:
[Error] Execution (32: 106): ORA-06550: line 32, column 106:
PLS-00642: local collection types not allowed in SQL statements
As the error indicates, you can't use a local collection type. You'd need to use a collection type defined at the SQL level.
CREATE TYPE NumberArray
AS TABLE OF NUMBER;
DECLARE
v_medium NumberArray;
...
This would require that you have the CREATE TYPE privilege and it would mean that your type definition would be visible to anyone with privileges to see it rather than having the scope just of your PL/SQL block.
Personally, I would avoid using Array in the name of a collection type that wasn't a VARRAY simply because the name could be confusing. If I see a type NumberArray, I would guess that it was defined as a VARRAY(n) OF NUMBER rather than a nested table type. Using a ntt suffix for a nested table type would seem more sensible (create type number_ntt as table of number).

I am executing following procedure in oracle but get compilation error

Actually I want to get usernames from table using Stored Procedure
CREATE OR REPLACE PROCEDURE GetRecord
(
p_ID IN integer ,
p_user OUT VARCHAR2
)
AS
BEGIN
SELECT
USERNAMES
INTO
p_user
FROM tblUsers
WHERE ID = p_ID ;
END GetRecord;
BEGIN
DECLARE A VARCHAR2
EXECUTE GetRecord(21,A);
END
When I run the above procedure I got following errors
Error starting at line : 17 in command -
BEGIN
DECLARE A VARCHAR2
EXECUTE GetRecord(21,A);
END
Error report -
ORA-06550: line 3, column 1:
PLS-00103: Encountered the symbol "EXECUTE" when expecting one of the following:
:= . ( # % ; not null range default character
The symbol ";" was substituted for "EXECUTE" to continue.
ORA-06550: line 4, column 1:
PLS-00103: Encountered the symbol "END" when expecting one of the following:
begin function pragma procedure subtype type <an identifier>
<a double-quoted delimited-identifier> current cursor delete
exists prior
06550. 00000 - "line %s, column %s:\n%s"
*Cause: Usually a PL/SQL compilation error.
*Action:
Actually I want to get usernames from table using Stored Procedure
The code that you are using to execute your procedure appears to be incorrect. It sounds like you want
DECLARE
l_usernames tblUsers.usernames%type;
BEGIN
GetRecord( 21, l_usernames );
END;
In general, I'd strongly object to your naming convention. It makes no sense to use a plural usernames as a column name. A procedure named GetRecord tells you nothing about what it does-- GetUsername would be much more meaningful. Your anonymous block also appears to be declaring a local variable a that stores the returned username which also makes no sense from the standpoint of using meaningful identifiers.
I'd also suggest that a procedure is the wrong approach here. If your goal is to return a single value, use a function. Functions return things, procedures do not. A function can potentially be used in a SQL statement, a procedure cannot be.

Can not have CREATE TABLE inside if else

When I run this query
DECLARE
num NUMBER;
BEGIN
SELECT COUNT(*) INTO num FROM user_all_tables WHERE TABLE_NAME=upper('DatabaseScriptLog')
;
IF num < 1 THEN
CREATE TABLE DatabaseScriptLog
(ScriptIdentifier VARCHAR(100) NOT NULL,
ScriptType VARCHAR(50),
StartDate TIMESTAMP,
EndDate TIMESTAMP,
PRIMARY KEY (ScriptIdentifier)
);
END IF;
END;
When execute the above, I got the following:
PLS-00103: Encountered the symbol
"CREATE" when expecting one of the
following:
begin case declare exit for goto if
loop mod null pragma raise return
select update while with << close current delete
fetch lock insert open rollback
savepoint set sql execute commit
forall merge pipe
06550. 00000 - "line %s, column %s:\n%s"
*Cause: Usually a PL/SQL compilation error.
You cannot run DDL statements like that. You need to use dynamic SQL (EXECUTE IMMEDIATE).
IF num < 1 THEN
EXECUTE IMMEDIATE 'CREATE TABLE DatabaseScriptLog (ScriptIdentifier VARCHAR(100) NOT NULL, ScriptType VARCHAR(50), StartDate TIMESTAMP, EndDate TIMESTAMP, PRIMARY KEY (ScriptIdentifier))'
END IF;
You cannot do this like you could in SQLServer. You need to execute the create code through a stored procedure that is already in the proper schema. You pass the create code as a parameter and the stored procedure that has the correct privileges does it for you.
I use a version script that updates the schema to the latest by running schema altering operations separated by if-then clauses to check what version the db is at. After altering it increments the version so that the next if statements test passes and so on. If you are up to date and run the script the ifs skip all altering code. If your db is at version 46 and you run the script which has all changes up to 50, you execute only the blocks that represent versions 47-50.
You could execute immediate but would need elevated privileges which I would not recommend.
Hope this helps.