The following code runs good as expected in SQL Developer without any errors, but when I run the code in SQL*Plus it is throwing an error.
declare
v_num1 integer := '&a';
v_num2 integer := '&b';
v_result number;
begin
v_result := v_num1 / v_num2;
dbms_output.put_line ('v_result: '||v_result);
end;
The error raised is
ERROR at line 1:
ORA-06502: PL/SQL: numeric or value error: character to number conversion error
ORA-06512: at line 2
Please point out where the error I made.
Your SQL*Plus session has substitution variables turned off, so the '&a' is being treated as a literal string, and it isn't prompting you for the value. In a simplified version:
set define off
declare
i integer := to_number('&a');
begin
null;
end;
/
declare
*
ERROR at line 1:
ORA-06502: PL/SQL: numeric or value error: character to number conversion error
ORA-06512: at line 2
To fix it, turn substitution variables back on:
set define &
Then it works:
declare
i integer := to_number('&a');
begin
null;
end;
/
Enter value for a:
If I enter 1:
PL/SQL procedure successfully completed.
You don't need the quotes, or the to_number(), unless you're using group seperators (and then you'd need to supply a format model too). You can just do:
declare
i integer := &a;
begin
null;
end;
/
As you have it you're doing an unnecessary implicit string-to-number conversion.
If you haven't explicitly turned them off, then you probably have a site or user profile that is doing the set define off, among other configuration settings. it's probably doing that for a reason, but if you currently only have a site profile then you could create your own user profile to override that. Just be aware of the effect of anything you change; you may have other scripts that rely on it being off, e.g. if you have data that has ampersands and it's been disabled to prevent issues interpreting those.
Related
I'm trying to determine the line in a stored procedure or the last SQL-statement which is causing an error. As a workaround I'm using temporary variables which I manually set to determine in which part of my stored procedure an error occurs.
See the following:
-- Create an ErrorLog table
Create Table SCHEMA.ErrorLog_lrc_test
(
ErrSQLCODE Integer ,
Codepart Char(1),
Type Char(1) ,
MsgText VarChar(1024));
CREATE OR REPLACE PROCEDURE SCHEMA.test_error(IN divisor INT)
LANGUAGE SQL
BEGIN
-- Define variables
DECLARE codepart_var Char(1);
DECLARE test_INT INT;
-- Define sqlcode
DECLARE SQLCODE INTEGER;
--Define Error-Handler
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
BEGIN
INSERT INTO SCHEMA.ErrorLog_lrc_test(ErrSQLCODE, Codepart, TYPE, MsgText)
VALUES(SQLCODE, codepart_var, 'E', SYSPROC.SQLERRM(SQLCODE));
END;
--Define Warning-Handler
DECLARE CONTINUE HANDLER FOR SQLWARNING, NOT FOUND
BEGIN
INSERT INTO SCHEMA.ErrorLog_lrc_test(ErrSQLCODE, Codepart, TYPE, MsgText)
VALUES(SQLCODE, codepart_var, 'W', SYSPROC.SQLERRM(SQLCODE));
END;
-- Set temporary variable to 'a' to get part of code where error occured
SET codepart_var = 'a';
-- Create Error
sELECT 1/divisor into test_INT
FROM SYSIBM.SYSDUMMY1;
SET codepart_var = 'b';
-- Create Error
sELECT 1/divisor into test_INT
FROM SYSIBM.SYSDUMMY1;
SET codepart_var = 'c';
-- Create Not Found (Sqlcode 100)
INSERT INTO SCHEMA.ErrorLog_lrc_test
SELECT NULL, NULL, NULL, NULL FROM "SYSIBM".SYSDUMMY1
WHERE 1 = 0 ;
END
call SCHEMA.test_error(0);
SELECT *
FROM SCHEMA.ErrorLog_lrc_test;
I get the following:
ERRSQLCODE
CODEPART
TYPE
MSGTEXT
-801
a
E
SQL0801N Division by zero was attempted.
-801
b
E
SQL0801N Division by zero was attempted.
100
c
W
SQL0100W No row was found for FETCH, UPDATE or DELETE; or the result of a query is an empty table.
So I am able to get the part of the code where an error or warning occurs, but it would be better to get the line or the SQL statement as I don't want to specify every part of the code with a temporary variable.
I already found this SQLCA --> sqlerrd(3): "...If an error is encountered during the compilation of an SQL routine, trigger, or dynamic compound SQL (inlined or compiled) statement, sqlerrd(3) contains the line number where the error was encountered". For now I didn't manage to make use of SQLCA variables. I don't know how to implement them in DB2 LUW in a stored procedure.
Is there another/better way to log the specific line or SQL-statement in a stored procedure which is causing an error?
My DB2 version is 10.5.0.
Thank you!
If your Db2-server platform is Linux/Unix/Windows, and you are using a recent version, then consider using DBMS_UTILITY.FORMAT_ERROR_BACKTRACE which may help you.
Documentation here. The documentation includes a worked example.
When using this for stored procedures or routines, it is wise to always create those routines with a meaningful specific name with the SPECIFIC clause on the create or replace statement. Otherwise the routine will have a system generated name which will not be meaningful to users when it appears in the output of DBMS_UTILITY.FORMAT_ERROR_BACKTRACE. There are other reasons you should always use a specific name for your routines.
The SQLCA is for calling programs (i.e. the program that calls the stored procedure).
When I do this in my stored procedure:
create procedure Proc1(
startdate IN TIMESTAMP,
ENDDATE IN TIMESTAMP
)
declare test_result number --line 55
test_result:=Stored_function1(startdate,enddate,11,13); --line 56
END;
SQL Developer throws 2 errors:
PLS-00103: Encountered the symbol "TEST_RESULT" when expecting one of the following: := . ( # % ; not null range default character The symbol "." was substituted for "TEST_RESULT" to continue.
PLS-00103: Encountered the symbol "END" when expecting one of the following: begin function pragma procedure subtype type current cursor delete exists prior
Stored_function1 is user defined, takes 4 parameters, and does not belong to any package. Where did I do wrong and how do I correct it? Thanks.
Without seeing more of it it's hard to tell, but it seems you have some syntax error(s) in your procedure. No DECLARE is needed, there should be a semi-colon at the end of line 55 and a BEGIN before line 56 at the least.
Here's a basic skeleton:
Create or replace procedure my_procedure as
test_result number;
BEGIN
test_result := Stored_function1(startdate, enddate, 11, 13);
END;
I am trying to create a stored procedure with boolean input parameter:-
Step 1: I have created a table like below
CREATE TABLE STOREBOOL ( BOOLVAL NUMBER(1));
Step 2: I created a stored procedure
Based on the following link: http://docs.oracle.com/cd/F49540_01/DOC/java.815/a64685/tips3.htm#1005343 created a procedure
CREATE OR REPLACE PROCEDURE boolProc(x boolean)
AS
BEGIN
INSERT INTO storebool("boolval") VALUES(x);
COMMIT;
END;
Output is:-
Warning: Procedure created with compilation errors.
Similarly created a second procedure:
CREATE OR REPLACE PROCEDURE boolWrap(x int)
AS
BEGIN
IF (x=1) THEN
boolProc(TRUE);
ELSE
boolProc(FALSE);
END IF;
END;
Output is:-
Warning: Procedure created with compilation errors.
Step 3: Executing the code
BEGIN
boolWrap(1);
END;
/
It is showing the following error:
boolWrap(1);
*
ERROR at line 2:
ORA-06550: line 2, column 1
PLS-00905: object SCOTT.BOO
ORA-06550: line 2, column 1
PL/SQL: Statement ignored
How do i run it properly?
Did you notice the "created with compilation errors" messages? You can see what the errors were immediately after you see that with show errors, or by querying the user_errors view.
In the first procedure you're dong this:
INSERT INTO storebool("boolval") VALUES(x);
... which has two problems: (a) you don't have a column called "boolval"; it was created unquoted as BOOLVAL so either refer to it unquoted, or quoted but in uppercase as it appears in the data dictionary; and (b) you're trying to set a number column to a boolean value. Oracle SQL doesn't have a boolean type, but there is no implicit conversion available. You need to define what number represents TRUE, and what represents FALSE, and then insert that number instead of the boolean x argument. So you'd see errors like:
4/46 PLS-00382: expression is of wrong type
4/28 PL/SQL: ORA-00904: "boolval": invalid identifier
The boolWrap can't compile because boolProc isn't valid.
Your boolProc and boolWrap procedures are reversed really; you want the 'real' procedure to take a number argument, and the wrapper to take a boolean and convert that to a number to call the real one.
This might make more sense:
CREATE TABLE STOREBOOL (BOOLVAL NUMBER(1),
CONSTRAINT BOOLCHECK CHECK (BOOLVAL IN (0,1))
);
CREATE OR REPLACE PROCEDURE boolProc(p_bool_num number)
AS
BEGIN
INSERT INTO storebool(boolval) VALUES (p_bool_num);
END;
/
CREATE OR REPLACE PROCEDURE boolWrap(p_bool boolean)
AS
l_bool_num number;
BEGIN
IF p_bool THEN
boolProc(1);
ELSE
boolProc(0);
END IF;
END;
/
Then you can call boolProc with a number argument, or boolWrap with a boolean argument:
BEGIN
boolProc(1);
END;
/
BEGIN
boolWrap(false);
END;
/
select * from storebool;
BOOLVAL
----------
1
0
pL/SQL PROCEDURE
This is the code for a procedure that takes the term,lineno,component name,student id,score as input and processes the student score.
The procedure should add the score into the scores table if there are no exceptions.
CODE
CREATE OR REPLACE PROCEDURE score_details
(aterm IN scores.Term%type,
alineno IN scores.Lineno%type,
acompname IN scores.Compname%type,
asid IN scores.sid%type,
apoints IN scores.points%type)
AS sterm scores.Term%type;
slineno scores.Lineno%type;
scompname scores.compname%type;
ssid scores.sid%type;
spoints scores.points%type;
BEGIN
SELECT term,lineno,compname,sid,points
INTO sterm,slineno,scompname,ssid,spoints
FROM scores
WHERE aterm=term AND alineno=Lineno AND acompname=compname AND asid=sid AND apoints=points;
EXCEPTION
when no_data_found THEN
dbms_output.put_line('Invalid details');
ANONYMOUS BLOCK
The below is the code for anonymous block to test the above procedure.
I'm not able to get the correct result.Please help me with the code.
ACCEPT prompt 'pterm','plineno','pcompname','psid','ppoints'
DECLARE pterm scores.Term%type;
plineno scores.Lineno%type;
pcompname scores.Compname%type;
psid scores.sid%type;
ppoints scores.points%type;
BEGIN
score_details(pterm,plineno,pcompname,psid,ppoints);
END
I'm going to assume that you've intentionally missed out most of the body of your stored procedure, as it fetches some values out of a table into some local variables and then does nothing with these values.
I'm guessing that you're wondering why the values you entered in the ACCEPT line didn't make it into the stored procedure call.
ACCEPT is a SQL*Plus statement that you can use to set substitution variables. The following example creates a substitution variable named colour and displays the result. The line Yellow was typed in by me:
SQL> ACCEPT &colour
Yellow
SQL> PROMPT Your favourite colour is &colour
Your favourite colour is Yellow
You can also provide a prompt to the user to clarify what you're asking for:
SQL> ACCEPT colour PROMPT 'Enter a colour > '
Enter a colour > Yellow
and you can also use substitution variables in SQL:
SQL> select '&colour' from dual;
old 1: select '&colour' from dual
new 1: select 'Yellow' from dual
'YELLO
------
Yellow
It seems there's two things not quite right with your PL/SQL block at the moment:
Your ACCEPT statement isn't working. You haven't quite got the order of the parts of it right (if used, PROMPT must come after the variable name). Also, I don't think you can set more than one substitution variable in a single ACCEPT.
You're not using the substitution variables that contain the values entered.
I'd therefore imagine that you'd want the call to your PL/SQL block to look a bit more like the following:
ACCEPT pterm PROMPT 'Enter a term > '
ACCEPT plineno PROMPT 'Enter a line number > '
-- and similarly for the others.
DECLARE
pterm scores.Term%type := '&pterm';
plineno scores.Lineno%type := '&plineno';
pcompname scores.Compname%type := '&pcompname';
psid scores.sid%type := '&psid';
ppoints scores.points%type := '&ppoints';
BEGIN
score_details(pterm,plineno,pcompname,psid,ppoints);
END;
I am having a problem getting a list of fields from a query defined at run time by the users of my program. I let my users enter a SQL query into a memo control and then I want to let them go through the fields that will return and do such things as format the output, sum column values and so forth. So, I have to get the column names so they have a place to enter the additional information.
I would do fine if there were no parameters, but I also have to let them define filter parameters for the query. So, if I want to set the parameters to null, I have to know what the parameter's datatype is.
I am using Delphi 2006. I connect to a Firebird 2.1 database using the DBExpress component TSQLConnection and TSQLQuery. Previously, I was successful using:
for i := 0 to Qry.Params.Count - 1 do Qry.Params[i].value := varNull;
I discovered I had a problem when I tried to use a date parameter. It was just a coincidence that all my parameters up until then had been integers (record IDs). It turns out that varNull is just an enumerated constant with a value of 1 so I was getting acceptable results (no records) was working okay.
I only need a list of the fields. Maybe I should just parse the SELECT clause of the SQL statement. I thought setting Qry.Prepared to True would get me a list of the fields but no such luck. It wants values for the parameters.
If you have an idea, I would sure like to hear it. Thanks for any help.
Replied again 'coz I'm interested. My methods works (with my queries) because they have been pre-defined with the params' datatypes preset to the correct type:)
I'm not sure how you are expecting the query to know or derive the datatype of the param given that you are not even selecting the field that it operates against.
So I think your query setup and user input method will need more attention. I've just looked up how I did this a while ago. I do not use a parameterised query - I just get the "parameter values" from the user and put them directly into the SQL. So your sql would then read:
SELECT s.hEmployee, e.sLastName
FROM PR_Paystub s
INNER JOIN PR_Employee e ON e.hKey = s.hEmployee
WHERE s.dtPaydate > '01/01/2008'
therefore no parameter type knowledge is necessary. Does not stop your users entering garbage but that goes back to input control :)
Although a slightly different dataset type this is what I use with TClientDataset simple and effective :)
for i := 0 to FilterDataSet.Params.Count -1 do
begin
Case FilterDataSet.Params.Items[i].Datatype of
ftString:
ftSmallint, ftInteger, ftWord:
ftFloat, ftCurrency, ftBCD:
ftDate:
ftTime:
ftDateTime:
.
.
.
end;
end;
can you not do something similar with the query?
You guys are making this way too hard:
for i := 0 to Qry.Params.Count - 1 do begin
Qry.Params[i].Clear;
Qry.Params[i].Bound := True;
end;
I'm not sure what version of Delphi you are using. In the Delphi 2006 help under Variant Types, it says:
Special conversion rules apply to the
Borland.Delphi.System.TDateTime type
declared in the System unit. When a
Borland.Delphi.System.TDateTime is
converted to any other type, it
treated as a normal Double. When an
integer, real, or Boolean is converted
to a Borland.Delphi.System.TDateTime,
it is first converted to a Double,
then read as a date-time value. When a
string is converted to a
Borland.Delphi.System.TDateTime, it is
interpreted as a date-time value using
the regional settings. When an
Unassigned value is converted to
Borland.Delphi.System.TDateTime, it is
treated like the real or integer value
0. Converting a Null value to Borland.Delphi.System.TDateTime raises
an exception.
The last sentence seems important to me. I would read that as varNull cannot be converted to a TDateTime to put into the field, and hence you get the exception that you're experiencing.
It also implies that this is the only special case.
Couldn't you do something like:
for i := 0 to Qry.Params.Count - 1 do
begin
if VarType(Qry.Params[i].value) and varTypeMask = varDate then
begin
Qry.Params[i].value := Now; //or whatever you choose as your default
end
else
begin
Qry.Params[i].value := varNull;
end;
end;
What I ended up doing was this:
sNull := 'NULL';
Qry.SQL.Add(sSQL);
for i := 0 to Qry.Params.Count - 1 do begin
sParamName := Qry.Params[i].Name;
sSQL := SearchAndReplace (sSQL, ':' + sParamName, sNull, DELIMITERS);
end;
I had to write SearchAndReplace but that was easy. Delimiters are just the characters that signal the end of a word.
TmpQuery.ParamByName('MyDateTimeParam').DataType := ftDate;
TmpQuery.ParamByName('MyDateTimeParam').Clear;
TmpQuery.ParamByName('MyDateTimeParam').Bound := True;