create or replace procedure "FINDMAX"
(maxroll OUT NUMBER)
is
begin
select max(rollno) into maxroll from std;
end;
in this code maxroll i took to make procedure and rollno i took it as a database field. This gives me maximum(LAST) roll number from table.
Can anyone suggest me how I can get next roll number(max+1)?
For your immediate problem, you can use max(col) + 1
create or replace procedure "FINDMAX"
(maxroll OUT NUMBER)
is
begin
select 1 + max(rollno) into maxroll from std;
end;
But if you are getting this to use while inserting the next record, it's not the right way.
You should use sequences instead.
create sequence roll_no_seq start with 1 increment by 1;
Then use it while inserting using roll_no_seq.nextval
Use a SEQUENCE.
CREATE SEQUENCE rollno_seq
START WITH 1
INCREMENT BY 1
NOCACHE
NOCYCLE;
Then you can use,
select rollno_seq.nextval from dual;
See also, Basic Elements of Oracle SQL: CURRVAL and NEXTVAL
Related
I really apologise if answer already was given, but I could only find answers for php.
My problem is that I got nested table array "test_nested_table" that got values ('a','b','c'). I also got table "test_table" in the DB that got three columns col1, col2, col3.
All I want to do is something like
insert into test_table values (test_nested_table);
I understand I can do that:
insert into test_table values (test_nested_table(1), test_nested_table(2), test_nested_table(3));
However, my actual real life table might be very big and I would be very surprised if I really need to type all 100 elements to insert.
I have come up with the following solution:
declare
type test_array is varray(3) of varchar2(10);
ta test_array := test_array ('a','b','c');
sql_txt varchar2(1000) := 'insert into test_table_1 values (''';
begin
for n in 1 .. ta.count loop
sql_txt := sql_txt || ta(n)||''',''';
end loop;
sql_txt := rtrim(sql_txt,',''')||''')';
execute immediate sql_txt;
end;
/
So, if you have an array or any other collection type, you can use dynamic approach to insert a lot of values without writing them all in the insert statement; however, I believe there still should be a more usual way to do that.
I have an oracle 19c ee database build via their docker image on Oracles github (https://github.com/oracle/docker-images/tree/master/OracleDatabase/SingleInstance). I am trying to follow their example as to how to create a function from here.
I have copied their example exactly. Setup table and data:
CREATE TABLE orders (
customer_id number(10),
order_total NUMBER(11,2)
);
INSERT INTO orders (customer_id, order_total) VALUES (1, 200.01)
The actual function:
CREATE FUNCTION get_bal(acc_no IN NUMBER)
RETURN NUMBER
IS acc_bal NUMBER(11,2);
BEGIN
SELECT order_total
INTO acc_bal
FROM orders
WHERE customer_id = acc_no;
RETURN(acc_bal);
END;
However I keep running into this error when I try and create the function
Query 2 ERROR: ORA-06550: line 5, column 27:
PL/SQL: ORA-00904: "ACC_NO": invalid identifier
ORA-06550: line 2, column 7:
PL/SQL: SQL Statement ignored
ORA-06550: line 6, column 7:
PLS-00372: In a procedure, RETURN statement cannot contain an expression
ORA-06550: line 6, column 7:
PL/SQL: Statement ignored
What am I doing wrong?
The example works for me. You must have mistyped something. Are you sure your function is exactly the same as the one in the manual?
ORA-00904: "ACC_NO": invalid identifier
suggests the declaration acc_bal NUMBER(11,2); is missing or different.
PLS-00372: In a procedure, RETURN statement cannot contain an expression
indicates that your code is a procedure, not a function.
SQL> CREATE TABLE orders (
2 customer_id number(10),
3 order_total NUMBER(11,2)
4 );
Table created
SQL> INSERT INTO orders (customer_id, order_total) VALUES (1, 200.01);
1 row inserted
SQL> CREATE FUNCTION get_bal(acc_no IN NUMBER)
2 RETURN NUMBER
3 IS acc_bal NUMBER(11,2);
4 BEGIN
5 SELECT order_total
6 INTO acc_bal
7 FROM orders
8 WHERE customer_id = acc_no;
9 RETURN(acc_bal);
10 END;
11 /
Function created
SQL> select get_bal(1) from dual;
GET_BAL(1)
----------
200.01
As an aside, while I'm a big fan of the Oracle documentation in general, and this example does neatly illustrate how to create a PL/SQL function, I think it could be improved:
For readability, it's better to give each declaration its own line, so line 3 would be better split into two with acc_bal NUMBER(11,2); on its own line.
The IS and AS keywords are interchangeable here, but surely create ... as (similar to what you might use to create a table or a view) reads better than create ... is.
Understandably, the author didn't want to complicate the example by introducing %type before it had been explained, but a more advanced version would use acc_bal orders.order_total%type; to make acc_bal inherit its datatype from the table column rather than hard-coding it. This goes for all three values used in the function.
The parameter and variable names are OK - they are at least clear - but there is a danger when using the same naming pattern for parameters and variables as for table columns. One day you will type WHERE c.customer_id = customer_id and wonder why you're getting more rows back than you expected. Again it's understandable that the author didn't want to get into that whole discussion in the first example, but it's something to think about. You might use get_bal.acc_no within the function, or use camelCase for parameters and variables, or prefix them with p_ for 'parameter' etc.
A basic rule of layout is that opening and closing keywords such as if/else and begin/end should be left-aligned. The END at line 10 is misaligned under its opening BEGIN. I suppose indenting the entire thing after the first line is a valid personal layout choice, but to me it doesn't add anything.
It's a good idea to leave blank lines around each SQL statement, to avoid a solid wall of text. Personally, I'd prefer a blank line before the RETURN at line 9.
A RETURN clause doesn't require any brackets. The compiler is ignoring the redundant brackets at line 9. I'd lose them.
It's good practice (though optional) to include the procedure/function name in the closing END, so line 10 would become END get_bal;
The COBOL-style uppercase habit is widespread in the industry, but there is no need for it. (PL/SQL's syntax is famously based on Ada, though some also point to ALGOL and PL/1 - nobody ever wrote those in uppercase.) I would improve readability by lowercasing the whole thing.
With these changes, I get this:
create or replace function get_bal
( inAccNo in orders.customer_id%type )
return orders.order_total%type
as
accBal orders.order_total%type;
begin
select order_total into accBal
from orders
where customer_id = inAccNo;
return accBal;
end;
I would like to display Unique numbers dynamically. I have tried below code for the same but same number is displaying all the times.
DECLARE
a NUMBER;
BEGIN
FOR i IN 1 .. 3 LOOP
DBMS_OUTPUT.PUT_LINE(&a);
END LOOP;
END;
the above code will ask me for "a" value three times, if i pass 1,2,3 as parameters then it should display 1,2,3 but this code is displaying first(1) value three time as 1,1,1.
Could you please help me to get the required output like 1,2,3
You can't really create an interactive program in just PL/SQL. When you put &a in the PL/SQL and run it in a tool like SQL Developer, it prompts you once for a value for a before it runs the code, using the value you typed instead of the substitution variable a.
You want to print i and not a. Also the ampersand in front of the a means you will be prompted to enter a value for a.
I'm new to PL/SQL, doing a homework question where I need to write a procedure that returns an error message based on day of week/time but I am stuck on a basic thing before I get to that point:
I have written the following:
CREATE OR REPLACE PROCEDURE
SECURE_IT
(p_weekday NUMBER,
p_currtime TIME)
IS
BEGIN
select to_char(current_timestamp,'D')
, to_char(current_timestamp,'HH24:MI')
into p_weekday, p_currtime
from dual;
DBMS_OUTPUT.PUT_LINE(p_weekday,p_currtime);
END;
I think all of my ;'s are all in place, can't see any difference between this, code in the book, and code I've found online yet still it returns this error:
PLS-00103: Encountered the symbol "end-of-file" when expecting one of the
following: ; <an identifier> <a double-quoted delimited-identifier>
current delete exists prior <a single-quoted SQL string> The symbol ";"
was substituted for "end-of-file" to continue.
I tried changing END; to END SECURE_IT; in hopes that it would fix something (all I could think of) but can't see what's wrong. Could someone help me with this?
Thank you in advance :-)
There is a couple of mistakes in your procedure
TIME datatype (In PL/SQL there is no such datatype see Documentation, Alternatively, you can pass datetime data types: DATE, TIMESTAMP, TIMESTAMP WITH TIME ZONE, and TIMESTAMP WITH LOCAL TIME ZONE)
You can extract time part using many options. For example:
SELECT TO_CHAR(p_currDateTime, 'HH24:MI:SS') into p_currtime FROM DUAL;
You are trying to update p_weekday & p_currtime which are IN parameters. These parameters are used to pass value to the procedure, and not to return ones.
DBMS_OUTPUT.PUT_LINE takes only one parameter, so instead of , you can concatenate the two values like this: DBMS_OUTPUT.PUT_LINE(p_weekday||'-'||p_currtime);
It would help a great deal if it is not clear what are you trying to achieve by this procedure
Basically what i can see here is you need to print some value out od procedure as a part of your homework. But using dbms_output will not do any good. You need to make these as OUT param to use these values any where.
CREATE OR REPLACE PROCEDURE SECURE_IT(
p_weekday OUT VARCHAR2,
p_currtime OUT VARCHAR2)
IS
BEGIN
p_weekday := TO_CHAR(CURRENT_TIMESTAMP,'D');
p_currtime:= TO_CHAR(CURRENT_TIMESTAMP,'HH24:MI');
DBMS_OUTPUT.PUT_LINE(p_weekday||' '||p_currtime);
END;
-----------------------------EXECUTE--------------------------------------------
var week VARChaR2(100);
var curtime VARChaR2(100);
EXEC SECURE_IT(:week,:curtime);
print week;
print curtime;
-------------------------------OUTPUT--------------------------------------------
WEEK
-
6
CURTIME
-----
06:12
-------------------------------OUTPUT----------------------------------------------
There is a table X having a "Long" Column and Table Y having a "CLOB" Column. Data has been migrated from Table X to Table Y. Now I need to verify if the data got converted correctly. I had the idea of using casting, but seems "Long" values cannot be converted to "Varchar" in select statements. Any ideas are highly appreciated.
Eg:
SELECT LONG_COLUMN FROM TABLE_X
Minus
SELECT CLOB_COLUMN FROM TABLE_Y
You have two problems: long can't easily be converted/compared to other datatypes and clob can't be used in a minus operation!
Fortunately, both of these can be overcome by using PL/SQL. Longs and clobs can be implicitly converted into varchar2 when they are selected in PL/SQL blocks. You can load these into a nested-table, then use the multiset except operator to find the differences between them:
create table long_t ( x long );
create table lob_t ( x clob );
insert into long_t values ('1');
insert into long_t values ('2');
insert into lob_t values ('1');
declare
type t is table of varchar2(32767);
longs t;
clobs t;
diff t;
begin
select x bulk collect into longs from long_t;
select x bulk collect into clobs from lob_t;
diff := longs multiset except clobs;
for i in 1 .. diff.count loop
dbms_output.put_line(diff(i));
end loop;
diff := clobs multiset except longs;
for i in 1 .. diff.count loop
dbms_output.put_line(diff(i));
end loop;
end;
/
anonymous block completed
2
If your tables contain more than a couple of thousand rows, you're likely to run out of memory using the above as-is, as the whole table will be loaded at once. If you've got an id or similar column on each table, then it would be best to fetch and compare rows in ranges, e.g. 1-1000, 1001-2000 and so on.