Output user input in PL/SQL using SQL developer - sql

Hi everyone I'm new to PL/SQL ,however I'm wrting a small code that a prompt a user to input a 2 numbers and display the numbers using DBMS_output.Put_line .
but I get a compilation error ,below is my code ,I'm using "Oracle SQL developer"
SET SERVEROUTPUT ON SIZE 1000000;
DECLARE
n_no_1 number(8,2);
n_no_2 number(8,2);
BEGIN
DBMS_output.put_line('Enter Value for no 1');
&n_no_1;
DBMS_output.put_line('Enter value for no 2');
&n_no_2;
DBMS_OUTPUT.PUT_LINE('The value of No 1 is' || n_no_1 );
DBMS_OUTPUT.PUT_LINE('The value of No 2 is' || n_no_2 );
END;
/

These 2 lines are your problem, however, not for the reasons mentioned in other answer:
&n_no_1;
&n_no_2;
In SQL, you can use the ampersand (&) to trigger something called "Macro substitution".
When the compiler comes across something like this (ie &n_no1), it prompts the user to input a value for it to substitute in it's place.
So if you enter "Hello". Your code becomes:
DBMS_output.put_line('Enter Value for no 1');
Hello;
And as you can see, that would fail, if you had just typed that out.
What you want to do is to assign that value to a variable, like this:
n_no_1 := '&n_no_1';
That gets "replaced" by this:
n_no_1 := 'Hello';
which compiles - and runs - just fine.
That all said, this is NOT the best way to do this, although this appears to be a learning excercise ?
Look up the PROMPT and ACCEPT keywords .. you can use those in SQL (outside your BEGIN / DECLARE / END block) to capture the values first in a neater fashion :)
http://docs.oracle.com/cd/E11882_01/server.112/e16604/ch_twelve032.htm#SQPUG052
http://docs.oracle.com/cd/E11882_01/server.112/e16604/ch_twelve005.htm#SQPUG026
Found an additional link here worth a good read. Explains a lot more than what you're looking at, but it discusses substitution variables, and other similiar things (and some other unrelated things :) )
https://blogs.oracle.com/opal/entry/sqlplus_101_substitution_varia

The &variable syntax is not PL/SQL: it is part of SQL Developer. I see what you are trying to do and syntax errors, but there's no point in correcting them because it's not going to work in the end.
The reason is: you cannot accept user input via PL/SQL at runtime.

Related

PLSQL construction a new function

I need a help
I'm make a function in plsql but I'm beginner
Challenge: Web a label has (imput) in less than 72hours it will not be able to leave stock
Could you help me or tell me if I'm on the right path?
create or replace FUNCTION CHECACODENTRADA
(
P_CODPRO IN VARCHAR2
)
RETURN VARCHAR2
AS
v_database DATE;
BEGIN
v_database := TRUNC (SYSDATE);
IF (p_CODPRO == '08932010','08932030','08942020','08942010','08942310','08932210')
THEN
SELECT SYSDATE+3/72 FROM DUAL
IF SYSDATE+3/72 >=
Welcome to stack overflow. I suggest you start by writing a function shell and then adding functionality to it.
create or replace FUNCTION CHECACODENTRADA
(
P_CODPRO IN VARCHAR2
) RETURN VARCHAR2
AS
BEGIN
RETURN 'ok';
END;
/
Then the next step. Add code and compile. Fix any errors and continue. If you're a complete novice then don't write large chunks of code because it could be hard to figure out what is wrong.
How are you creating these functions - are you using a proper tool like sqldeveloper ? If not... well you should. Those tools make developing pl/sql a lot easier.
Read documentation, look for examples. pl/sql has its own syntax, don't assume that you can just borrow the syntax javascript or java uses... that will cause numerous errors. In your code, for example:
-- 2 errors in following line.
-- 1. The "==" is not valid oracle syntax
-- 2. What is this ? '08932010','08932030'... is that a list of arguments - how would the operator "==" handle this ? What are you expecting ?
IF (p_CODPRO == '08932010','08932030','08942020','08942010','08942310','08932210') THEN
-- the code below will not compile. You cannot "just select" in pl/sql, you need to SELECT INTO a variable.
SELECT SYSDATE+3/72 FROM DUAL
Some places you can start:
the source of it all: https://docs.oracle.com/en/database/oracle/oracle-database/18/lnpls/plsql-language-fundamentals.html#GUID-640DB3AA-15AF-4825-BD6C-1D4EB5AB7715
Google "pl/sql basics" and read up on it.
write small blocks in pl/sql to try things out. Use the sample schema emp/dept to have sample data everyone knows. You can get a database schema on apex.oracle.com (well that is an apex workspace but in the sql workshop you can do all the pl/sql you want) or use livesql.oracle.com
Other than that, you wrote your first code and asked questions about it - so you're definitely on the right track :)

parameters FDquery delphi does not work

i have the following delphi code:
FDQuery1.SQL.Clear;
FDQuery1.SQL.Add('SELECT * FROM :Tablename');
FDQuery1.ParamByName('Tablename').AsString := 'tasks';
ShowMessage(FDQuery1.sql.Text);
FDQuery1.Open;
(coppied from this link: http://www.delphigroups.info/2/da/237634.html)
it does not work because the parameter is not filled but stays the same.
does somebody know why it is not filled?
Because you cannot use parameters for table name substitution in SQL commands in general. You are lucky enough here though, FireDAC supports preprocessor macros to parametrize table names in SQL commands. So you can write for example this (note that if you want to see the command as you did in your code, it must be after macro preprocessing, that is e.g. after calling Prepare):
FDQuery1.SQL.Text := 'SELECT * FROM &TableName';
FDQuery1.MacroByName('TableName').AsIdentifier := 'tasks';
FDQuery1.Open;
For details about this kind of macros, see the substitution variables topic.

Does Oracle 12 have problems with local collection types in SQL?

To make a long story short I propose to discuss the code you see below.
When running it:
Oracle 11 compiler raises
"PLS-00306: wrong number or types of arguments tips in call to 'PIPE_TABLE'"
"PLS-00642: Local Collection Types Not Allowed in SQL Statement"
Oracle 12 compiles the following package with no such warnings, but we have a surprise in runtime
when executing the anonymous block as is - everything is fine
(we may pipe some rows in the pipe_table function - it doesn't affect)
now let's uncomment the line with hello; or put there a call to any procedure, and run the changed anonumous block again
we get "ORA-22163: left hand and right hand side collections are not of same type"
And the question is:
Does Oracle 12 allow local collection types in SQL?
If yes then what's wrong with the code of PACKAGE buggy_report?
CREATE OR REPLACE PACKAGE buggy_report IS
SUBTYPE t_id IS NUMBER(10);
TYPE t_id_table IS TABLE OF t_id;
TYPE t_info_rec IS RECORD ( first NUMBER );
TYPE t_info_table IS TABLE OF t_info_rec;
TYPE t_info_cur IS REF CURSOR RETURN t_info_rec;
FUNCTION pipe_table(p t_id_table) RETURN t_info_table PIPELINED;
FUNCTION get_cursor RETURN t_info_cur;
END buggy_report;
/
CREATE OR REPLACE PACKAGE BODY buggy_report IS
FUNCTION pipe_table(p t_id_table) RETURN t_info_table PIPELINED IS
l_table t_id_table;
BEGIN
l_table := p;
END;
FUNCTION get_cursor RETURN t_info_cur IS
l_table t_id_table;
l_result t_info_cur;
BEGIN
OPEN l_result FOR SELECT * FROM TABLE (buggy_report.pipe_table(l_table));
RETURN l_result;
END;
END;
/
DECLARE
l_cur buggy_report.t_info_cur;
l_rec l_cur%ROWTYPE;
PROCEDURE hello IS BEGIN NULL; END;
BEGIN
l_cur := buggy_report.get_cursor();
-- hello;
LOOP
FETCH l_cur INTO l_rec;
EXIT WHEN l_cur%NOTFOUND;
END LOOP;
CLOSE l_cur;
dbms_output.put_line('success');
END;
/
In further experiments we found out that problems are even deeper than it's been assumed.
For example, varying elements used in the package buggy_report we can get an ORA-03113: end-of-file on communication channel
when running the script (in the question). It can be done with changing the type of t_id_table to VARRAY or TABLE .. INDEX BY ... There are a lot of ways and variations leading us to different exceptions, which are off topic to this post.
The one more interesting thing is that compilation time of buggy_report package specification can take up to 25 seconds,
when normally it takes about 0.05 seconds. I can definitely say that it depends on presence of TYPE t_id_table parameter in the pipe_table function declaration, and "long time compilation" happen in 40% of installation cases. So it seems that the problem with local collection types in SQL latently appear during the compilation.
So we see that Oracle 12.1.0.2 obviously have a bug in realization of using local collection types in SQL.
The minimal examples to get ORA-22163 and ORA-03113 are following. There we assume the same buggy_report package as in the question.
-- produces 'ORA-03113: end-of-file on communication channel'
DECLARE
l_cur buggy_report.t_info_cur;
FUNCTION get_it RETURN buggy_report.t_info_cur IS BEGIN RETURN buggy_report.get_cursor(); END;
BEGIN
l_cur := get_it();
dbms_output.put_line('');
END;
/
-- produces 'ORA-22163: left hand and right hand side collections are not of same type'
DECLARE
l_cur buggy_report.t_info_cur;
PROCEDURE hello IS BEGIN NULL; END;
BEGIN
l_cur := buggy_report.get_cursor;
-- comment `hello` and exception disappears
hello;
CLOSE l_cur;
END;
/
Yes, in Oracle 12c you are allowed to use local collection types in SQL.
Documentation Database New Features Guide says:
PL/SQL-Specific Data Types Allowed Across the PL/SQL-to-SQL Interface
The table operator can now be used in a PL/SQL program on a collection whose data type is declared in PL/SQL. This also allows the data type to be a PL/SQL associative array. (In prior releases, the collection's data type had to be declared at the schema level.)
However, I don't know why your code is not working, maybe this new feature has still a bug.
I fiddled around your example. The trick how Oracle 12c can use PL/SQL collections in SQL statements is that Oracle creates surrogate schema object types with compatible SQL type attributes and uses these surrogate types in a query. Your case looks like a bug. I traced the execution and the surrogate types are created only once if not exist. So the effective type doesn't change nor recompile (don't know if implicit recompilation are done using ALTER statement) during execution of pipelined function. And the issue only occurs if you use the p parameter in pipe_table function. If you don't call l_table := p; the code executes successfully even with enabled method call.

Oracle SQL procedure to check input length

I am trying to check the input length, to see if it's less than 7. It should show an error message, but the code below doesn't work. What's wrong with it?
CREATE OR REPLACE PROCEDURE prc_staffContact(IN_staffID IN CHAR, IN_staffContact IN VARCHAR) IS
v_staffName VARCHAR(50);
v_staffID CHAR(6);
v_staffContact VARCHAR(11);
BEGIN
SELECT s.staffName, s.staffID, s.staffContact
INTO v_staffName, v_staffID, v_staffContact
FROM staff s
WHERE staffID = IN_staffID;
IF (LENGTH(IN_staffContact) < 7 )
THEN
DBMS_OUTPUT.PUT_LINE('Error. Contact number at least 7 digits.');
ELSE
UPDATE staff
SET staffContact = IN_staffContact
WHERE staffID = IN_staffID;
DBMS_OUTPUT.PUT_LINE('=============================================================================');
DBMS_OUTPUT.PUT_LINE('The contact number of [ ' ||v_staffName || ' ] has been updated successfully.');
DBMS_OUTPUT.PUT_LINE('New contact number: [ ' ||v_staffContact || ' ].');
DBMS_OUTPUT.PUT_LINE('=============================================================================');
END IF;
END;
/
You said something that appears to be contradictory:
it shows [PL/SQL procedure successfully completed.] but doesn't checking even my input is less than 7 characters. and the data doesn't update also.
You described that you're doing as:
i save it under procedure1.sql and i start it in sql plus. that is my 1st call. after that i call the exec prc_staffContact('100001', '0000')
Together those suggest that when you say the data isn't updated, what you really mean is you don't get the contact number/new contact number message from the else branch, and I think you're assuming that means the update doesn't happen either, so it didn't execute either branch. But you must have gone into either the if or the else, by definition.
So if you didn't get either message, then you haven't done:
set serveroutput on
in SQL*Plus before calling exec. That setting is off by default, unless you have it turned on in your login.sql or glogin.sql, so you have to turn it on explicitly if you want to see dbms_output messages.
In this case, for the validation, (a) you probably want the select inside the elsef` too, partly because (b) if the passed values doesn't exist you'll get a no_data_found exception, and (c) you might want to consider throwing an exception if the length is less than 7 rather than (only) displaying a message. Someone else calling this might not have serverout on either, or might be using a different client that doesn't have that option.
You've also got v_staffID defined as char(6). Apart from wondering why that isn't a varchar2, the length you've given it means that if IN_staffID is 7 chars or more, the select into will get a 'character string buffer too small' error. I'd declare that as:
v_staffID staff.staffID%TYPE;
... to avoid issues like that, and the same for the other fields that relate to table columns.
And your 'success' message is showing the old contact number, not the new one. Not sure you need v_staffContact at all.
Look at your code carefully. May be your variable types not compatible or stored procedure parameters not compatible with other variables (or table columns, column's types). I tested your code in my database, all success. but may be error not data found in your select statement, or may be buffer too small error. Hope this help you. thanks

Selecting large sequence value into global variable not working in PL/SQL code

I have a script where I would like to have a global variable to store a transaction number for later use. The code is working fine on one schema where the sequence value that I'm fetching is relatively low. It is not working on another schema with a higher sequence value where I get a "Numeric Overflow". If I change that sequence value to a lower number it is working as well but that is not an option.
VAR TRANSACTIONNR NUMBER;
BEGIN
--Works with NEXTVAL being around 946713241
--Doesn't work with NEXTVAL being around 2961725541
SELECT MY_SEQUENCE.NEXTVAL INTO :TRANSACTIONNR FROM DUAL;
MY_PACKAGE.STARTTRANSACTION(:TRANSACTIONNR);
END;
/
-- SQL Statements
BEGIN
MY_PACKAGE.ENDTRANSACTION;
MY_PACKAGE.DO_SOMETHING(:TRANSACTIONNR);
END;
/
What is also working is selecting the sequence into a variable declared in the DECLARE block:
DECLARE
TRANSACTIONNR NUMBER;
BEGIN
SELECT MY_SEQUENCE.NEXTVAL INTO TRANSACTIONNR FROM DUAL;
MY_PACKAGE.STARTTRANSACTION(TRANSACTIONNR);
END;
/
But that means I won't be able to reuse it in the block at the end. Setting the size of the number is not possible.
VAR TRANSACTIONNR NUMBER(15)
is not valid.
Any ideas what I could try or other ways to store global state?
On further investigation this looks like it might be a SQL Developer bug (making assumptions about what you're doing again, of course...). I can get the same error with:
VAR TRANSACTIONNR NUMBER;
BEGIN
SELECT 2961725541 INTO :TRANSACTIONNR FROM DUAL;
END;
/
It appears that SQL Developer's NUMBER is limited to 2^31, which isn't the case normally for Oracle.
A possibly workaround is to use BINARY_FLOAT to store the value, but you'll run into precision problems eventually (not sure where, but looks OK up to 2^53-ish), and you'll need to cast() it back to NUMBER when using it.
VAR TRANSACTIONNR BINARY_DOUBLE;
BEGIN
SELECT 2961725541 INTO :TRANSACTIONNR FROM DUAL;
-- dbms_output.put_line(cast(:TRANSACTIONNR as NUMBER)); -- null for some reason
END;
/
...
BEGIN
dbms_output.put_line(cast(:TRANSACTIONNR as NUMBER));
END;
/
For some reason I don't seem to be able to refer to the bind variable again in the anonymous block I set it in - it's null in the commented-out code - which seems to be another SQL Developer quirk, regardless of the var type; but as you were doing so in your code, I may again have assumed too much...
Original answer for posterity, as it may still be relevant in other circumstances...
Presumably you're doing something to end the current transaction, e.g. a commit in endtransaction; otherwise you could just refer to my_sequence.currval in the do_something call. A number variable is fine for this size of number though, it won't have a problem with a sequence that size, and it won't make any difference that it is from a sequence rather than manually assigned. I don't think the problem is with the storage or the sequence.
It seems rather more likely that the error is coming from one of the package procedures you're calling, though I can't quite imagine what you might be doing with it; something like this will cause the same error though:
create sequence my_sequence start with 2961725541;
create package my_package as
procedure starttransaction(v_num number);
procedure endtransaction;
procedure do_something(v_num number);
end my_package;
/
create package body my_package as
procedure starttransaction(v_num number) is
begin
dbms_output.put_line('starttransaction(): ' || v_num);
for i in 1..v_num loop
null;
end loop;
end starttransaction;
procedure endtransaction is
begin
dbms_output.put_line('endtransaction()');
end endtransaction;
procedure do_something(v_num number) is
begin
dbms_output.put_line('do_something(): ' || v_num);
end do_something;
end my_package;
/
When your code is run against that it throws your error:
BEGIN
*
ERROR at line 1:
ORA-01426: numeric overflow
ORA-06512: at "STACKOVERFLOW.MY_PACKAGE", line 6
ORA-06512: at line 5
endtransaction()
do_something():
Note the error is reported against line 6 in the package, which is the for ... loop line, not from the assignment in your anonymous block.
Looping like that would be an odd thing to do of course, but there are probably other ways to generate that error. The breakpoint for it working is if the nextval is above 2^31. If I start the sequence with 2147483647 it works, with 2147483648 it errors.
I'm assuming you are actually getting an ORA-01426 from the original question; if it's actually a ORA-1438 or ORA-06502 then it's easier to reproduce, by trying to assign the value to a number(9) column or variable. 'Numeric overflow' is pretty specific though.