PL/SQL: can't find the error in this function - oracle

I'm a student and I have this exercise: we have to write a function with 2 parameters, account number and withdrawal, and return the new balance of a bank account only if the account balance - withdrawal > Flow threshold
This is my code:
set serveroutput on
CREATE OR REPLACE FONCTION Retrait
(f_numcomp in varchar2,f_montant NUMBER(38,3))
RETURN NUMBER(38,3)
AS
v_compte compte%rowtype;
v_solde compte.Solde%type;
BEGIN
SELECT * into v_compte from compte where f_numcomp = compte.NUMEROCOMPTE;
if (v_compte.Solde - f_montant) > v_compte.SeuilDebit /*and compte.Etat != 'desactiver'*/ THEN
v_solde := v_compte.Solde - f_montant;
UPDATE compte SET Solde = Solde - f_montant where f_numcomp = compte.NumeroCompte;
else
dbms_output.put_line('solde insufusant!');
end if;
return(v_solde);
END Retrait;
/
This is what I get:
Rapport d'erreur -
ORA-06550: Ligne 9, colonne 16 :
PLS-00103: Symbole "(" rencontré à la place d'un des symboles suivants :
. ;
06550. 00000 - "line %s, column %s:\n%s"
*Cause: Usually a PL/SQL compilation error.
*Action:
I'm new here; I read some articles here but still didn't find the error

You should be able to simplify the function to use a single UPDATE statement with a RETURNING clause (rather than SELECT and then UPDATE):
CREATE FUNCTION Retrait (
f_numcomp IN COMPTE.NUMEROCOMPTE%TYPE,
f_montant IN COMPTE.SOLDE%TYPE
) RETURN COMPTE.SOLDE%TYPE
AS
v_solde COMPTE.SOLDE%TYPE;
BEGIN
UPDATE compte
SET Solde = Solde - f_montant
WHERE f_numcomp = NumeroCompte
AND solde - f_montant > SeuilDebit
RETURNING solde INTO v_solde;
IF SQL%ROWCOUNT = 0 THEN
DBMS_OUTPUT.PUT_LINE('solde insufusant!');
END IF;
RETURN v_solde;
END Retrait;
/
However, it is not usual to have DML statements in a function; you would normally use a PROCEDURE and have an OUT parameter to return the value.
fiddle

FONCTION must be FUNCTION, but that was obviously just a typo in the request here, because the error you got is something else.
In the function declaration Oracle wants types without precision. I.e. NUMBER instead of NUMBER(38, 3).
CREATE OR REPLACE FUNCTION Retrait (f_numcomp IN VARCHAR2, f_montant NUMBER)
RETURN NUMBER
AS
v_compte compte%ROWTYPE;
v_solde compte.Solde%TYPE;
BEGIN
SELECT * INTO v_compte
FROM compte
WHERE f_numcomp = compte.NUMEROCOMPTE;
...

Related

Exception PL/SQL

Caution my english is horrible.
No one answers in the spanish forum.
doubts :
¿How can I control the exception of a empty entry? (Explanation following)
¿What value is saved in the variable if there's an empty entry?
I created this variable:
DECLARE
v_input NUMBER(8) := &entry;
I don't give a value, I mean in the pop up I accept directly without writing anything.
POPUP IMG
when I read the error code ( that I will leave at the end), I came across this line.
PLS-00103: Encountered the symbol ' ; ' when expecting one of the following :
So I guess the variable default value if you do a empty entry is -> ;
then I thought of controling the exception creating a IF, this if compares my variable (with empty entry/value) to ' ; ' you can see below.
BEGIN
DBMS_OUTPUT.PUT_LINE(v_codigo);
IF(v_codigo = ';')THEN
RAISE v_excepcion;
END IF;
EXCEPTION
when v_excepcion then
DBMS_OUTPUT.PUTLINE('No has insertado un valor válido');
END;
ERROR CODE
Informe de error -
ORA-06550: línea 2, columna 32:
PLS-00103: Se ha encontrado el símbolo ";" cuando se esperaba uno de los siguientes:
( - + 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 al
06550. 00000 - "line %s, column %s:\n%s"
*Cause: Usually a PL/SQL compilation error.
*Action:
sorry for my grammar mistakes, If it does not clear I will follow the post for give you more information.
thx
Exception handlers are for exceptions (errors) encountered during execution of your function or procedure or anonymous block. They don't handle syntax errors - where the function or procedure won't even compile.
You are using a substitution variable - one that is "substituted" (replaced) by your editor BEFORE the code is ever presented to the PL/SQL parser. THAT kind of error can't be anticipated and handled in the code - if you return nothing (an empty string) when prompted, the code itself will receive "nothing" in that place, and the syntax of your function or procedure will be incorrect, so you will get the syntax error you received. Your code looks exactly like this after the substitution:
v_input NUMBER(8) := ;
resulting exactly in the syntax error you have seen.
The correct way to pass in a value at run time is to use parameters (for functions or procedures), or bind variables in an anonymous block. The variable must be declared and initialized before executing the block (in a command line environment like SQL*Plus); or, in a GUI like Toad or SQL Developer, you will be prompted for a value for the bind variable. If you press Enter without giving a value, that is interpreted as NULL, which is perfectly valid. (Whether it is what you want or not, that's a different question.)
The code would have to look something like this:
declare
v_input number(8) := :entry; -- notice the leading colon :
begin
dbms_output.put_line(v_input);
end;
/
If you want to handle the case of NULL input, you would do it in code:
declare
v_input number(8) := :entry;
begin
if v_input is null then
-- handle it here
else
dbms_output.put_line(v_input); -- or whatever else you need to do
end if;
end;
/
You don't need exception to handle that; you could dbms_output.put_line (note that you used putline, which is wrong) within IF, or even raise_application_error if you want to stop execution. For example (valid value first):
SQL> declare
2 v_codigo varchar2(20) := '&entry';
3 begin
4 if v_codigo = ';' or v_codigo is null then
5 raise_application_Error(-20000, 'Invalid value');
6 else
7 dbms_output.put_line('You entered ' || v_codigo);
8 end if;
9 end;
10 /
Enter value for entry: Littlefoot
You entered Littlefoot
PL/SQL procedure successfully completed.
Invalid values: empty string and ;;
SQL> /
Enter value for entry:
declare
*
ERROR at line 1:
ORA-20000: Invalid value
ORA-06512: at line 5
SQL> /
Enter value for entry: ;
declare
*
ERROR at line 1:
ORA-20000: Invalid value
ORA-06512: at line 5
SQL>
Or, to reuse your own code: declare the exception first (line #3), then use it:
SQL> declare
2 v_codigo varchar2(20) := '&entry';
3 v_excepcion exception;
4 BEGIN
5 DBMS_OUTPUT.PUT_LINE(v_codigo);
6 IF(v_codigo = ';' or v_codigo is null) THEN
7 RAISE v_excepcion;
8 END IF;
9
10 EXCEPTION
11 when v_excepcion then
12 DBMS_OUTPUT.PUT_LINE('No has insertado un valor válido');
13 END;
14 /
Enter value for entry: Littlefoot
Littlefoot
PL/SQL procedure successfully completed.
SQL> /
Enter value for entry:
No has insertado un valor válido
PL/SQL procedure successfully completed.
SQL> /
Enter value for entry: ;
;
No has insertado un valor válido
PL/SQL procedure successfully completed.
SQL>

12703. 00000 - "this character set conversion is not supported"

i'm using a function to convert currencies but i'm getting this error 12703. 00000 - "this character set conversion is not supported", i don't know why.
here's the code i'm using
CREATE OR REPLACE FUNCTION convert(r_comm IN commande.ref_commande%type,
devise IN VARCHAR2)
RETURN commande.montant_ttc%type IS
mt commande.montant_ttc%type;
montant NUMBER(20) :=0;
BEGIN
IF devise = 'dollar' THEN
SELECT montant_ttc INTO mt FROM commande WHERE ref_commande = r_comm;
montant := mt*10;
END IF;
IF devise = 'euro' THEN
SELECT montant_ttc INTO mt FROM commande WHERE ref_commande = r_comm;
montant := mt*11;
END IF;
RETURN montant;
END convert;
/
DECLARE
r_comm commande.ref_commande%type :=1;
devise VARCHAR2(6) := 'dollar';
mt NUMBER(20);
BEGIN
mt := convert(r_comm, devise);
dbms_output.put_line(mt);
END;
/
THE ERROR
THE TABLE
It seems that the call is going to the different convert function (oracle provides convert function).
The Oracle CONVERT() function converts a string from one character
set to another.
Solution:
I would suggest changing the name of your function as the convert is the oracle function.
Or
Use your schemaname.function while calling it. But It is not recommended to have same object name so option 1 is better.
Cheers!!

Ajust table rows after after modifications (Delete, Insert Update) on Oracle

On table "Types", the rows order shall be controled by the field "ordem".
After any data modifications(Delete, Insert or Update), the others rows should be ajusted to assure correct exbitions of the content.
To implement this funcionality, I tried to code a trigger to correct the values in two similar ways (I, II).
CREATE TABLE TYPES (
ID NUMBER PRIMARY KEY,
ARG_1 VARCHAR2(20) NOT NULL,
ARG_2 VARCHAR2(20) NOT NULL,
ORDEM NUMBER NOT NULL
);
-----------------------------------------------
-- I
-----------------------------------------------
CREATE OR REPLACE TRIGGER TGR_TYPES
AFTER INSERT OR UPDATE OR DELETE ON TYPES
DECLARE
V_NORDEM NUMBER := NEW.ORDEM;
CURSOR C_TYPES IS
SELECT ID, ORDEM
FROM TYPES
WHERE ORDEM >= V_NORDEM;
BEGIN
IF UPDATING OR INSERTING THEN
BEGIN
FOR R_TYPE IN C_TYPES LOOP
UPDATE TYPEA SET ORDEM = (ORDEM + 1) WHERE ID = R_TYPE.ID;
END LOOP;
END;
ELSE
DECLARE V_ORDEM NUMBER := 0;
BEGIN
FOR R_TYPE IN C_TYPES LOOP
UPDATE OSP_TP_ADDR_COMPLEMENTOS SET ORDEM = (V_ORDEM + 1) WHERE ID = R_COMPLEMENTO.ID;
END LOOP;
END;
END IF;
END;
/*
ERROR ON COMPILE:
ORA-04082: referências NEW ou OLD não permitidas nos gatilhos de nível de tabela
04082. 00000 - "NEW or OLD references not allowed in table level triggers"
*Cause: The trigger is accessing "new" or "old" values in a table trigger.
*Action: Remove any new or old references.
*/
-----------------------------------------------
-- II
-----------------------------------------------
CREATE OR REPLACE
TRIGGER TRG_TYPES
AFTER INSERT OR UPDATE OR DELETE ON TYPES
FOR EACH ROW
BEGIN
IF UPDATING OR INSERTING THEN
BEGIN
FOR TP IN (
SELECT *
FROM TYPES
WHERE ORDEM >= :NEW.ORDEM
) LOOP
UPDATE TYPES SET ORDEM = (ORDEM + 1) WHERE ID = TP.ID;
COMMIT;
END LOOP;
END;
ELSE
DECLARE V_ORDEM NUMBER := 0;
BEGIN
FOR TP IN (
SELECT *
FROM TYPES
ORDER BY ORDEM
) LOOP
UPDATE TYPES SET ORDEM = (V_ORDEM + 1) WHERE ID = TP.ID;
END LOOP;
END;
END IF;
END;
/*
ERROR ON UPDATE:
UPDATE TYPES
SET ORDEM = 14
WHERE ID=26
Relatório de erros -
ORA-04091: a tabela TYPES é mutante; talvez o gatilho/função não possa localizá-la
ORA-06512: em "", line 9
ORA-04088: erro durante a execução do gatilho ''
*/
I expect one solution to assure the trigger operation or other approach to the integrity of the order field. I have thinking about tring the Index-Organized Table structure, but not sure.

ORA-01403 no data found in trigger execution

I have a problem here with a trigger. The purpose of the trigger is to verify that the client associated with a car registration, paid or not paid the bill in a workshop. If the client has paid all, then it is created a new service order (so far runs), but then paid the account is not possible to create a work order. So here the problem arises when I try to insert a service.
This is what's causing the trigger to fire:
INSERT INTO ORDEM(cod_ordem,data,codigo_func_m,tipo_ordem,matricula,estado_ordem)
VALUES(to_char(seq_cod_ordem.nextval),to_date('23/11/2014','dd/mm/yyyy'),'2','Serviço','66-AB-00','Pendente')
and I get this error after the execution:
Error starting at line : 140 in command -
INSERT INTO ORDEM(cod_ordem,data,codigo_func_m,tipo_ordem,matricula,estado_ordem)
VALUES(to_char(seq_cod_ordem.nextval),to_date('23/11/2014','dd/mm/yyyy'),'2','Serviço','66-AB-00','Pendente')
Error report -
SQL Error: ORA-01403: não foram encontrados dados
ORA-06512: na "BD1415_DC5.SALDO_CLIENTE_OFICINA", linha 7
ORA-04088: erro durante a execução do trigger 'BD1415_DC5.SALDO_CLIENTE_OFICINA'
01403. 00000 - "no data found"
*Cause:
*Action:
This is my code:
create or replace TRIGGER saldo_cliente_Oficina
BEFORE INSERT ON ORDEM
FOR EACH ROW
DECLARE
t_codigo_cliente CLIENTES.codigo_cliente%TYPE;
t_estado BOOLEAN := TRUE;
t_excecao EXCEPTION;
BEGIN
SELECT DISTINCT codigo_cliente INTO t_codigo_cliente
FROM ORDEM, VEICULO
WHERE (ORDEM.matricula = :NEW.matricula) AND (ORDEM.matricula = VEICULO.matricula);
FOR t_estado_fact IN (SELECT FACTURA.numero_factura,FACTURA.codigo_cliente,FACTURA.estado FROM FACTURA,CLIENTES
WHERE CLIENTES.codigo_cliente = t_codigo_cliente AND CLIENTES.codigo_cliente = FACTURA.codigo_cliente)LOOP
IF t_estado_fact.estado = 'Não Paga' THEN
t_estado := FALSE;
END IF;
IF t_estado = FALSE THEN
RAISE t_excecao;
END IF;
END LOOp;
EXCEPTION
WHEN t_excecao THEN
RAISE_APPLICATION_ERROR(-20001, 'O Proprietário do veiculo que pretende criar uma ordem, deve serviços a Oficina.');
END saldo_cliente_Oficina;
The below select in the trigger is returning no rows.In this case the first insert in ORDEM for a particular matricula will always fail.
SELECT DISTINCT codigo_cliente INTO t_codigo_cliente
FROM ORDEM, VEICULO
WHERE (ORDEM.matricula = :NEW.matricula) AND (ORDEM.matricula = VEICULO.matricula);
Kindly try changing this to the below
SELECT DISTINCT codigo_cliente INTO t_codigo_cliente
FROM VEICULO
WHERE VEICULO.matricula=:NEW.matricula;

Get screen output of a function

I am an absolute beginner with PLSQL and I'm stuck on that problem :
I have a function :
FUNCTION fn_easy RETURN VARCHAR2 IS
BEGIN
RETURN 'This is a simple function';
END fn_easy;
I would like in a simple SQL script to see this output, but this code :
set serveroutput on format wraped;
VAR retMsg VARCHAR2;
BEGIN
dbms_output.put_line('=================================================');
retMsg := pkg.fn_easy();
dbms_output.put_line('=================================================');
END;
print retMsg;
Gives me the following error :
ORA-06550: Ligne 3, colonne 3 :
PLS-00201: identifier 'RETMSG' must be declared
ORA-06550: Ligne 3, colonne 3 :
PL/SQL: Statement ignored
06550. 00000 - "line %s, column %s:\n%s"
*Cause: Usually a PL/SQL compilation error.
*Action:
Prepend the variable declaration in your anonymous block with DECLARE keyword. Also note that your 'print' should be also probably dbms_output.put_line and that it is outside the block (after the END keyword).
This did the job :
DECLARE
retMsg VARCHAR2(100);
/* Appel de la procédure */
BEGIN
dbms_output.put_line('=================================================');
dbms_output.put_line(agi_ws_pkg.fn_easy());
dbms_output.put_line('=================================================');
END;

Resources