PLSQL construction a new function - sql

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 :)

Related

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.

Output user input in PL/SQL using SQL developer

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.

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.

Call a stored procedure, into another stored procedure

I have a procedure, that insert a line into one of my table.
After the INSERT in the procedure, I want to find all the lines into another table, and then, call the insert procedure of the second table.
So I have all the first procedure that works fine
P_INSERT_TABLE1
INSERT INTO TABLE1
...
COMMIT;
FOR record_po IN (SELECT C3, ...
FROM T_TABLE2
WHERE id = v_id)
LOOP
P_INSERT_TABLE2(record_po.C3, ...);
END LOOP;
All "in parameters" for P_INSERT_TABLE2 are VARCHAR2, so I make a "to_char" for each column are not varchar2 :
P_INSERT_TABLE2(pi_id,
record_po.C3,
record_po.C4,
record_po.C5,
record_po.C6,
record_po.C7,
to_char(record_po.C8, 'DD/MM/YYYY');
Here, pi_id, is one of the in parameters of P_INSERT_TABLE1, in VARCHAR2.
So now, I have this error message :
Erreur(357,1): PLS-00306: number or args types wrong in the call of P_INSERT_TABLE2
I don't understand, why P_INSERT_TABLE2 don't accept parameters, while there are all the good types in the good order?
If I call the procedure like "call P_INSERT_TABLE2(...)" I have an error like :
Erreur(357,9): PLS-00103: Symbol "P_INSERT_TABLE2" instead one of this symbols : := . ( # % ; immediate Symbole ":="
create or replace
PROCEDURE P_INSERT_TABLE2 (
pi_id IN VARCHAR2
,pi_C3 IN VARCHAR2
,pi_C4 IN VARCHAR2
,pi_C5 IN VARCHAR2
,pi_C6 IN VARCHAR2
,pi_C7 IN VARCHAR2
,pi_C8 IN VARCHAR2
,pmessage OUT NOCOPY VARCHAR2
)
Thanks for helping.
The declaration of P_INSERT_TABLE2 is invalid. You can't have 5 input parameters all named pi_C4. Since you're not getting a compilation error creating that procedure, I'll guess that this was a bug that was introduced posting the question here rather than something that is actually in the code.
According to the declaration of P_INSERT_TABLE2, the procedure takes 7 input parameters and one output parameter. In the code you posted, you appear to be passing in 7 input parameters but you are not passing in a variable for the output parameter. It appears that you need something like
P_INSERT_TABLE2(pi_id,
record_po.C3,
record_po.C4,
record_po.C5,
record_po.C6,
record_po.C7,
to_char(record_po.C8, 'DD/MM/YYYY'),
<<some local variable for the output parameter>> );
Beyond the syntax errors, I am extremely dubious when I see someone taking a perfectly good DATE, casting it to a string, and then passing that to a procedure. That implies either that P_INSERT_TABLE2 is going to turn around and convert the string back to a date, which means that you're doing extra work and have introduced additional points where the conversions can fail, or that you are going to write the string representation of a date to a table. Neither of these implications are good.
I am also highly dubious of any procedure that has an OUT parameter named pMessage. That tends to imply that you're not using exceptions properly and that you're passing an error message back rather than throwing an exception if your code encounters an error. That virtually always leads to much more brittle code that is much more difficult to debug than when you use proper exceptions.

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.