I have a problem with some P-SQL syntax. I have reduced the code sample to its minimum below.
The following works:
CREATE OR REPLACE FUNCTION MyFunction(LINE_ID SMALLINT)
RETURN VARCHAR2 IS
tmp VARCHAR2(4000);
BEGIN
tmp := CAST(LINE_ID AS VARCHAR2);
RETURN(tmp);
END MyFunction;
/
However, I need to change the LINE_ID parameter to NUMBER(5, 0), after which the following does not work:
CREATE OR REPLACE FUNCTION MyFunction2(LINE_ID NUMBER(5, 0))
RETURN VARCHAR2 IS
tmp VARCHAR2(4000);
BEGIN
tmp := CAST(LINE_ID AS VARCHAR2);
RETURN(tmp);
END MyFunction2;
/
The error message in Oracle SQL Developer 3.2.10.09 is
Error(1,36): PLS-00103: Encountered the symbol "(" when expecting one
of the following: := . ) , # % default character The symbol ":="
was substituted for "(" to continue.
How should I write the CAST statement in order to make it work with NUMBER(5, 0) instead of SMALLINT?
Again, this is not the original code but I am looking for a solution that does not deviate too much from the second version and preferably not another function call either. The VARCHAR2 return type is important as well.
The function you're looking for is TO_CHAR:
tmp := TO_CHAR(LINE_ID);
You can't specify NUMBER precision and scale for a function's parameter. Just declare it like this:
CREATE OR REPLACE FUNCTION MyFunction2(LINE_ID NUMBER)
The issue is not in your CAST, but rather in your parameter definition. From the documentation:
You can declare a formal parameter of a constrained subtype, like this:
DECLARE
SUBTYPE n1 IS NUMBER(1);
SUBTYPE v1 IS VARCHAR2(1);
PROCEDURE p (n n1, v v1) IS ...
But you cannot include a constraint in a formal parameter declaration, like this:
DECLARE
PROCEDURE p (n NUMBER(1), v VARCHAR2(1)) IS ...
Related
This is the code:
CREATE PROCEDURE print_string(IN input_string VARCHAR(255))
BEGIN
DECLARE num_chars INT DEFAULT 0;
IF input_string IS NULL THEN
SET num_chars = 0;
ELSE
SET num_chars = CHAR_LENGTH(input_string);
END IF;
SELECT UPPER(input_string), num_chars;
END;
I get error:
PLS-00103: Encountered the symbol "IN" when expecting one of the following: <an identifier> <a double-quoted delimited-identifier>
current delete exists prior
Errors: check compiler log
How do I fix: current delete exists prior?
The immediate error is that you have the argument name and mode the wrong way around - it should be (input_string IN ... not (IN input_string .... But there are other problems:
Oracle recommends VARCHAR2 over VARCHAR.
arguments just have the data type, not a size (or precision/scale), so it should be (input_string IN VARCHAR2) not (input_string IN VARCHAR2(255).
you are missing the IS/AS keyword.
DECLARE comes before BEGIN in a PL/SQL block; having a nested block here would be valid, but you're missing a BEGIN and END; if you do that, and it isn't necessary so I don't think it's what you meant. And you don't need the DECLARE at all for a procedure, it's implied.
if you want a default value for a PL/SQL variable then assign it, rather than using DEFAULT. (You don't really need to do this here, as you always assign a value later anyway, but I'm sticking with your general approach.)
it's probably better to use native Oracle types, so NUMBER or PLS_INTEGER instead of INT.
assignment of values is with :=, not SET ... = ....
CHAR_LENGTH should just be LENGTH (unless you have your own function with that name).
in PL/SQL you have to select into something, and from something. But if you do that here, you still have to return it to the caller somehow.
given that you want to 'print' the string, you probably want dbms_output - though that relies on the client showing the result, which most don't by default, and it's generally only used for debugging...
So this would work:
CREATE PROCEDURE print_string(input_string IN VARCHAR2) AS
num_chars PLS_INTEGER := 0;
BEGIN
IF input_string IS NULL THEN
num_chars := 0;
ELSE
num_chars := LENGTH(input_string);
END IF;
DBMS_OUTPUT.PUT_LINE(UPPER(input_string) || ': ' || num_chars);
END;
/
BEGIN
DBMS_OUTPUT.ENABLE;
print_string('This is a test');
END;
/
1 rows affected
dbms_output:
THIS IS A TEST: 14
fiddle
But again, dbms_output isn't ideal. And it could be done much more simply (#Mto has shown one way), or without using PL/SQL at all.
You can fix the issues (listing in #Alex Poole's answer) and simplify the procedure to:
CREATE PROCEDURE print_string(
input_string IN VARCHAR2
)
IS
BEGIN
DBMS_OUTPUT.PUT_LINE(UPPER(input_string) || ', ' || COALESCE(LENGTH(input_string), 0));
END;
/
Then:
BEGIN
DBMS_OUTPUT.ENABLE;
print_string('This is a test');
print_string(NULL);
END;
/
Outputs:
THIS IS A TEST, 14
, 0
fiddle
The code syntax is incorrect here. It should be something like
CREATE OR REPLACE PROCEDURE print_string(input_string IN VARCHAR2)
IS
BEGIN
I want to declare amount parameter as type NUMBER(9,2); How come this Oracle PL/SQL procedure doesn't work?
CREATE OR REPLACE PROCEDURE spupdate_price_by_cat(amount IN NUMBER(9, 2))AS
BEGIN
END;
/
When I run it, i get error:
Encountered the symbol "(" when expecting one of the following: := . ) , # % default character. The symbol ":=" was substituted for "(" to continue.
You cannot declare a NUMBER data type with scale and precision in the signature of a PL/SQL function or procedure.
Either use NUMBER:
CREATE OR REPLACE PROCEDURE spupdate_price_by_cat(
amount IN NUMBER
)
AS
BEGIN
NULL;
END;
/
Or use a %TYPE declaration to declare it to have the same data type as a column of a table (but it still does not respect the column's scale and precision in the signature):
CREATE OR REPLACE PROCEDURE spupdate_price_by_cat(
amount IN table_name.column_name%TYPE
)
AS
BEGIN
NULL;
END;
/
If you want a particular scale and precision enforcing in a PL/SQL variable then declare it locally in the procedure:
CREATE OR REPLACE PROCEDURE spupdate_price_by_cat(
amount IN table_name.column_name%TYPE
)
AS
v_amount1 NUMBER(9,2) := amount;
v_amount2 table_name.column_name%TYPE := amount;
BEGIN
DBMS_OUTPUT.PUT_LINE(v_amount1);
END;
/
db<>fiddle here
I am using oracle SQL developper.
I am trying to create a function that will accept two parameters (first and last name) and return them as one variable, with the last name showing up first. Here is my function.
CREATE OR REPLACE FUNCTION LASTNAMEFIRST
(
varFirstName IN VARCHAR2,
varLastName IN VARCHAR2
)
RETURN VARCHAR2 AS
BEGIN
DECLARE varFullName VARCHAR2;
DEFINE varFullName := CONCAT(varLastName,' ' ,varFirstName);
RETURN varFullName;
END LASTNAMEFIRST;
I am receiving an error on the semicolon at 'end lastnamefirst' "syntax error"
I keep trying to change small things and that same error just shows up in different places whenever I change things. What am I doing wrong?
Wrong syntax. Should be
SQL> create or replace function lastnamefirst
2 (varfirstname in varchar2,
3 varlastname in varchar2)
4 return varchar2
5 as
6 begin
7 return varlastname||' '||varfirstname;
8 end;
9 /
Function created.
SQL> select lastnamefirst('Little', 'Foot') result from dual;
RESULT
------------------------------
Foot Little
SQL>
What's wrong with your code?
you don't DECLARE within the body; if you do, there's no DECLARE keyword at all, and datatype requires length (such as VARCHAR2(30))
CONCAT accepts only two arguments; use a concatenation operator, double pipe || instead
there's no DEFINE in PL/SQL
I would expect the Oracle syntax to look more like:
CREATE OR REPLACE FUNCTION LASTNAMEFIRST (
in_FirstName IN VARCHAR2,
in_LastName IN VARCHAR2
)
RETURN VARCHAR2 AS
v_FullName varchar2(4000);
BEGIN
v_FullName := in_LastName || ' ' || in_FirstName;
RETURN v_FullName;
END; -- LASTNAMEFIRST;
This can of course be simplified (say by not using a local variable), but it follows the logic of your code.
Using DECLARE where you have is essentially starting a new code block, which is leading to the error you see. In your code, the DECLAREisn't necessary if you move the variable declaration prior to the BEGIN. DEFINE is also invalid. Something like this should work:
CREATE OR REPLACE FUNCTION LASTNAMEFIRST
(
varFirstName IN VARCHAR2,
varLastName IN VARCHAR2
)
RETURN VARCHAR2 AS
varFullName VARCHAR2(100);
BEGIN
varFullName := varLastName || ' ' || varFirstName;
RETURN varFullName;
END LASTNAMEFIRST;
This could be simplified further by removing the variable declaration completely:
CREATE OR REPLACE FUNCTION LASTNAMEFIRST
(
varFirstName IN VARCHAR2,
varLastName IN VARCHAR2
)
RETURN VARCHAR2 AS
BEGIN
RETURN varLastName || ' ' || varFirstName;
END LASTNAMEFIRST;
I'm trying to write a PL SQL function however I'm having some issues with the formatting and general syntax.
This is my current code:
create or replace function getName(name IN varchar, organisation OUT varchar,
xcoord OUT number, ycoord OUT number, errormsg OUT varchar)
return varchar2
as
begin
Select name, LOCX, LOCY
from BBT_POI
WHERE (UPPER(name) LIKE UPPER('%'|| NAME || '%'));
return NAME;
end;
/
variable input varchar;
Variable errormsg varchar;
Variable organisation varchar;
Variable x NUMBER;
Variable y NUMBER;
execute getName ('yach', organisation, x, y, errormsg);
and these are the error messages when I run the create or replace function command:
FUNCTION GETNAME compiled
Errors: check compiler log
Error(5,3): PLS-00428: an INTO clause is expected in this SELECT statement
and when I run the execute line I get this:
Error starting at line : 40 in command -
execute getName ('yach', organisation, x, y, errormsg)
Error report -
ORA-06550: line 1, column 24:
PLS-00201: identifier 'ORGANISATION' must be declared
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:
The procedure should take one IN parameter (name or part of a name), and return four OUT parameters (the name of the organisation, X-coord, Y-coord, error message(or 'none' if no error message is thrown)).
I know currently the code doesn't look for errors however I am just trying to get a simple version working which I can add more functionality to after.
There are couple of problems with your code:
First you are experiencing the PLS-00428 error which means you are not giving an INTO clause.
Example:
create or replace function getName(name IN varchar, organisation OUT varchar,
xcoord OUT number, ycoord OUT number, errormsg OUT varchar)
return varchar2
as
begin
Select name, LOCX, LOCY
INTO name, XCOORD, YCOORD -- I assume this is how you need them kept
from BBT_POI
WHERE (UPPER(name) LIKE UPPER('%'|| NAME || '%'));
return NAME;
end;
/
Next your next block must be defined as an anonymous block. Oracle doesn't know you are trying to use PL/SQL unless you specifically tell it you are.
So your variable stuff needs to be in a block (also you don't need to say variable):
DECLARE
input varchar;
errormsg varchar;
organisation varchar;
x NUMBER;
y NUMBER;
BEGIN
getName('yach', organisation, x, y, errormsg);
END;
/
I do not have a tool right now to check whether it works for sure, but try this:
create or replace function getName(p_name IN varchar, organisation OUT varchar,
xcoord OUT number, ycoord OUT number, errormsg OUT varchar)
return varchar2
as
begin
Select name, LOCX, LOCY
INTO p_name, xcoord, coord
from BBT_POI
WHERE (UPPER(p_name) LIKE UPPER('%'|| p_name || '%'));
return p_name;
end;
/
declare
input varchar;
errormsg varchar;
organisation varchar;
x NUMBER;
y NUMBER;
begin
input:=getName ('yach', organisation, x, y, errormsg);
end;
I'm trying to make a modification to an existing PL/SQL package I coded some time ago. The original block in question looked like this (which worked fine)
FUNCTION GEN_COLUMN_TYPE ( DTYPE IN VARCHAR2, --DATATYPE OF THE COLUMN
PRCISION IN NUMBER, --PRECISION OF THE COLUMN
SCLE IN NUMBER, --PRECISION OF THE COLUMN
DATA_LENGTH IN NUMBER, --DATA LENGTH OF THE COLUMN
CHAR_LENGTH IN NUMBER, --CHARACTER LENGTH OF THE COLUMN
APP_CODE IN VARCHAR2 -- DATABASE TYPE
)RETURN STRING IS
V_COLUMNTYPE STRING(40) := NULL;
BEGIN
V_COLUMNTYPE := DTYPE;
BEGIN
SELECT TARGET_DATATYPE INTO V_COLUMNTYPE FROM DDL_DATATYPE_MAP DDM
INNER JOIN DDL_SOURCE_APPLICATION DSA ON DDM.TARGET_DBMS=DSA.TARGET_DBMS AND DDM.SOURCE_DBMS=DSA.SOURCE_DBMS
AND DSA.APPLICATION_CODE=APP_CODE
WHERE DDM.SOURCE_DATATYPE=DTYPE;
END;
I have modified it now to look like this. The only change I made was adding the 2 new variables into the definition and the "SELECT INTO" statement so I could use them based on the same join criteria later in the code.
FUNCTION GEN_COLUMN_TYPE ( DTYPE IN VARCHAR2, --DATATYPE OF THE COLUMN
PRCISION IN NUMBER, --PRECISION OF THE COLUMN
SCLE IN NUMBER, --PRECISION OF THE COLUMN
DATA_LENGTH IN NUMBER, --DATA LENGTH OF THE COLUMN
CHAR_LENGTH IN NUMBER, --CHARACTER LENGTH OF THE COLUMN
APP_CODE IN VARCHAR2, --APPLICATION CODE
DBMS_TYPE VARCHAR2, --DATABASE TYPE
VAR_LENGTH_IND VARCHAR2 --WHETHER THE FIELD NEEDS PARENS OR NOT
)RETURN STRING IS
V_COLUMNTYPE VARCHAR2(40) := NULL;
BEGIN
V_COLUMNTYPE := DTYPE;
BEGIN
SELECT TARGET_DATATYPE, SOURCE_DBMS, TARGET_VARIABLE_LEN_IND INTO V_COLUMNTYPE, DBMS_TYPE, VAR_LENGTH_IND FROM DDL_DATATYPE_MAP DDM
INNER JOIN DDL_SOURCE_APPLICATION DSA ON DDM.TARGET_DBMS=DSA.TARGET_DBMS AND DDM.SOURCE_DBMS=DSA.SOURCE_DBMS
AND DSA.APPLICATION_CODE=APP_CODE
WHERE DDM.SOURCE_DATATYPE=DTYPE;
END;
And I'm getting the all-too-common "PL-00382: expression is of wrong type" on the "SELECT ... INTO" statement at the end. All three variables and columns datatypes are varchar2. I've poured over this and can't see anything wrong. What am I missing?
Edit:
Sharing this attempt to use local variables and rewrote the naming for more readability. However I am getting an error on "'V_DBMS_TYPE' must be declared"
FUNCTION GEN_COLUMN_TYPE ( P_DTYPE IN VARCHAR2, --DATATYPE OF THE COLUMN
P_PRCISION IN NUMBER, --PRECISION OF THE COLUMN
P_SCLE IN NUMBER, --PRECISION OF THE COLUMN
P_DATA_LENGTH IN NUMBER, --DATA LENGTH OF THE COLUMN
P_CHAR_LENGTH IN NUMBER, --CHARACTER LENGTH OF THE COLUMN
P_APP_CODE IN VARCHAR2
)RETURN STRING IS
V_COLUMNTYPE VARCHAR2(40) := NULL;
BEGIN
V_COLUMNTYPE := P_DTYPE;
DECLARE
V_DBMS_TYPE VARCHAR2(30) := NULL; --DATABASE TYPE
V_VAR_LENGTH_IND VARCHAR2(30) := NULL; --WHETHER THE FIELD NEEDS PARENS OR NOT
BEGIN
SELECT TARGET_DATATYPE, SOURCE_DBMS, TARGET_VARIABLE_LEN_IND INTO V_COLUMNTYPE, V_DBMS_TYPE, V_VAR_LENGTH_IND FROM DDL_DATATYPE_MAP DDM
INNER JOIN DDL_SOURCE_APPLICATION DSA ON DDM.TARGET_DBMS=DSA.TARGET_DBMS AND DDM.SOURCE_DBMS=DSA.SOURCE_DBMS
AND DSA.APPLICATION_CODE=P_APP_CODE
WHERE DDM.SOURCE_DATATYPE=P_DTYPE;
You added two parameters to your function. You don't specify a parameter mode (IN, OUT, or IN OUT) so the default is IN. An IN parameter cannot be the target of a SELECT INTO because the parameter is read-only inside the function.
It is not clear to me whether you really wanted to declare two new local variables, rather than two additional parameters, or whether you really want to declare the two new parameters to be OUT or IN OUT parameters. If you declare the parameters as OUT or IN OUT, they could be the target of an assignment. But your function would no longer be callable from SQL. If you declare two new local variables, the function would still be callable from SQL but you would not be able to pass in (or be passed back) the values that you gather from your SELECT statement.
As a general suggestion, I would much prefer to see a common naming convention used to differentiate parameters from local variables and from column names-- that makes reading the code much easier. If your parameters, for example, used a standard P_ prefix, it would be immediately clear in your SELECT statement that some of your targets were local variables and some were parameters.
Based on the comments, you want to declare additional local variables rather than adding parameters. That would look something like
CREATE OR REPLACE FUNCTION GEN_COLUMN_TYPE (
DTYPE IN VARCHAR2, --DATATYPE OF THE COLUMN
PRCISION IN NUMBER, --PRECISION OF THE COLUMN
SCLE IN NUMBER, --PRECISION OF THE COLUMN
DATA_LENGTH IN NUMBER, --DATA LENGTH OF THE COLUMN
CHAR_LENGTH IN NUMBER, --CHARACTER LENGTH OF THE COLUMN
APP_CODE IN VARCHAR2 --APPLICATION CODE
)
RETURN VARCHAR2
IS
V_DBMS_TYPE VARCHAR2(100); --DATABASE TYPE
V_VAR_LENGTH_IND VARCHAR2(100); --WHETHER THE FIELD NEEDS PARENS OR NOT
V_COLUMNTYPE VARCHAR2(40) := NULL;
BEGIN
V_COLUMNTYPE := DTYPE;
SELECT TARGET_DATATYPE, SOURCE_DBMS, TARGET_VARIABLE_LEN_IND
INTO V_COLUMNTYPE, V_DBMS_TYPE, V_VAR_LENGTH_IND
FROM DDL_DATATYPE_MAP DDM
INNER JOIN DDL_SOURCE_APPLICATION DSA ON DDM.TARGET_DBMS=DSA.TARGET_DBMS AND
DDM.SOURCE_DBMS=DSA.SOURCE_DBMS AND
DSA.APPLICATION_CODE=APP_CODE
WHERE DDM.SOURCE_DATATYPE=DTYPE;
<<more code>>
END;